
import os
import sys
import re
from pyplusplus import module_builder
from pyplusplus.module_builder.call_policies import *
from pyplusplus import function_transformers as FT


def generate_test_source():
    mb = module_builder.module_builder_t(files = ['test.h']
           , gccxml_path='C:/Program Files/gccxml_sherm/bin/gccxml.exe'
           , include_paths=['.']
           , define_symbols=["_HAS_TR1=0"]
         )

    wrap_foo(mb)
    wrap_std_ostream(mb)
    wrap_streambuf(mb)
    wrap_fileobj(mb)

    mb.build_code_creator(module_name='_test')
    mb.split_module('generated_code')
    open(os.path.join('generated_code', 'generate_test.stamp'), "w").close()

def wrap_foo(mb):
    foo = mb.class_('Foo')
    foo.held_type = 'std::auto_ptr< %s >' % foo.alias
    foo.include_files.append('python_streambuf.h')
    foo.include_files.append('mds_utils/python/fileobj.hpp')
    foo.add_declaration_code("""
        using boost_adaptbx::python::streambuf;
        void foo_say_hello_streambuf_wrapper1(Foo& foo, streambuf& output) {
            streambuf::ostream os(output);
            foo.say_hello_streambuf(os);
        }
        void foo_say_hello_streambuf_wrapper2(Foo& foo, bp::object& pyfile) {
            streambuf output(pyfile);
            foo_say_hello_streambuf_wrapper1(foo, output);
        }
        using mds_utils::python::oFileObj;
        void foo_say_hello_fileobj_wrapper1(Foo& foo, oFileObj& output) {
            std::ostream& os = static_cast<std::ostream&>(output);
            foo.say_hello_fileobj(os);
        }
        void foo_say_hello_fileobj_wrapper2(Foo& foo, bp::object& pyfile) {
            oFileObj output(pyfile);
            foo_say_hello_fileobj_wrapper1(foo, output);
        }
        // # http://wiki.python.org/moin/boost.python/HowTo#namedconstructors.2BAC8factories.28asPythoninitializers.29
        static Foo* makeFoo1(streambuf& output) {
            streambuf::ostream os(output);
            return new Foo(os);
        }
        static Foo* makeFoo2(oFileObj& output) {
            std::ostream& os = static_cast<std::ostream&>(output);
            return new Foo(os);
        }
        static Foo* makeFoo3(bp::object& pyfile) {
            // std::cout << "bp::object constructor" << std::endl;
            // 
            // oFileObj output(pyfile);
            // return makeFoo2(output);
            // or
            streambuf output(pyfile);
            return makeFoo1(output);
        }
        """)
    # Nuclear option wrapper that takes bp::object argument must be registered FIRST, in
    # order to be considered LAST in testing arguments
    foo.add_registration_code('def("__init__", bp::make_constructor(makeFoo3))', tail=False)
    foo.add_registration_code('def("__init__", bp::make_constructor(makeFoo1))')
    foo.add_registration_code('def("__init__", bp::make_constructor(makeFoo2))')
    foo.add_registration_code('def("say_hello_streambuf", &foo_say_hello_streambuf_wrapper2)', tail=False)
    foo.add_registration_code('def("say_hello_streambuf", &foo_say_hello_streambuf_wrapper1)', tail=True)
    foo.add_registration_code('def("say_hello_fileobj", &foo_say_hello_fileobj_wrapper2)', tail=False)
    foo.add_registration_code('def("say_hello_fileobj", &foo_say_hello_fileobj_wrapper1)', tail=True)
    # foo.add_registration_code("""def("say_hello_fileobj", 
    #             as<void(conv(bp::object&))>( &foo_say_hello_fileobj_wrapper1 ) )""",
    #         tail=True)

def wrap_streambuf(mb):
    mb.add_declaration_code('#include "register_adaptbx_ostream.hpp"')
    mb.add_registration_code('register_adaptbx_ostream_class();', tail=True)

def wrap_fileobj(mb):
    mb.add_declaration_code('#include "register_fileobj.hpp"')
    mb.add_registration_code('register_fileobj_class();', tail=True)

def wrap_std_ostream(mb):
    ostream = mb.namespace('std').typedef('ostream').type.declaration
    ostream.alias = 'std_ostream'
    ostream.include()
    ostream.member_functions().exclude()
    ostream.member_operators().exclude()
    ostream.constructors().exclude()
    ostream.no_init = True
    cout = mb.namespace('std').variable('cout')
    cout.include()

if __name__ == "__main__":
    generate_test_source()

