/*****************************************************************************
 * $LastChangedDate: 2011-03-16 11:12:21 -0400 (Wed, 16 Mar 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Macros/functions to assist and simplify writing glue code between C++ and Lua.
 * @remarks Indeed, macros.cc is a C++ file.
 *------------------------------------------------------------------------------
 * REMEMBER: Indexs of tables passed between C++ and Lua must be ## ZERO-BASED ##.
 * Lua scripts must define special Lua classes such as Vec3 with zero-based indexs
 * because passing array-like one-based Lua tables to zero-based C++ is wrong.
 *------------------------------------------------------------------------------
 *//*
 * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

// Avoid lua_/LUA_ which is reserved by Lua.

#define LUABIND_MACROS_GENERIC_CC 1
#include "base/module.hh"
using namespace base;
#include "lua_bind/module.hh"
#include "lua_bind/bind.hh"

namespace lua {
namespace bind {

/*****************************************************************************
 * Register a binding.
 * This registers a table to emulate a class in Lua.
 * It also registers a metatable for userdata.
 *
 * Explanation of the metatable:
 * This answers why a metatable is created here,
 * but luaL_setmetatable() isn't called here.
 * Brief:
 * REGISTER_BINDING() must allocate a metatable for a userdata
 * in case of a binding that represents a C++ object as userdata.
 * Details:
 * A C++ object is represented by userdata.
 * In Lua, userdata requires a metatable.  
 * When Lua calls New() (to create a C++ object) it calls NEW_USERDATA_*().
 * NEW_USERDATA() creates the userdata then calls lua_getmetatable()/lua_setmetatable().
 * REGISTER_BINDING() has to allocate the metatable because NEW_USERDATA_*()
 * doesn't have access to the method table needed to register a metatable.
 *****************************************************************************/
void
REGISTER_BINDING( lua_State* L,
                  const char* metaTableName,
                  const char* className,
                  const struct luaL_reg methodTable[] )
{
SAVE_STACK(L);                                      // save/restore stack as this method
                                                    // pushes items without popping them
    // Do once:
    // Create a table under Lua's global table.
    // This is named the "module table".
    // The module table contains bindings and provides a namespace.
    lua_getglobal( L, LUABIND_MODULE_NAME );        // stack { moduleTable }
    if ( lua_isnil( L, -1 ) )
    {
        lua_newtable( L );
        lua_setglobal( L, LUABIND_MODULE_NAME );
    }

    // Make the class table under the module table.
    lua_getglobal( L, LUABIND_MODULE_NAME );        // stack { moduleTable, ... }
    lua_newtable( L );                              // stack { classTable, moduleTable, ... }
    lua_setfield( L, -2, className );               // stack unchanged

    // Register the class table.
    lua_getglobal( L, LUABIND_MODULE_NAME );        // stack { moduleTable, ... }
    lua_getfield( L, -1, className );               // stack { classTable, moduleTable, ... }
    luaL_register( L, NULL, methodTable );          // stack unchanged

    // Make and register a metatable.
    // To represent a C++ object, a binding could create userdata, which requires a metatable.
    // Assign metatable.__index with metatable itself.
    luaL_newmetatable( L, metaTableName );          // stack { metaTable, classTable, moduleTable, ... }
    lua_pushstring( L, "__index" );                 // stack { "__index", metaTable, classTable, moduleTable, ... }
    lua_pushvalue( L, -2 );                         // re-push metatable, stack { metaTable, "__index", metaTable, classTable, moduleTable, ... }
    lua_settable( L, -3 );                          // metatable.__index = metatable, pops k/v, stack { metaTable, classTable, moduleTable, ... }
    luaL_register( L, NULL, methodTable );          // stack unchanged

RESTORE_STACK(L);                                   // restore stack
}

} // namespace bind
} // namespace lua
