#include "stdafx.h"

int g_hMLuaLangpack;
CMPlugin g_plugin;

PLUGININFOEX pluginInfoEx =
{
	sizeof(PLUGININFOEX),
	__PLUGIN_NAME,
	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
	__DESCRIPTION,
	__AUTHOR,
	__COPYRIGHT,
	__AUTHORWEB,
	UNICODE_AWARE,
	// {27d41d81-991f-4dc6-8749-b0321c87e694}
	{ 0x27d41d81, 0x991f, 0x4dc6,{ 0x87, 0x49, 0xb0, 0x32, 0x1c, 0x87, 0xe6, 0x94 } }
};

static int ScriptsCompare(const CMLuaScript* p1, const CMLuaScript* p2)
{
	return mir_wstrcmpi(p1->GetName(), p2->GetName());
}

CMPlugin::CMPlugin()
	: PLUGIN(MODULENAME, pluginInfoEx),
	L(nullptr),
	m_scripts(1, ScriptsCompare)
{
	MUUID muidLast = MIID_LAST;
	g_hMLuaLangpack = GetPluginLangId(muidLast, 0);

	RegisterProtocol(PROTOTYPE_FILTER);

	CreatePluginService(MS_LUA_CALL, &CMPlugin::Call);
	CreatePluginService(MS_LUA_EXEC, &CMPlugin::Exec);
	CreatePluginService(MS_LUA_EVAL, &CMPlugin::Eval);
}

void CMPlugin::LoadLuaScripts()
{
	CMLuaScriptLoader::Load(L, m_scripts);
}

void CMPlugin::UnloadLuaScripts()
{
	for (auto &script : m_scripts.rev_iter()) {
		script->Unload();
		delete m_scripts.removeItem(&script);
	}
}

void CMPlugin::LoadLua()
{
	Log("Loading lua engine");
	L = luaL_newstate();
	Log("Loading standard modules");
	luaL_openlibs(L);

	lua_atpanic(L, luaM_atpanic);

	CMLuaFunctionLoader::Load(L);
	CMLuaModuleLoader::Load(L);
	CMLuaVariablesLoader::Load(L);
}

void CMPlugin::UnloadLua()
{
	UnloadLuaScripts();

	if (L != nullptr) {
		KillObjectEventHooks(L);
		KillObjectServices(L);

		Log("Unloading lua engine");

		lua_close(L);
	}

	KillModuleIcons(this);
	KillModuleSounds(this);
	KillModuleMenus(this);
	KillModuleHotkeys(this);
}

void CMPlugin::ReloadLuaScripts()
{
	UnloadLuaScripts();
	LoadLuaScripts();
}

/***********************************************/

int CMPlugin::OnOptionsInit(WPARAM wParam, LPARAM)
{
	OPTIONSDIALOGPAGE odp = {};
	odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE | ODPF_DONTTRANSLATE;
	odp.szGroup.w = LPGENW("Services");
	odp.szTitle.w = L"Lua";

	odp.szTab.w = LPGENW("Scripts");
	odp.pDialog = new CMLuaOptionsMain(*this);
	g_plugin.addOptions(wParam, &odp);

	odp.szTab.w = LPGENW("Evaluate");
	odp.pDialog = new CMLuaEvaluateOptions(*this);
	g_plugin.addOptions(wParam, &odp);

	return 0;
}

int CMPlugin::OnModulesLoaded(WPARAM, LPARAM)
{
	g_hCLibsFolder = FoldersRegisterCustomPathW(MODULENAME, "CLibsFolder", MIRLUA_PATHT, TranslateT("C libs folder"));
	g_hScriptsFolder = FoldersRegisterCustomPathW(MODULENAME, "ScriptsFolder", MIRLUA_PATHT, TranslateT("Scripts folder"));

	LoadLuaScripts();

	HookPluginEvent(ME_OPT_INITIALISE, &CMPlugin::OnOptionsInit);

	return 0;
}

int CMPlugin::Load()
{
	LoadIcons();
	LoadNetlib();
	LoadLua();

	HookPluginEvent(ME_SYSTEM_MODULESLOADED, &CMPlugin::OnModulesLoaded);

	return 0;
}

int CMPlugin::Unload()
{
	UnloadLua();
	UnloadNetlib();
	return 0;
}

/***********************************************/

static int mlua_call(lua_State *L)
{
	const char *module = luaL_checkstring(L, -3);
	const char *function = luaL_checkstring(L, -2);

	if (module && module[0]) {
		lua_getglobal(L, "require");
		lua_pushstring(L, module);
		lua_pcall(L, 1, 1, 0);

		lua_getfield(L, -1, function);
		lua_replace(L, -2);
	}
	else
		lua_getglobal(L, function);

	lua_pcall(L, 0, 1, 0);

	return 1;
}

INT_PTR CMPlugin::Call(WPARAM wParam, LPARAM lParam)
{
	const wchar_t *module = (const wchar_t*)wParam;
	const wchar_t *function = (const wchar_t*)lParam;

	lua_pushstring(L, T2Utf(module));
	lua_pushstring(L, T2Utf(function));

	lua_pushcfunction(L, mlua_call);

	CMLuaEnvironment env(L);
	env.Call();
	wchar_t *result = mir_utf8decodeW(lua_tostring(L, -1));
	lua_pop(L, 1);
	env.Unload();

	return (INT_PTR)result;
}

INT_PTR CMPlugin::Eval(WPARAM, LPARAM lParam)
{
	const wchar_t *script = (const wchar_t*)lParam;

	CMLuaEnvironment env(L);
	env.Eval(script);
	wchar_t *result = mir_utf8decodeW(lua_tostring(L, -1));
	lua_pop(L, 1);
	env.Unload();

	return (INT_PTR)result;
}

INT_PTR CMPlugin::Exec(WPARAM, LPARAM lParam)
{
	const wchar_t *path = (const wchar_t*)lParam;

	CMLuaEnvironment env(L);
	env.Exec(path);
	wchar_t *result = mir_utf8decodeW(lua_tostring(L, -1));
	lua_pop(L, 1);
	env.Unload();

	return (INT_PTR)result;
}