#include <Winsock2.h>
#include <Windows.h>
#include <Wininet.h>
#include "scripts.h"
#include "engine.h"
#include "Misc.h"
extern "C" 
{
	#include "LuaLib\lua.h"
	#include "LuaLib\lualib.h"
	#include "LuaLib\lauxlib.h"
}
#include "sqlite3.h"
#include "LuaManager.h"
#include "LuaFunctions.h"
#include "Lua_Class.h"

#pragma warning(disable: 4291)

LuaScriptManager::Script *LuaScriptManager::Scripts[256];

void LuaScriptManager::Load()
{
	memset((void *)Scripts, 0, 256*4);
}

ScriptImpClass *LuaScriptManager::Get_Script(GameObject *obj, int scriptID)
{
	SimpleDynVecClass<GameObjObserverClass *> *observers = &((ScriptableGameObj *)obj)->Observers;
	int x = observers->Count();
	for (int i = 0;i < x;i++)
	{
		if((*observers)[i]->GetID() == scriptID)
		{
			return ((ScriptImpClass*)(*observers)[i]);
		}
	}
	return 0;
}

void LuaScriptManager::Lua_DestroyScript(GameObject *obj, int scriptID)
{
	if(!obj)
	{
		return;
	}
	ScriptImpClass *script = Get_Script(obj, scriptID);
	if(!script)
	{
		return;
	}
	script->Destroy_Script();
}

float LuaScriptManager::Lua_Get_Float_Parameter(GameObject *obj, int scriptID, const char *Name)
{
	if(!obj)
	{
		return 0;
	}
	ScriptImpClass *script = Get_Script(obj, scriptID);
	if(!script)
	{
		return 0;
	}
	return script->Get_Float_Parameter(Name);
}

int LuaScriptManager::Lua_Get_Int_Parameter(GameObject *obj, int scriptID, const char *Name)
{
	if(!obj)
	{
		return 0;
	}
	ScriptImpClass *script = Get_Script(obj, scriptID);
	if(!script)
	{
		return 0;
	}
	return script->Get_Int_Parameter(Name);
}

const char *LuaScriptManager::Lua_Get_String_Parameter(GameObject *obj, int scriptID, const char *Name)
{
	if(!obj)
	{
		return "";
	}
	ScriptImpClass *script = Get_Script(obj, scriptID);
	if(!script)
	{
		return "";
	}
	return script->Get_Parameter(Name);
}

int LuaScriptManager::Register_Script(lua_State *L)
{
	if(!lua_istable(L, -1))
	{
		luaL_error(L, "Bad argument #1 to Register_Script. Expected table.");
		return 0;
	}

	const char *Name = luaL_checkstring(L, -3);
	if(!Name)
	{
		return 0;
	}

	const char *Parameters = luaL_checkstring(L, -2);
	if(!Parameters)
	{
		return 0;
	}

	for(int script_it = 0; script_it < 256; script_it++)
	{
		if(Scripts[script_it])
		{
			if(_stricmp(Scripts[script_it]->Name, Name) == 0)
			{
				lua_pop(L, 1);
				luaL_error(L, "You cannot register more than one script with the same name.");
				return 0;
			}
		}
	}

	int uRef = luaL_ref(L, LUA_REGISTRYINDEX);
	
	Script *s = new Script;
	s->uRef = uRef;
	strcpy(s->Name, Name);
	strcpy(s->Param, Parameters);
	s->scriptreg = new ScriptRegistrant<LuaScript>((char *)Name, (char *)Parameters);
	s->lua = L;

	for(int script_it = 0; script_it < 256; script_it++)
	{
		if(Scripts[script_it] == 0)
		{
			Scripts[script_it] = s;
			return 0;
		}
	}
	return 0;	
}

bool LuaScriptManager::Report_Errors(lua_State *L, int status)
{
	if(status != 0) 
	{
		printf("%s\n", lua_tostring(L, -1));
		lua_pop(L, 1); 
		return 1;
	}
	return 0;
}

void LuaScriptManager::Lua_Start_Timer(GameObject *obj, int scriptID, float Time, int Number)
{
	if(!obj)
	{
		return;
	}
	ScriptImpClass *script = Get_Script(obj, scriptID);
	if(!script)
	{
		return;
	}
	Commands->Start_Timer(obj, script, Time, Number);
}



void LuaScriptManager::Cleanup()
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		delete s->scriptreg;
		delete s;
		
	}
	memset((void *)Scripts, 0, 256*4);
}

void LuaScriptManager::RegisterScriptFunctions(lua_State *L)
{
	lua_register(L, "DestroyScript", LuaScriptManager::Lua_DestroyScript_Wrap);
	lua_register(L, "Get_Float_Parameter", LuaScriptManager::Lua_Get_Float_Parameter_Wrap);
	lua_register(L, "Get_Int_Parameter", LuaScriptManager::Lua_Get_Int_Parameter_Wrap);
	lua_register(L, "Get_String_Parameter", LuaScriptManager::Lua_Get_String_Parameter_Wrap);
	lua_register(L, "Start_Timer", LuaScriptManager::Lua_Start_Timer_Wrap);
	lua_register(L, "Register_Script", LuaScriptManager::Register_Script);
}
int LuaScriptManager::Lua_DestroyScript_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 2)
	{
		return 0;
	}
	int scriptID = (int)lua_tonumber(L, 1);
	int objID = (int)lua_tonumber(L, 2);
	LuaScriptManager::Lua_DestroyScript(Commands->Find_Object(objID), scriptID);
	return 0;
}
int LuaScriptManager::Lua_Get_Float_Parameter_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 3)
	{
		return 0;
	}
	int scriptID = (int)lua_tonumber(L, 1);
	int objID = (int)lua_tonumber(L, 2);
	const char *name = lua_tostring(L, 3);
	lua_pushnumber(L, LuaScriptManager::Lua_Get_Float_Parameter(Commands->Find_Object(objID), scriptID, name));
	return 1;
}
int LuaScriptManager::Lua_Get_Int_Parameter_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 3)
	{
		return 0;
	}
	int scriptID = (int)lua_tonumber(L, 1);
	int objID = (int)lua_tonumber(L, 2);
	const char *name = lua_tostring(L, 3);
	lua_pushnumber(L, LuaScriptManager::Lua_Get_Int_Parameter(Commands->Find_Object(objID), scriptID, name));
	return 1;
}
int LuaScriptManager::Lua_Get_String_Parameter_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 3)
	{
		return 0;
	}
	int scriptID = (int)luaL_checknumber(L, 1);
	int objID = (int)luaL_checknumber(L, 2);
	const char *name = luaL_checkstring(L, 3);
	lua_pushstring(L, LuaScriptManager::Lua_Get_String_Parameter(Commands->Find_Object(objID), scriptID, name));
	return 1;
}
int LuaScriptManager::Lua_Start_Timer_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 4)
	{
		return 0;
	}
	int scriptID = (int)lua_tonumber(L, 1);
	int obj = (int)lua_tonumber(L, 2);
	float Time = (float)lua_tonumber(L, 3);
	int Number = (int)lua_tonumber(L, 4);
	LuaScriptManager::Lua_Start_Timer(Commands->Find_Object(obj), scriptID, Time, Number);
	return 0;
}

//The dynamic script itself
void LuaScriptManager::LuaScript::Created(GameObject *obj)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Created");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				LuaManager::Report_Errors(L, lua_pcall(L, 3, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Destroyed(GameObject *obj)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Destroyed");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				LuaManager::Report_Errors(L, lua_pcall(L, 3, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Killed(GameObject *obj,GameObject *shooter)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Killed");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushnumber(L, Commands->Get_ID(shooter));
				LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Damaged(GameObject *obj,GameObject *damager,float damage)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Damaged");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushnumber(L, Commands->Get_ID(damager));
				lua_pushnumber(L, damage);
				LuaManager::Report_Errors(L, lua_pcall(L, 5, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Custom(GameObject *obj,int message,int param,GameObject *sender)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Custom");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushnumber(L, message);
				lua_pushnumber(L, param);
				lua_pushnumber(L, Commands->Get_ID(sender));
				LuaManager::Report_Errors(L, lua_pcall(L, 6, 0, 0));
			}
		}
	}
}

void LuaScriptManager::LuaScript::Enemy_Seen(GameObject *obj,GameObject *seen)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Enemy_Seen");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushnumber(L, Commands->Get_ID(seen));
				LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Action_Complete(GameObject *obj,int action, ActionCompleteReason reason)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Action_Complete");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushnumber(L, action);

				LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Timer_Expired(GameObject *obj,int number)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Timer_Expired");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushnumber(L, number);
				LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Animation_Complete(GameObject *obj,const char *anim)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Animation_Complete");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushstring(L, anim);
				LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Poked(GameObject *obj,GameObject *poker)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Poked");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushnumber(L, Commands->Get_ID(poker));
				LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Entered(GameObject *obj,GameObject *enter)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Entered");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushnumber(L, Commands->Get_ID(enter));
				LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
			}
		}
	}
}
void LuaScriptManager::LuaScript::Exited(GameObject *obj,GameObject *exit)
{
	for(int script_it = 0; script_it < 256; script_it++)
	{
		Script *s = Scripts[script_it];
		if(!s)
		{
			continue;
		}
		if(strcmp(s->Name, this->Get_Name()) == 0)
		{
			lua_State *L = s->lua;
			if(!L)
			{
				continue;
			}
			lua_rawgeti(L, LUA_REGISTRYINDEX, s->uRef);
			if(!lua_istable(L, -1))
			{
				return;
			}

			lua_getfield(L, -1, "Exited");
			if(lua_isfunction(L, -1))
			{
				lua_pushvalue(L, -2);
				lua_pushnumber(L, GetID());
				lua_pushnumber(L, Commands->Get_ID(obj));
				lua_pushnumber(L, Commands->Get_ID(exit));
				LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
			}
		}
	}
}



//LuaManager

lua_State *LuaManager::Lua[256];
bool LuaManager::Reload_Flag = 0;

void LuaManager::Load()
{
	memset((void *)Lua, 0, 256*4);
}

void LuaManager::ShowBanner()
{
	printf("|---------------------------------------------------|\n");
	printf("| Lua 5.0 Copyright (C) 1994-2003 Tecgraf, PUC-Rio  |\n");
	printf("| R. Ierusalimschy, L. H. de Figueiredo & W. Celes  |\n");
	printf("| www.lua.org                                       |\n");
	printf("| Embedded with scripts.dll by jnz                  |\n");
	printf("| www.dcomproductions.com                           |\n");
	printf("|---------------------------------------------------|\n");
}

void LuaManager::LoadLua(const char *LuaFile)
{
	lua_State *L = lua_open();
	luaL_openlibs(L);
	
	LuaSocketManager::Register(L);
	AddFunctions(L);
	int s = luaL_loadfile(L, LuaFile);
	if (s == 0) 
	{
		s = lua_pcall(L, 0, LUA_MULTRET, 0);
	}
	if(Report_Errors(L, s))
	{
		lua_close(L);
		printf("Failed to load plugin: %s\n", LuaFile);
		return;
	}

	lua_getglobal(L, "OnLoad");
	if(lua_isfunction(L, -1))
	{
		LuaManager::Report_Errors(L, lua_pcall(L, 0, 0, 0));
	}
	else
	{
		lua_setglobal(L, "OnLoad");
	}

	printf("Loaded plugin: %s\n", LuaFile);
	
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		if(!Lua[lua_it])
		{
			Lua[lua_it] = L;
			break;
		}
	}
}


void LuaManager::Cleanup()
{
	Call_Unload();
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		if(Lua[lua_it])
		{
			printf("Unloaded a lua file\n");
			
			lua_close(Lua[lua_it]);
			Lua[lua_it] = 0;
		}
	}
}

bool LuaManager::Report_Errors(lua_State *L, int status)
{
	if(status != 0) 
	{
		const char *err = lua_tostring(L, -1);
		lua_pop(L, 1);

		lua_getglobal(L, "OnError");
		if(lua_isfunction(L, -1))
		{
			lua_pushstring(L, err);
			if(lua_pcall(L, 1, 0, 0) != 0)
			{
				printf("[Error] Could not call OnError\n%s\n", lua_tostring(L, -1));
				lua_pop(L, 1);
			}
		}
		else
		{
			printf("[Error] %s\n", err);
		}
		return 1;
	}

	return 0;
}

//hooks

void LuaManager::Call_Unload()
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "OnUnload");
		if(lua_isfunction(L, -1))
		{
			LuaManager::Report_Errors(L, lua_pcall(L, 0, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnUnload");
		}
	}
}

bool LuaManager::Call_Chat_Hook(int ID, int Type, WideStringClass &Msg, int Target)
{
	int ret = 1;
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "OnChat");
		if(lua_isfunction(L, -1))
		{
			const char *Msg2 = WideCharToChar((const wchar_t *)Msg);
			lua_pushnumber(L, ID);
			lua_pushnumber(L, Type);
			lua_pushstring(L, Msg2);
			lua_pushnumber(L, Target);
			LuaManager::Report_Errors(L, lua_pcall(L, 4, 1, 0));
			ret = !ret ? ret : (int)lua_tonumber(L, -1);
			delete []Msg2;
		}
		else
		{
			lua_setglobal(L, "OnChat");
		}
	}
	return ret != 0 ? 1 : 0;
}
void LuaManager::Call_Host_Hook(int ID, int Type, const char *Msg)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "OnHostMessage");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, ID);
			lua_pushnumber(L, Type);
			lua_pushstring(L, Msg);
			LuaManager::Report_Errors(L, lua_pcall(L, 3, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnHostMessage");
		}
	}
}
void LuaManager::Call_Player_Join_Hook(int ID, const char *Nick)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "OnPlayerJoin");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, ID);
			lua_pushstring(L, Nick);
			LuaManager::Report_Errors(L, lua_pcall(L, 2, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnPlayerJoin");
		}
	}
}
void LuaManager::Call_Player_Leave_Hook(int ID)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		
		lua_getglobal(L, "OnPlayerLeave");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, ID);
			LuaManager::Report_Errors(L, lua_pcall(L, 1, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnPlayerLeave");
		}
	}
}
void LuaManager::Call_Level_Loaded_Hook()
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		
		lua_getglobal(L, "OnLevelLoaded");
		if(lua_isfunction(L, -1))
		{
			LuaManager::Report_Errors(L, lua_pcall(L, 0, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnLevelLoaded");
		}
	}
}
void LuaManager::Call_GameOver_Hook()
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		
		lua_getglobal(L, "OnGameOver");
		if(lua_isfunction(L, -1))
		{
			LuaManager::Report_Errors(L, lua_pcall(L, 0, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnGameOver");
		}
	}
}
void LuaManager::Call_Console_Output_Hook(const char *Output)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		
		lua_getglobal(L, "OnConsoleOutput");
		if(lua_isfunction(L, -1))
		{
			lua_pushstring(L, Output);
			LuaManager::Report_Errors(L, lua_pcall(L, 1, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnConsoleOutput");
		}
	}
}
void LuaManager::Call_Version_Hook(int ID, float Ver)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		
		lua_getglobal(L, "OnPlayerVersion");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, ID);
			lua_pushnumber(L, Ver);
			LuaManager::Report_Errors(L, lua_pcall(L, 2, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnPlayerVersion");
		}	
	}
}

void LuaManager::Purchase_Hook(BaseControllerClass *base, GameObject *purchaser, unsigned int cost, unsigned int preset,unsigned int purchaseret,const char *data)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		
		lua_getglobal(L, data);
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, Commands->Get_ID(purchaser));
			lua_pushnumber(L, cost);
			lua_pushnumber(L, preset);
			lua_pushnumber(L, purchaseret);
			LuaManager::Report_Errors(L, lua_pcall(L, 4, 0, 0));
		}
		else
		{
			lua_setglobal(L, data);
		}
	}
}

void LuaManager::Call_Object_Hook(void *data, GameObject *obj)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "OnObjectCreate");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, Commands->Get_ID(obj));
			LuaManager::Report_Errors(L, lua_pcall(L, 1, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnObjectCreate");
		}
	}
}

void LuaManager::Call_DDE_Hook(const char *DDEStr)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "OnDDE");
		if(lua_isfunction(L, -1))
		{
			lua_pushstring(L, DDEStr);
			LuaManager::Report_Errors(L, lua_pcall(L, 1, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnDDE");
		}
	}
}

void LuaManager::Call_Think_Hook()
{
	if(Reload_Flag)
	{
		Reload_Flag = 0;
		Reload_All();
	}
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "OnThink");
		if(lua_isfunction(L, -1))
		{
			LuaManager::Report_Errors(L, lua_pcall(L, 0, 0, 0));
		}
		else
		{
			lua_setglobal(L, "OnThink");
		}
	}	
}

void LuaManager::Call_Serial_Hook(int ID, const char *Serial)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "Serial_Hook");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, ID);
			lua_pushstring(L, Serial);
			LuaManager::Report_Errors(L, lua_pcall(L, 2, 0, 0));
		}
		else
		{
			lua_setglobal(L, "Serial_Hook");
		}
	}
}

void LuaManager::Call_LoadingE_Hook(int ID, bool Loaded)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "Loading_Hook");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, ID);
			lua_pushnumber(L, Loaded ? 1.0 : 0.0);
			LuaManager::Report_Errors(L, lua_pcall(L, 2, 0, 0));
		}
		else
		{
			lua_setglobal(L, "Loading_Hook");
		}
	}
}

bool LuaManager::Call_Damage_Hook(int ID, int Damager, int Target, float Damage, unsigned int Warhead)
{
	int ret = 1;
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "Damage_Hook");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, ID);
			lua_pushnumber(L, Damager);
			lua_pushnumber(L, Target);
			lua_pushnumber(L, Damage);
			lua_pushnumber(L, Warhead);
			LuaManager::Report_Errors(L, lua_pcall(L, 5, 1, 0));
			ret = !ret ? ret : (int)lua_tonumber(L, -1);
		}
		else
		{
			lua_setglobal(L, "Damage_Hook");
		}
	}
	return ret;
}

void LuaManager::Call_Ping_Hook(int ID, int Ping)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "Ping_Hook");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, ID);
			lua_pushnumber(L, Ping);
			LuaManager::Report_Errors(L, lua_pcall(L, 2, 0, 0));
		}
		else
		{
			lua_setglobal(L, "Ping_Hook");
		}
	}
}

bool LuaManager::Call_Suicide_Hook(int ID)
{
	int ret = 1;
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "Suicide_Hook");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, ID);
			LuaManager::Report_Errors(L, lua_pcall(L, 1, 1, 0));
			ret = !ret ? ret : (int)lua_tonumber(L, -1);
		}
		else
		{
			lua_setglobal(L, "Suicide_Hook");
		}
	}
	return ret;
}

bool LuaManager::Call_Radio_Hook(int Team, int PlayerId, int a, int RadioId, int b)
{
	int ret = 1;
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "Radio_Hook");
		if(lua_isfunction(L, -1))
		{
			lua_pushnumber(L, Team);
			lua_pushnumber(L, PlayerId);
			lua_pushnumber(L, a);
			lua_pushnumber(L, RadioId);
			lua_pushnumber(L, b);
			LuaManager::Report_Errors(L, lua_pcall(L, 5, 1, 0));
			ret = !ret ? ret : (int)lua_tonumber(L, -1);
		}
		else
		{
			lua_setglobal(L, "Radio_Hook");
		}
	}
	return ret;
}


void LuaManager::CallInvoke(const char *Function, const char *Arg)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, Function);
		if(lua_isfunction(L, -1))
		{
			lua_pushstring(L, Arg);
			LuaManager::Report_Errors(L, lua_pcall(L, 1, 0, 0));
		}
	}
}

void LuaManager::CallHostCallback(const char *Data, const char *Host)
{
	for(int lua_it = 0; lua_it < 256; lua_it++)
	{
		lua_State *L = Lua[lua_it];
		if(!L)
		{
			continue;
		}
		lua_getglobal(L, "HostCallback");
		if(lua_isfunction(L, -1))
		{
			lua_pushstring(L, Data);
			LuaManager::Report_Errors(L, lua_pcall(L, 1, 0, 0));
		}
	}
}

void LuaManager::Reload_All()
{
	LuaManager::Cleanup();
	LuaManager::Load();

	char basepath[64];
	GetCurrentDirectory(64, basepath);
	char pluginsdir[256];
	sprintf(pluginsdir, "%s\\LuaPlugins\\*", basepath);

	char *plugins[256];
	memset((void *)plugins, 0, 256*4);
	GetFiles(pluginsdir, plugins, 256);

	for(int i = 0; i < 256; i++)
	{
		char *x = plugins[i];
		if(!x)
		{
			continue;
		}
		char *path = new char[strlen(x)+strlen(basepath)+14];
		sprintf(path, "%s\\LuaPlugins\\%s", basepath, x);
		LuaManager::LoadLua(path);
		delete []path;
		delete []x;
	}
}
//socket manager

TCPSocket *LuaSocketManager::Sockets[64];
int LuaSocketManager::Upload_State;
int LuaSocketManager::Upload_Error;

void LuaSocketManager::Load()
{
	memset((void *)Sockets, 0, 4*64);
	WSADATA wsaData;
	if(WSAStartup(0x202, &wsaData))
	{
		printf("Error starting winsock, LuaSocket may not work.\n");
	}

}

int LuaSocketManager::Connect(const char *IP, int port)
{
	TCPSocket *s = new TCPSocket;

	if(!s->Client(IP, port))
	{
		delete s;
		return -1;
	}

	for(int i = 0; i < 64; i++)
	{
		if(!Sockets[i])
		{
			Sockets[i] = s;
			return i;
		}
	}
	delete s;
	return -1;
}

int LuaSocketManager::Server(const char *IP, int port)
{
	TCPSocket *s = new TCPSocket;

	if(!s->Server(IP, port, 10))
	{
		delete s;
		return -1;
	}

	for(int i = 0; i < 64; i++)
	{
		if(!Sockets[i])
		{
			Sockets[i] = s;
			return i;
		}
	}
	delete s;
	return -1;
}

int LuaSocketManager::Accept(int Socket)
{
	if(Socket < 0)
	{
		return -1;
	}
	TCPSocket *s = Sockets[Socket];
	if(!s)
	{
		return -1;
	}

	TCPSocket *c = new TCPSocket;
	if(!s->Accept(c))
	{
		delete c;
		return -1;
	}
	
	for(int i = 0; i < 64; i++)
	{
		if(!Sockets[i])
		{
			Sockets[i] = c;
			return i;
		}
	}
	delete c;
	return -1;
}

int LuaSocketManager::Recv(int Socket, char *Data, int Length)
{
	if(Socket < 0)
	{
		return -1;
	}
	TCPSocket *s = Sockets[Socket];
	if(!s)
	{
		return -1;
	}

	if(!s->RecviveData(Data, &Length))
	{
		return -1;
	}
	Data[Length] = 0;
	return 0;
}

int LuaSocketManager::Send(int Socket, const char *Data)
{
	if(Socket < 0)
	{
		return -1;
	}
	TCPSocket *s = Sockets[Socket];
	if(!s)
	{
		return -1;
	}

	if(!s->SendData(Data, strlen(Data)))
	{
		return -1;
	}
	return 0;
}

int LuaSocketManager::Is_DataAvaliable(int Socket)
{
	if(Socket < 0)
	{
		return -1;
	}
	TCPSocket *s = Sockets[Socket];
	if(!s)
	{
		return -1;
	}

	if(!s->Is_DataAvaliable())
	{
		return -1;
	}
	return 0;
}

int LuaSocketManager::Is_ClientWaiting(int Socket)
{
	if(Socket < 0)
	{
		return -1;
	}
	TCPSocket *s = Sockets[Socket];
	if(!s)
	{
		return -1;
	}

	if(!s->Is_ConnectionWaiting())
	{
		return -1;
	}
	return 0;
}

void LuaSocketManager::Disconnect(int Socket)
{
	if(Socket < 0)
	{
		return;
	}
	TCPSocket *s = Sockets[Socket];
	if(!s)
	{
		return;
	}

	s->Destroy();
	delete s;
	Sockets[Socket] = 0;
}


int LuaSocketManager::Lua_Connect_Wrap(lua_State *L)
{

	int argc = lua_gettop(L);
	if(argc < 2)
	{
		return 0;
	}
	const char *Host = lua_tostring(L, 1);
	int Port = (int)lua_tonumber(L, 2);
	lua_pushnumber(L, Connect(Host, Port));
	return 1;
}

int LuaSocketManager::Lua_Server_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 2)
	{
		return 0;
	}
	const char *Host = lua_tostring(L, 1);
	int Port = (int)lua_tonumber(L, 2);
	lua_pushnumber(L, Server(Host, Port));
	return 1;
}

int LuaSocketManager::Lua_Accept_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 1)
	{
		return 0;
	}
	int Sock = (int)lua_tonumber(L, 1);
	lua_pushnumber(L, Accept(Sock));
	return 1;
}

int LuaSocketManager::Lua_Recv_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 1)
	{
		return 0;
	}
	char data[1024];
	*data = 0;
	int Sock = (int)lua_tonumber(L, 1);
	Recv(Sock, data, 1023);
	lua_pushstring(L, data);
	return 1;
}

int LuaSocketManager::Lua_Send_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 2)
	{
		return 0;
	}
	int Sock = (int)lua_tonumber(L, 1);
	const char *str = lua_tostring(L, 2);
	lua_pushnumber(L, Send(Sock, str));
	return 1;
}

int LuaSocketManager::Lua_DataAvaliable_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 1)
	{
		return 0;
	}
	int Sock = (int)lua_tonumber(L, 1);
	lua_pushnumber(L, Is_DataAvaliable(Sock));
	return 1;
}

int LuaSocketManager::Lua_ClientWaiting_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 1)
	{
		return 0;
	}
	int Sock = (int)lua_tonumber(L, 1);
	lua_pushnumber(L, Is_ClientWaiting(Sock));
	return 1;
}

int LuaSocketManager::Lua_Disconnect_Wrap(lua_State *L)
{
	int argc = lua_gettop(L);
	if(argc < 1)
	{
		return 0;
	}
	
	int Sock = (int)lua_tonumber(L, 1);
	Disconnect(Sock);
	return 0;
}


void LuaSocketManager::Register(lua_State *L)
{
	lua_register(L, "Client", Lua_Connect_Wrap);
	lua_register(L, "Server", Lua_Server_Wrap);
	lua_register(L, "Accept", Lua_Accept_Wrap);
	lua_register(L, "Recv", Lua_Recv_Wrap);
	lua_register(L, "Send", Lua_Send_Wrap);
	lua_register(L, "DataAvaliable", Lua_DataAvaliable_Wrap);
	lua_register(L, "ClientWaiting", Lua_ClientWaiting_Wrap);
	lua_register(L, "Disconnect", Lua_Disconnect_Wrap);
	lua_register(L, "Upload", Lua_Upload);
	lua_register(L, "Check_Upload", Lua_Check_Upload);
	lua_register(L, "Check_Upload_Error", Lua_Check_Upload_Error);
}

void LuaSocketManager::Cleanup()
{
	for(int i = 0; i < 64; i++)
	{
		if(Sockets[i])
		{
			delete Sockets[i];
		}
	}
	memset((void *)Sockets, 0, 4*64);

	WSACleanup();
}

DWORD __stdcall LuaSocketManager::Upload(void *x)
{
	UploadIfo *ifo = (UploadIfo *)x;

	Upload_State = 1;
	HINTERNET intern = InternetOpen("Lua", INTERNET_OPEN_TYPE_DIRECT, 0, 0, 0);
	if(!intern)
	{
		Upload_State = 2;
		Upload_Error = GetLastError();
		delete ifo;
		return 0;
	}
	HINTERNET intern2 = InternetConnect(intern, ifo->Host, (INTERNET_PORT)ifo->Port, ifo->Username, ifo->Password, INTERNET_SERVICE_FTP, 0, 0);

	if(!intern2)
	{
		Upload_State = 3;
		Upload_Error = GetLastError();
		InternetCloseHandle(intern);
		delete ifo;
		return 0;
	}

	if(FtpPutFile(intern2, ifo->LocalFile, ifo->RemoteFile, FTP_TRANSFER_TYPE_BINARY, 0))
	{
		Upload_State = 5;
	}
	else
	{
		Upload_State = 4;
		Upload_Error = GetLastError();
	}

	InternetCloseHandle(intern);
	delete ifo;
	return 0;
}

int LuaSocketManager::Lua_Upload(lua_State *L)
{
	if(Upload_State > 0 || Upload_State == 5)
	{	
		luaL_error(L, "There is already an upload in progress");
		return 0;
	}
	Upload_State = 0;

	if(!lua_istable(L, -1))
	{
		luaL_error(L, "Bad argument #1 to Upload. Expected table.");
		return 0;
	}
	
	UploadIfo *ifo = new UploadIfo;

	lua_getfield(L, -1, "Host");
	if(!lua_isstring(L, -1))
	{
		luaL_error(L, "Bad table passed to Upload. Field 'Host' was not found.");
		delete ifo;
		return 0;
	}
	strcpy(ifo->Host, lua_tostring(L, -1));
	lua_pop(L, 1);

	lua_getfield(L, -1, "Port");
	if(!lua_isnumber(L, -1))
	{
		luaL_error(L, "Bad table passed to Upload. Field 'Port' was not found.");
		delete ifo;
		return 0;
	}
	ifo->Port = (int)lua_tonumber(L, -1);
	lua_pop(L, 1);

	lua_getfield(L, -1, "LocalFile");
	if(!lua_isstring(L, -1))
	{
		luaL_error(L, "Bad table passed to Upload. Field 'LocalFile' was not found.");
		delete ifo;
		return 0;
	}
	strcpy(ifo->LocalFile, lua_tostring(L, -1));
	lua_pop(L, 1);

	lua_getfield(L, -1, "RemoteFile");
	if(!lua_isstring(L, -1))
	{
		luaL_error(L, "Bad table passed to Upload. Field 'RemoteFile' was not found.");
		delete ifo;
		return 0;
	}
	strcpy(ifo->RemoteFile, lua_tostring(L, -1));
	lua_pop(L, 1);

	lua_getfield(L, -1, "Username");
	if(!lua_isstring(L, -1))
	{
		luaL_error(L, "Bad table passed to Upload. Field 'Username' was not found.");
		delete ifo;
		return 0;
	}
	strcpy(ifo->Username, lua_tostring(L, -1));
	lua_pop(L, 1);

	lua_getfield(L, -1, "Password");
	if(!lua_isstring(L, -1))
	{
		luaL_error(L, "Bad table passed to Upload. Field 'Password' was not found.");
		delete ifo;
		return 0;
	}
	strcpy(ifo->Password, lua_tostring(L, -1));
	lua_pop(L, 1);

	
	CreateThread(NULL, NULL, LuaSocketManager::Upload, (void *)ifo, 0, 0);
	return 0;
}

int LuaSocketManager::Lua_Check_Upload(lua_State *L)
{
	lua_pushnumber(L, Upload_State);
	Upload_State = Upload_State == 1 ? 1 : 0;
	return 1;
}

int LuaSocketManager::Lua_Check_Upload_Error(lua_State *L)
{
	lua_pushnumber(L, Upload_Error);
	Upload_Error = 0;
	return 1;
}