/* Copyright (C) 2015 Graham Sanderson, All Rights Reserved */
#include "extdll.h"
#include "eiface.h"
#include "util.h"
#include "game.h"
#include "gslua.h"

#include "lua/lua.hpp"

#define MAX_LUA_LIB_FUNCS 200
#define MAX_LUA_LIB_GLOBALS 200

extern lua_State *g_L;
extern BOOL g_bLuaInitialized;

typedef enum gsLuaType
{
	LTYPE_INT,
	LTYPE_STRING,
	LTYPE_FLOAT,
	LTYPE_FUNC
} gsLuaType;

typedef union gsLuaGlobal
{
	int i;
	char const *str;
	float f;
	lua_CFunction fn;
} gsLuaGlobal;

typedef struct gsLuaRegGlobal
{
	const char *name;
	gsLuaGlobal value;
	gsLuaType type;
} gsLuaRegGlobal;

enum
{
	LF_PRINT_CONSOLE = 6
};

static gsLuaRegGlobal gsLib_GLOBALS[] = {
	{ "at_notice",		at_notice,		LTYPE_INT },
	{ "at_console",		at_console,		LTYPE_INT },
	{ "at_aiconsole",	at_aiconsole,	LTYPE_INT },
	{ "at_warning",		at_warning,		LTYPE_INT },
	{ "at_error",		at_error,		LTYPE_INT },
	{ "at_logged",		at_logged,		LTYPE_INT },
	{ "Print_Console",	0,				LTYPE_FUNC },
	{ NULL }
};

// --- Init/quit  ---

#define LuaError(...) (ALERT(at_error, __VA_ARGS__), g_bLuaInitialized = FALSE, LuaQuit(), g_L = NULL)

void LuaInit(void)
{
	gsLib_GLOBALS[LF_PRINT_CONSOLE].value.fn = LUA_PrintConsole;

	g_L = luaL_newstate();
	luaopen_base(g_L);
	luaopen_math(g_L);
	luaopen_string(g_L);

	g_bLuaInitialized = TRUE;
	ALERT(at_console, "[HLua] Lua initialized.\n");

	LuaRegisterGlobals();

	if(luaL_dostring(g_L, "Print_Console(at_console, \"[HLua::Lua] Initialization success!\");") == TRUE)
	{
		LuaError("[HLua] Failed initial Lua test. Stopping Lua.\n");
		return;
	}

	LuaParseScripts();
}

void LuaQuit(void)
{
	lua_close(g_L);
	ALERT(at_console, "[HLua] Lua shutdown.\n");
}

void LuaCheckNull(void const *vpToCheck)
{
	if(vpToCheck == NULL)
	{
		LuaError("[HLua] Null pointer exception!\n");
	}
}

// --- Misc functions ---
void LuaRunScript(char *szFileName)
{
	if(luaL_dofile(g_L, szFileName) == TRUE)
	{
		LuaError("[HLua] Failed to run script %s\nLua: %s\n", szFileName, lua_tostring(g_L, -1));
	}
}

void LuaParseScripts(void)
{
	FILE *fScripts;
	char szLn[1024];
	char szFilenamePulledOutOfMyAss[MAX_PATH];
	char szAlsoPulledOutOfMyAss[MAX_PATH];

	GET_GAME_DIR(szAlsoPulledOutOfMyAss);
	strcat(szAlsoPulledOutOfMyAss, "/lua");
	CreateDirectory(szAlsoPulledOutOfMyAss, NULL);
	strcpy(szFilenamePulledOutOfMyAss, szAlsoPulledOutOfMyAss);
	strcat(szFilenamePulledOutOfMyAss, "/scripts.txt");

	fScripts = fopen(szFilenamePulledOutOfMyAss, "r");

	if(fScripts == NULL)
	{
		LuaError("[HLua] Failed to open lua/scripts.txt!\n");
		return;
	}

	while(fgets(szLn, 1024, fScripts) != NULL)
	{
		BOOL bContinue = FALSE;
		if(szLn[0] == '\r' || szLn[0] == '\n') continue;

		char ch = szLn[0], pvch = szLn[0];
		for(int i = 1; ch != '\0'; pvch = ch, ch = szLn[i++])
		{
			if(i == 1) continue;

			if(pvch == '/' && ch == '/')
			{
				bContinue = TRUE;
				break;
			}

			if(ch == '\n' || ch == '\r')
			{
				szLn[i-1] = '\0';
				break;
			}
		}

		if(bContinue == TRUE) continue;

		char szSeriouslyIHaveALotOfThingsInMyAss[MAX_PATH];
		strcpy(szSeriouslyIHaveALotOfThingsInMyAss, szAlsoPulledOutOfMyAss);
		strcat(szSeriouslyIHaveALotOfThingsInMyAss, szLn);

		LuaRunScript(szSeriouslyIHaveALotOfThingsInMyAss);
	}
}

// --- Binded lua functions and globals ---

void LuaRegisterGlobals(void)
{
	int i;
	ALERT(at_console, "[HLua] Registering globals.\n");

	for(i = 0; i < MAX_LUA_LIB_GLOBALS; i++)
	{
		if(gsLib_GLOBALS[i].name == NULL) break;

		switch(gsLib_GLOBALS[i].type)
		{
		case LTYPE_INT: lua_pushnumber(g_L, gsLib_GLOBALS[i].value.i); break;
		case LTYPE_STRING: lua_pushstring(g_L, gsLib_GLOBALS[i].value.str); break;
		case LTYPE_FLOAT: lua_pushnumber(g_L, gsLib_GLOBALS[i].value.f); break;
		case LTYPE_FUNC: lua_pushcfunction(g_L, gsLib_GLOBALS[i].value.fn); break;
		default: LuaError("[HLua] Invalid type in global initializer!\n"); return;
		}

		lua_setglobal(g_L, gsLib_GLOBALS[i].name);
	}

	ALERT(at_console, "[HLua] Global registry success, put out %d globals.\n", i);
}

int LUA_PrintConsole(lua_State *L)
{
	const char *szToWrite = luaL_checkstring(L, 2);
	ALERT_TYPE eType = (ALERT_TYPE)luaL_checkinteger(L, 1);
	LuaCheckNull(szToWrite);
	ALERT(eType, "%s\n", szToWrite);
	return 0;
}
