//-------------------------------------------------------------------------------------------------
// <copyright file="LqrSMACLuaInstance.h" company="LiquidRobotics">
//  Copyright (c) Liquid Robotics Corporation, confidential and proprietary.  All rights reserved. 
// </copyright>
//
// <summary>
//	Declarations for LuaInstance, a wrapper for an instance of the lua interpreter.
//	It provides a thin wrapper for each API that is provided.
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------------------------

#ifndef __LqrSMACLuaInstance_H__
#define __LqrSMACLuaInstance_H__

#include "LqrSMACSharedLib.h"
#include <lua.hpp>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>

namespace LiquidRobotics {
namespace SMACFramework {

class SHARED_LIB_PUBLIC LuaInstance : public boost::noncopyable
{
public:

	typedef boost::shared_ptr<LuaInstance> Ptr;
	typedef std::string string;
	typedef boost::shared_ptr<string> StringPtr;

	typedef lua_CFunction CFunction;
	typedef lua_Debug Debug;
	typedef lua_Integer Integer;
	typedef lua_Number Number;
	typedef lua_Reader Reader;
	typedef lua_Writer Writer;

	struct StackStateHolder
	{
		StackStateHolder(LuaInstance& _lua);
		~StackStateHolder();

		LuaInstance& lua;
		bool initialized;
		const int top;
	};


	LuaInstance();
	LuaInstance(lua_State* thread);
	LuaInstance(const string& fileName);
	~LuaInstance();

	int L_dofile(const string& fileName, bool throwOnError = false);
	void* L_checkudata (int narg, const string& tname);
	void L_getmetatable(const string& registeredName);
	int L_loadfile(const string& fileName, bool throwOnError = false);
	int L_newmetatable(const string& registeredName);

	int gettop();
	void settop(int idx);
	void pushvalue(int idx);
	void remove(int idx);
	void insert(int idx);
	void replace(int idx);
	int checkstack(int sz);

	int isnumber(int idx);
	int isstring(int idx);
	int iscfunction(int idx);
	int isuserdata(int idx);
	int type(int idx);
	const char* _typename(int tp);

	int equal(int idx1, int idx2);
	int rawequal(int idx1, int idx2);
	int lessthan(int idx1, int idx2);

	Number tonumber(int idx);
	Integer tointeger(int idx);
	int toboolean(int idx);
	StringPtr tolstring(int idx);
	StringPtr tostring(int idx)	{ return tolstring(idx); }
	size_t objlen(int idx);
	CFunction tocfunction(int idx);
	void* touserdata(int idx);
	Ptr tothread(int idx);
	const void* topointer(int idx);

	void pushnil();
	void pushnumber(Number n);
	void pushinteger(Integer n);
	void pushlstring(const string& s);
	void pushlstring(const char *s, size_t l);
	void pushstring(const char* s);
	StringPtr pushvfstring(const char* fmt, va_list argp);
	StringPtr pushvfstring(const string& fmt, va_list argp) { return pushvfstring(fmt.c_str(), argp); }
	StringPtr pushfstring(const char* fmt, ...);
	void pushcclosure(lua_CFunction fn, int n);
	void pushboolean(int b);
	void pushlightuserdata(void *p);
	int pushthread(LuaInstance& thd);

	void gettable(int idx);
	void getfield(int idx, const char *k);
	void getfield(int idx, const string& k)				{ getfield(idx, k.c_str()); }
	void rawget(int idx);
	void rawgeti(int idx, int n);
	void createtable(int narr, int nrec);
	void* newuserdata(size_t sz);
	int getmetatable(int objindex);
#if LUA_VERSION_NUM < 502
	void getfenv(int idx);
#endif
	void getuservalue(int idx);

	void settable(int idx);
	void setfield(int idx, const char *k);
	void setfield(int idx, const string& k)				{ setfield(idx, k.c_str()); }
	void rawset(int idx);
	void rawseti(int idx, int n);
	int setmetatable(int objindex);
#if LUA_VERSION_NUM < 502
	int setfenv(int idx);
#else
	void setuservalue(int idx);
#endif

	void call(int nargs, int nresults);
	int pcall(int nargs, int nresults, int errfunc, bool throwOnError = false);
	int cpcall(lua_CFunction func, void *ud);
#if LUA_VERSION_NUM < 502
	int load(Reader reader, void *dt, const char *chunkname);
#else
	int load(Reader reader, void* data, const char* source, const char* mode);
#endif
	int dump(Writer writer, void *data);

	int yield(int nresults);
	int resume(int narg);
#if LUA_VERSION_NUM >= 502
	int resume(const LuaInstance& from, int narg);
#endif
	int status();

	int gc(int what, int data);

	int error();
	int next(int idx);
	void concat(int n);
	lua_Alloc getallocf(void **ud);
	void setallocf(lua_Alloc f, void *ud);

	void pop(int n);
	void newtable();
	void _register(const char* name, CFunction fn);
	void _register(const string& name, CFunction fn)	{ _register(name.c_str(), fn); }
	void pushcfunction(CFunction fn);
	size_t strlen(int idx);
	int isfunction(int idx);
	int istable(int idx);
	int islightuserdata(int idx);
	int isnil(int idx);
	int isboolean(int idx);
	int isthread(int idx);
	int isnone(int idx);
	int isnoneornil(int idx);
	//void pushliteral(const char* s);
	//void pushliteral(const string& s)					{ pushliteral(s.c_str()); }
	void setglobal(const char* name);
	void setglobal(const string& name)					{ setglobal(name.c_str()); }
	void getglobal(const char* name);
	void getglobal(const string& name)					{ getglobal(name.c_str()); }

	int upvalueindex(int i)								{ return lua_upvalueindex(i); }

	int clearhook(lua_Hook hook);
	int sethook(lua_Hook hook, int mask = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT, int count = 0);
	int getinfo(const string& what, lua_Debug& ar);

private:

	inline void CheckState();

	// NULL if not opened yet; lua doesn't give visibility about the content of lua_State,
	// so it can only be allocated, used or released with lua API's
	lua_State* L;

	// true if the instance is a thread (not closed on delete), false if the instance
	// represents a full state
	const bool isThread;
};

}
}

#endif
