extern "C" 
{
	#include "LuaLib\lua.h"
	#include "LuaLib\lualib.h"
	#include "LuaLib\lauxlib.h"
}

#pragma warning(disable: 4291)


class sqlite_wrapper
{
	struct callback_str
	{
		lua_State *L;
		char callback[256];
		char data[256];
	};
	sqlite3 *db;
	static int cCallback(void *Data, int argc, char **ColumnData, char **Columns)
	{
		if(!Data)
		{
			return 0;
		}
		callback_str *str = (callback_str *)Data;
		lua_State *L = str->L;

		lua_getglobal(L, str->callback);
		if(lua_isfunction(L, -1))
		{
			lua_pushstring(L, str->data);
			lua_pushnumber(L, argc);
			lua_createtable(L, argc, 0);
			int cdataTable = lua_gettop(L);

			lua_createtable(L, argc, 0);
			int cnameTable = lua_gettop(L);

			int index = 1;

			for(int i = 0; i < argc; i++)
			{
				lua_pushstring(L, ColumnData[i]);
				lua_rawseti(L, cdataTable, index);

				lua_pushstring(L, Columns[i]);
				lua_rawseti(L, cnameTable, index);
				index++;
			}
			LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
		}
		return 0;
	}
public:

	sqlite_wrapper(const char *dbFile)
	{
		if(sqlite3_open(dbFile, &db) != 0)
		{
			db = 0;
		}
	}

	~sqlite_wrapper()
	{
		if(db)
		{
			sqlite3_close(db);
		}
	}

	void Query(lua_State *L, const char *data, const char *luaCallback, const char *Query)
	{
		if(!db)
		{
			return;
		}
		callback_str str;

		if(luaCallback && *luaCallback)
		{
			strcpy(str.callback, luaCallback);
		}
		else
		{
			*str.callback = 0;
		}

		strcpy(str.data, data == 0 ? "" : data);
		str.L = L;
		
		char *Error = 0;
		if(sqlite3_exec(db, Query, cCallback, (void *)&str, &Error) != 0)
		{
			printf("%s\n", Error);
			sqlite3_free(Error);
		}
	}	
};

class Lua_sqlite_wrapper
{
	static const luaL_reg methods[];

	static int RegisterTable(lua_State *L) 
	{
		int argc = lua_gettop(L);

		sqlite_wrapper *a = new sqlite_wrapper(argc < 1 ? "db.db" : lua_tostring(L, 1));
		lua_boxpointer(L, a);
		luaL_getmetatable(L, "sqlite");
		lua_setmetatable(L, -2);		
		return 1;
	}

	static int Query(lua_State *L)
	{
		int argc = lua_gettop(L);
		if(argc < 3)
		{
			return 0;
		}
		sqlite_wrapper *a = GetInstance(L, 1);
		const char *data = lua_tostring(L, 2);
		const char *cb = lua_tostring(L, 3);
		const char *q = lua_tostring(L, 4);
		a->Query(L, data, cb, q);
		return 0;
	}

	static int DeleteInstance(lua_State *L) 
	{
		sqlite_wrapper *a = (sqlite_wrapper *)lua_unboxpointer(L, 1);
		delete a;
		return 0;
	}

public:

	static void Register(lua_State *L)
	{
		lua_newtable(L);                
		int methodtable = lua_gettop(L);
		luaL_newmetatable(L, "sqlite"); 
		int metatable = lua_gettop(L);

		lua_pushliteral(L, "__metatable");
		lua_pushvalue(L, methodtable);
		lua_settable(L, metatable);

		lua_pushliteral(L, "__index");
		lua_pushvalue(L, methodtable);
		lua_settable(L, metatable);

		lua_pushliteral(L, "__gc");
		lua_pushcfunction(L, DeleteInstance);
		lua_settable(L, metatable);

		lua_pop(L, 1);

		luaL_openlib(L, 0, methods, 0);
		lua_pop(L, 1);  

		lua_register(L, "sqlite", RegisterTable);
	}
	static sqlite_wrapper *GetInstance(lua_State *L, int n)
	{
		luaL_checktype(L, n, LUA_TUSERDATA);
		void *ud = luaL_checkudata(L, n, "sqlite");
		if(!ud) luaL_typerror(L, n, "sqlite");
		return *(sqlite_wrapper**)ud;
	}
};