/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   For testing (compiled if DEBUG).
 * @remarks module.txt documents how to export new C++ classes to Lua.
 * Purpose of the second class (Test2) is to test the ability
 * of REGISTER_BINDING() to correctly export
 * more than one class to a C module's namespace.
 *//*
 * 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).
 *****************************************************************************/

#if DEBUG && ! DOXYGEN

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

#include "base/module.hh"
#include "base/stream.hh"
using namespace base;
#include "lua_bind/module.hh"
#include "lua_bind/bind.hh"

namespace lua {
namespace bind {

// Assigned by the test (tests/lua/src/lua.cc).
bool gLuaTestObjectWhileHoldingRefCount = false;

#define TEST_BIND_CLASS_NAME       "Test"
#define TEST_BIND_METATABLE_NAME   LUABIND_METATABLE_STRING(TEST_BIND_CLASS_NAME)
#define TEST2_BIND_CLASS_NAME      "Test2"
#define TEST2_BIND_METATABLE_NAME  LUABIND_METATABLE_STRING(TEST2_BIND_CLASS_NAME)

/// Type signatures.
enum { TYPESIG_TEST  = (uint('t')<<24) | (uint('e')<<16) | (uint('s')<<8) | uint('1') };
enum { TYPESIG_TEST2 = (uint('t')<<24) | (uint('e')<<16) | (uint('s')<<8) | uint('2') };

////////////////////////////////////////////////////////////////////////////////
/// @brief For Testing.
///
class Test : public Shared
{
public:
    Test( void )
    {
        SET_TYPESIG(this,TYPESIG_TEST);
        ++msInstanceCount;
        ++msInstanceCountNeverDec;
    }

    ~Test()
    {
        INVALIDATE_TYPESIG(this,TYPESIG_TEST);
        --msInstanceCount;
    }

    CLASS_VAR int msInstanceCount;
    CLASS_VAR int msInstanceCountNeverDec;
    DECLARE_TYPESIG(TYPESIG_TEST)
};
int Test::msInstanceCount = 0;
int Test::msInstanceCountNeverDec = 0;  // is never decremented

////////////////////////////////////////////////////////////////////////////////
/// @brief Second test class.
///
class Test2 : public Shared
{
public:
    Test2( void )
    {
        SET_TYPESIG(this,TYPESIG_TEST2);
    }

    ~Test2()
    {
        INVALIDATE_TYPESIG(this,TYPESIG_TEST2);
    }

    DECLARE_TYPESIG(TYPESIG_TEST2)
};

// For test to verify.
int GetTestClassInstanceCount( void )
{
    return Test::msInstanceCount;
}

int GetTestClassInstanceCountNeverDec( void )
{
    return Test::msInstanceCountNeverDec;
}

//==============================================================================
// C functions called by Lua that access C++ objects.
//==============================================================================

// Need a function to check typesig because of macro/template limitations.
INTERN void
CheckTypesigTest( shptr<Test> obj )
{
    CHECK_TYPESIG(obj,TYPESIG_TEST);
}
INTERN void
CheckTypesigTest2( shptr<Test2> obj )
{
    CHECK_TYPESIG(obj,TYPESIG_TEST2);
}

/*****************************************************************************
 * Create a Test object.
 *****************************************************************************/
INTERN int
Test_New( lua_State* L )
{
    // Create a C++ object.
    if ( not gLuaTestObjectWhileHoldingRefCount )
    {
        shptr<Test> test = new Test;
        return NEW_USERDATA_SHPTR<Test>( L, test, TEST_BIND_METATABLE_NAME );
    }
    else
    {
        // Lua's garbage-collector should not cause this to be destroyed
        // since a reference is being held.
        PERSISTENT shptr<Test> test = new Test;
        return NEW_USERDATA_SHPTR<Test>( L, test, TEST_BIND_METATABLE_NAME );
    }
}

INTERN int
Test2_New( lua_State* L )
{
    // Create a C++ object.
    if ( not gLuaTestObjectWhileHoldingRefCount )
    {
        shptr<Test2> test2 = new Test2;
        return NEW_USERDATA_SHPTR<Test2>( L, test2, TEST2_BIND_METATABLE_NAME );
    }
    else
    {
        // Lua's garbage-collector should not cause this to be destroyed
        // since a reference is being held.
        PERSISTENT shptr<Test2> test2 = new Test2;
        return NEW_USERDATA_SHPTR<Test2>( L, test2, TEST2_BIND_METATABLE_NAME );
    }
}

/*****************************************************************************
 * Destructor.
 *****************************************************************************/
INTERN int
Test_Destroy( lua_State* L )
{
    return DESTROY_USERDATA_SHPTR<Test>( L, CheckTypesigTest );
}
INTERN int
Test2_Destroy( lua_State* L )
{
    return DESTROY_USERDATA_SHPTR<Test2>( L, CheckTypesigTest2 );
}

/*****************************************************************************
 * Test returning a constant.
 *****************************************************************************/
INTERN int
Test_GetABC( lua_State* L )
{
    // This is to test the typesig.  Popping is ok.
    shptr<Test> test = POP_USERDATA_AS_SHPTR<Test>( L );
    CHECK_TYPESIG( test, TYPESIG_TEST );

    // Magic value for test to check.
    lua_pushstring( L, "ABC" );
    return 1;  // number of stack items
}
INTERN int
Test2_GetDEF( lua_State* L )
{
    // This is to test the typesig.  Popping is ok.
    shptr<Test2> test2 = POP_USERDATA_AS_SHPTR<Test2>( L );
    CHECK_TYPESIG( test2, TYPESIG_TEST2 );

    // Magic value for test to check.
    lua_pushstring( L, "DEF" );
    return 1;  // number of stack items
}

/*****************************************************************************
 * Test Vec3.
 *****************************************************************************/
INTERN int
GetVec3( lua_State* L )
{
    Vector3 v( 123.0f, 456.0f, 789.0f );
    PUSH_VECTOR3( L, v );
    return 1;  // number of stack items
}

INTERN int
SetVec3( lua_State* L )
{
    Vector3 v = POP_VECTOR3<Vector3>( L );
    if ( v[XX] != 123.0f or v[YY] != 456.0f or v[ZZ] != 789.0f )
    {
        CERROR << "Test of SetVec3 failed" << std::endl;
        EXIT(1);
    }
    return 0;  // number of stack items
}

//==============================================================================
// Register C functions with Lua.
//==============================================================================

// Functions for testing.
INTERN const struct luaL_reg sFuncTable[] =
{
    {   "GetVec3",      GetVec3 },
    {   "SetVec3",      SetVec3 },
    {   NULL,           NULL }
};

// The Test class.
INTERN const struct luaL_reg sMethodTable_Test[] =
{
    {   "new",          Test_New },
    {   "__gc",         Test_Destroy },
    {   "GetABC",       Test_GetABC },
    {   NULL,           NULL }
};

// The Test2 class.
INTERN const struct luaL_reg sMethodTable_Test2[] =
{
    {   "new",          Test2_New },
    {   "__gc",         Test2_Destroy },
    {   "GetDEF",       Test2_GetDEF },
    {   NULL,           NULL }
};

void
RegisterTest( lua_State* L )
{
    // Register functions which will appear as test.Func() in Lua.
    luaL_register( L, "test", sFuncTable );

    // Register the Test class.
    REGISTER_BINDING( L,
                      TEST_BIND_METATABLE_NAME,
                      TEST_BIND_CLASS_NAME,
                      sMethodTable_Test );

    // Register the Test2 class.
    REGISTER_BINDING( L,
                      TEST2_BIND_METATABLE_NAME,
                      TEST2_BIND_CLASS_NAME,
                      sMethodTable_Test2 );
}

} // namespace bind
} // namespace lua

#endif // DEBUG && ! DOXYGEN
