/*****************************************************************************
 * $LastChangedDate: 2011-08-20 12:07:38 -0400 (Sat, 20 Aug 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Interface between C++ and Lua scripts.
 * @remarks module.txt documents how to export new C++ classes to Lua.
 *//*
 * 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.

#ifndef LUABIND_LUA_HH
#define LUABIND_LUA_HH 1

#include <vector>
#include <map>
#include "base/module.hh"
#include "base/singleton.hh"
using namespace base;

#define GET_LUA() (lua::interpreter::Lua::GetInstance())

namespace lua {
namespace interpreter {

/// luaconf.h defines a Lua number's type by "#define LUA_NUMBER double"
typedef LUA_NUMBER LuaNumber;

/// @brief Exception in Lua.
class LuaException : public Exception
{
public:
    LuaException( void ) : Exception() { }
    LuaException( const string& text ) : Exception(text) { }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief Lua interpreter.
///
/// NOTE: Most methods might throw a C++ exception.
///
class Lua
{
PREVENT_COPYING(Lua)
private:
    Lua( void );
    ~Lua();

public:
                DEFINE_GetInstance( Lua )  // Singleton
    void        ExportCmdLine( int argc, char** argv );
    void        SetScriptDir( const string& scriptDir );  // throw
    string      GetScriptDir( void ) { return mScriptDir; }
    void        RunScript( const string& scriptName, const bool recompile = false );  // throw
    bool        ReadVarAsBool( const string& varName );  // throw
    LuaNumber   ReadVarAsNumber( const string& varName );  // throw
    string      ReadVarAsString( const string& varName );  // throw
    LuaNumber   ReadTableAsNumber( const string& tableName, const int tableIdx );  // throw
    LuaNumber   ReadTableAsNumber( const string& tableName, const string& memberName );  // throw
    string      ReadTableAsString( const string& tableName, const int tableIdx );  // throw
    string      ReadTableAsString( const string& tableName, const string& memberName );  // throw
    LuaNumber   CallLuaFunc( const string& tableName, const string& funcName, const std::vector<LuaNumber>& args );

private:
    void SaveStack( void )
    {
        mSavedStack = lua_gettop( mLua );
    }

    void RestoreStack( void )
    {
        lua_settop( mLua, mSavedStack );
    }

    // Throws a C++ exception but first restores Lua's stack.
    void ThrowLuaException( const string& msg )
    {
        RestoreStack();
        throw LuaException( msg );
    }

private:
    // mFuncTable[] is defined at lua_bind/funcs.cc
    typedef std::map<string,int> BytecodeMap;
    DECLARE_SINGLETON_CLASS_VARS( Lua )
public:
    CLASS_VAR bool              msTypeErrorAsException;  ///< throw C++ exception instead of longjump back to Lua
private:
    CLASS_VAR const luaL_Reg    mFuncTable[];   ///< C interface functions exported to Lua scripts
    lua_State*                  mLua;           ///< the Lua interpreter (C code)
    string                      mScriptDir;     ///< directory containing scripts
    BytecodeMap                 mBytecodeMap;   ///< avoids recompiling Lua bytecode
    int                         mSavedStack;    ///< stack depth
};

} // namespace interpreter
} // namespace lua

#endif // LUABIND_LUA_HH
