def process_overload_by_arg_count(orig_method, context):
    logging.debug('processing a overload by arg count: %s' + orig_method.name)

    acount = orig_method.overloads['by_arg_count']    
    switch = cpp.cpp_switch('lua_gettop(L)')

    for by_arg_count in acount:
        case = cpp.cpp_case()
        case.expr = len(by_arg_count.parameters)

        params = ['L']
        if by_arg_count.is_virtual:
            params.append('lua_self')

        case.body.append(cpp.cpp_return(cpp.cpp_method_call('lua_munch_' + by_arg_count.name, params=params)))

        switch.exprs.append(case)

        #we bake the overload method and add it back to our context
        other_method = lua_context_builder.bake([by_arg_count], preprocess = False).translated[0]
        other_method.public = False
        context.translated.append(other_method)

        for item in other_method.execution:
            if type(item) == cpp.cpp_method_call:
                item.expr = orig_method.name if not orig_method.parent else 'lua_self->' + orig_method.name
                break

    orig_method.translation.execution.append(switch)
def init_translated_method(orig_method, context):
    assert 'translation' not in orig_method.__dict__

    logging.debug('entered [init_translated_method] with method:' + repr(orig_method))

    method = cpp.cpp_method('lua_munch_' + orig_method.name,
            static=True, returns= cpp.cpp_type('int'),
            params=[cpp.cpp_variable('L', cpp.cpp_type('lua_State', pointer=True))])
        
    if orig_method.parent:
        method.parent = orig_method.parent.translation

    method.orig_name = orig_method.name

    method.public = True

    #now we embed the default object with a very specific format that will be useful later
    method.initialization = [] # will contain initialization steps
    method.validation = [] # will contain validation steps
    method.recover = [] # will contain the steps to translate the lua arg to a C++ arg
    method.execution = [] # will contain execution steps, i.e, calling the native method
    method.lua_return = [] # will contain the steps to push a value back to lua

    if not 'is_overload' in method.__dict__:
        method.is_overload = False

    if orig_method.parent and orig_method.is_virtual:
        method.parameters.append(cpp.cpp_variable('lua_self', cpp.cpp_type(orig_method.parent.qualname, pointer=True)))

    method.return_value = cpp.cpp_return(0)

    #we 'tie' the translated method on the original method
    #so it's easier to work with them without needing to search for them on every callback
    orig_method.translation = method
def process_virtual_function_callback(orig_method, context):
    lua_method = copy(orig_method.translation)    
    lua_method.parameters = orig_method.translation.parameters[:-1]
    
    lua_method.exprs = []
    lua_method.return_value = cpp.cpp_return(cpp.cpp_method_call(lua_method.name, params=['L', 'nullptr']))
    
    orig_method.translation.parent.public.append(lua_method)
def process_function(function, context):    
    method = function.translation

    #will hold the real variables the method uses, after initialization
    method.transformed_parameters = []

    #adding an index value to each positional parameter
    #firstly we add the default initializers for each mapped type
    for i, parameter in enumerate(function.parameters):
        overriden_variable = lua_context_builder.apply_variable_initialization(parameter, context)
        overriden_variable.index = parameter.index = i if function.is_constructor else i + 2              
        
        method.transformed_parameters.append(overriden_variable)
        method.initialization.append(overriden_variable)
    
    #this additional step will ensure that the amount of parameters is correct
    method.validation.append('assert(lua_gettop(L) == %d)' % (len(function.parameters) + (0 if function.is_constructor else 1)))

    #then we add type checking
    for parameter in method.transformed_parameters:
        method.validation.append(lua_context_builder.apply_variable_check(parameter, context))

    #then we get the variables from LUA
    if function.parent and not function.is_constructor:
        if function.is_virtual:
            cif = cpp.cpp_if(['lua_self != nullptr'])
            cif.body.append('lua_self = static_cast<{}*>(lua_touserdata(L, 1))'.format(function.parent.qualname))

            method.recover.append(cif)
        else:
            method.initialization.append('{0}* lua_self = static_cast<{1}*>(lua_touserdata(L, 1))'.format(function.parent.qualname, function.parent.qualname))

    for parameter in method.transformed_parameters:
        method.recover.append(lua_context_builder.apply_variable_conversion_from_target(parameter, context))

    applied_parameters = []
    #then we add type casting to the original variable type
    for tp, fp in zip(method.transformed_parameters, function.parameters):
        applied_parameters.append(tp.cast(cpp.cpp_static_cast, fp.ctype))

    if function.is_constructor:
        method.execution.append('{0}* lua_self = new {0}({1})'.format(function.parent.qualname,
                                                                      ','.join(map(str,applied_parameters))))
        method.execution.append('lua_pushlightuserdata(L, (void*) lua_self)')
        method.return_value = cpp.cpp_return(1)
    else:    
        #then we call the original method!
        fname = function.name if not function.is_overload else function.orig_name

        method.execution.append(cpp.cpp_method_call(fname if not function.parent else 'lua_self->' + fname, applied_parameters))

        if function.returns != cpp.cpp_type('void'):
            logging.debug('lua_return: translating variable %r to LUA', function.returns)
            #if the function is not void, we recover the method execution
            method_execution = method.execution[0]
            del method.execution[:]

            return_var = cpp.cpp_variable('lua_return', function.returns)

            #create a variable assignment
            method.execution.append(return_var.declare(return_var.assign(method_execution)))

            #push the value to lua
            method.lua_return.append(lua_context_builder.apply_variable_conversion_to_target(return_var, context))
            print 'lua return is', method.lua_return
            #and then change the call function to 1 so LUA knows it has one value to unpack
            method.return_value = cpp.cpp_return(1)

    method.exprs += method.initialization + method.validation + method.recover + method.execution + method.lua_return

    logging.debug("finished translating: %r", method)
def init_translated_class(orig_class, context):
    assert 'translation' not in orig_class.__dict__

    lua_cls = cpp.cpp_class('lua_munch_' + orig_class.identifier_name)
    orig_class.translation = lua_cls

    luaReg = cpp.cpp_variable_array('lua_reg_' + orig_class.identifier_name, 
                                cpp.cpp_type('constexpr luaL_Reg', static=True))

    logging.debug('baking methods for class: %s : %s' % (orig_class.name ,orig_class.public))
    
    #lua_cls.public.append(make_getter(orig_class))
    
    #we now bake all methods and subclasses from this class, but not preprocess them
    class_ctx = lua_context_builder.bake(orig_class.public, preprocess=False)
    
    for item in class_ctx.translated:
        if item.public:
            lua_cls.public.append(item)
        else:
            lua_cls.protected.append(item)

        if type(item) == cpp.cpp_method and item.public:
            luaReg.expr.append('{ "%s" , lua_munch_%s::%s }' % (item.orig_name, orig_class.identifier_name, item.name))    

    lua_cls.public.append(make_binder(orig_class, lua_cls))

    #the base methods are shims that only retrieve it's self and pass it down to the
    #base class
    for base in orig_class.bases:
        for item in base.public:
            if type(item) != cpp.cpp_method:
                continue

            method = cpp.cpp_method('lua_munch_' + item.name,
                static=True, returns=cpp.cpp_type('int'),
                params=[cpp.cpp_variable('L', cpp.cpp_type('lua_State', pointer=True))])

            if item.parent:
                item.parameters.append(cpp.cpp_variable('lua_self', cpp.cpp_type(item.parent.name, pointer=True)))
                method.exprs.append('%s* lua_self = lua_munch_%s::get(L, %d)' % (orig_class.qualname, orig_class.identifier_name, 1))

            method.exprs.append(cpp.cpp_return(cpp.cpp_method_call('lua_munch_%s::lua_munch_%s' % (item.parent.name, item.name), params=['L', 'lua_self'])))

            lua_cls.public.append(method)

            if not item.is_constructor:
                luaReg.expr.append('{ "%s" , lua_munch_%s::lua_munch_%s }' % (item.name, orig_class.name, item.name))             

    luaReg.expr.append('{ 0, 0 }')

    lua_cls.public.append(luaReg)

    context.binder_method.exprs.append('{}::bind(L)'.format(lua_cls.name))
    
    luaReg.parent = lua_cls

    vardecl = luaReg.define()
    vardecl = vardecl.replace('static', '')

    context.luareg_declarations.append(vardecl)

    orig_class.translation = lua_cls
def lua_preprocess(data, context):
    classes = filter(lambda item: type(item) == cpp.cpp_class, data)
    functions = filter(lambda item: type(item) == cpp.cpp_method and item.parent == None, data)
    other = filter(lambda item: type(item) != cpp.cpp_method and type(item) != cpp.cpp_class, data)

    del data[:]

    #resolving overloads
    for item in classes:
        item.identifier_name = item.qualname\
                                        .replace('::', '_')\
                                        .replace(' ', '')\
                                        .replace('<','_')\
                                        .replace('>', '_')\
                                        .replace(',', '_')
        process_functions(item)

    #resolving dependencies
    sorted_classes = []
    class_set = set([])

    previous_count = len(classes)
    iteration = 0

    while classes:
        current = classes.pop(0)
        processed = True
        
        #print 'testing', current.name, current.dependencies
        for dependency in current.dependencies:
            if 'std' in dependency or dependency == current.qualname:
                continue

            #remove this once the generator knows how to get template items
            if '<' in dependency:
                templates = dependency[dependency.find('<') + 1:dependency.find('>')].split(',')
                current.dependencies += templates

            if not dependency in class_set and not iteration == 10:
                #print 'I dont have dependency: ', dependency
                classes.append(current)
                processed = False
                break 

        if processed:
            #print '### added', current.name
            sorted_classes.append(current)
            class_set.add(current.qualname)
            iteration = 0

        if previous_count == len(classes):            
            if iteration == 10:
                raise Exception("Couldn't resolve dependencies for the classes:" + str(classes))
            iteration += 1

        previous_count = len(classes)

    fake_cls = cpp.cpp_class('Fake')
    fake_cls.public = functions

    process_functions(fake_cls)

    data += sorted_classes + fake_cls.public + other

    context.binder_method = cpp.cpp_method('luaopen_gdx',
                returns=cpp.cpp_type('int'),
                attributes=['extern', '"C"'],
                params=[cpp.cpp_variable('L', cpp.cpp_type('lua_State', pointer=True))])
    context.luareg_declarations = []
    context.binder_method.exprs.append('lua_newtable(L)')
    context.binder_method.return_value = cpp.cpp_return(1)