from pyplusplus import module_builder
from pyplusplus.module_builder.call_policies import *
from pygccxml import declarations
from pyplusplus import function_transformers as FT
import fileptr_ostream_transformer

def wrap_std():
    mb = module_builder.module_builder_t(files = ['wrap_std.hpp']
           , gccxml_path='C:/Program Files/gccxml_sherm/bin/gccxml.exe'
           , include_paths=["C:/Program Files/boost/boost_1_42"]
           , define_symbols=["_HAS_TR1=0"])
           
    # wrap_string(mb)
    wrap_ostream(mb)
    wrap_foo(mb)
    
    mb.build_code_creator(module_name='_std')
    mb.split_module('generated_code')
    

def wrap_foo(mb):
    foo = mb.class_('foo')
    foo.member_function('say_hello').add_transformation(
        FT.input_ostream("os"))
    foo.add_declaration_code("""
        void foo_say_hello_wrapper1(const foo& f, const fileptr_ostream_t& output)
        {
            fileptr_ostream_t& os = const_cast<fileptr_ostream_t&>(output);
            f.say_hello(os);
        }
        void foo_say_hello_wrapper2(const foo& f, const std::ostream& output)
        {
            std::ostream& os = const_cast<std::ostream&>(output);
            f.say_hello(os);
        }
    """)


def wrap_string(mb):
    std = mb.namespace('std')
    string_typedef = std.typedef('string')
    string = string_typedef.type.declaration
    string.include()
    # Many methods return a reference to self
    string_t = declarations.declarated_t(string)
    string_ref_t = declarations.reference_t(declarations.declarated_t(string))
    for fn in string.member_functions(lambda f: f.return_type == string_ref_t):
        fn.call_policies = return_self()
    # Underscore methods should not be exposed
    for fn in string.member_functions(lambda f: f.name.startswith('_')):
        fn.exclude()
    for var in string.variables(lambda v: v.name.startswith('_')):
        var.exclude()
    # for union in string.unions(lambda u: u.name.startswith('_')):
    #     union.exclude()
    # One "at" method returns a non-const char&, and should be removed
    for fn in string.member_functions("at"):
        t = fn.return_type.base
        if not declarations.is_const(t):
            fn.exclude()
    # Compile troubles for default arguments with "_Elem" and others
    # hack...
    string.add_declaration_code("#define _Elem char")
    string.add_declaration_code("#define _Traits std::char_traits<char>")
    string.add_declaration_code("#define _Ax std::allocator<char>")
    # string.copy method generates reasonable sounding compiler warning
    string.member_function('copy').exclude()
    # Use built-in boost.python mechanism for wrapping __str__
    string.add_registration_code('def( bp::self_ns::str(bp::self) )')
    # Rather, use custom auto conversion (doesn't that already exist?)
    # mb.add_declaration_code('#include "convert_string.hpp"')
    # mb.add_registration_code('register_std_string_conversion();')


def wrap_ostream(mb):
    std = mb.namespace('std')
    ostream_typedef = std.typedef('ostream')
    ostream = ostream_typedef.type.declaration
    ostream.include()
    ostream.member_function('flush').call_policies = return_self()
    ostream.member_function('put').call_policies = return_self()
    ostream.member_functions('seekp').call_policies = return_self()
    ostream.member_function('write').call_policies = return_self()
    std.variable('cout').include()
    std.variable('cerr').include()
    ostream_t = declarations.declarated_t(ostream)
    ostream_ref_t = declarations.reference_t(ostream_t)
    for endl in std.free_functions('endl'):
        if endl.return_type == ostream_ref_t:
            endl.include()
            endl.call_policies = return_internal_reference(1)
    # operator<<
    # We cannot use "def( bp::self << long() )" because self is an lvalue
    for typename in ['long', 'double', 'bool']:
        ostream.add_registration_code("""//
            {
                std::ostream& (std::ostream::*lshift_op)( %s ) = &std::ostream::operator<<;
                ostream_exposer.def("__lshift__", lshift_op, bp::return_self<>() );
            }
        """ % (typename), works_on_instance=False )
    # trouble with os << string
    ostream.add_declaration_code("""//
        std::ostream& lshift_ostream_string_wrapper(std::ostream& os, const std::string& str) {
            return os << str;
        }
        std::ostream& lshift_ostream_ostream_wrapper(std::ostream& os, std::ostream& os2) {
            return os << os2;
        }
    """)
    ostream.add_registration_code('def("__lshift__", &lshift_ostream_string_wrapper, bp::return_self<>())')
    ostream.add_registration_code('def("__lshift__", &lshift_ostream_ostream_wrapper, bp::return_self<>())')
    # convert from python file to fileptr_ostream_t
    mb.add_declaration_code('#include "convert_fileptr_ostream.hpp"')
    mb.add_registration_code('register_fileptr_ostream_conversion();')


if __name__ == "__main__":
    wrap_std()

