// PluginNMDC
// ``````````

#include "../common/pluginAPI.h"
#include "../common/mUtil.h"
#include "nmdc.h"

static NMDCHub* hub;
static CTinyJS* js;
static std::string hubPass;

////////////////////////////
// Javascript interface (O)
////////////////////////////

void callJS(std::string& script) {
	if (! js) return;

	try {
		js->execute(script.c_str());
	} catch (CScriptException *ex) {
		// oh dear : (
	}
}

void callJS(const char* script) {
	std::string sScript(script);
	callJS(sScript);
}

void callJsPublic(std::string& user, std::string& message) {
	std::string _user(user), _message(message);
	replace(_user,    "\"", "\\\"");
	replace(_message, "\"", "\\\"");
	std::string _call =
		"for( _jsti=0; _jsti < _onPublic.length; _jsti++) _onPublic[_jsti](\""
		+ _user
		+ "\", \""
		+ _message
		+ "\");"
	;
	callJS(_call);
}

void callJsPrivate(std::string& user, std::string& message) {
	std::string _user(user), _message(message);
	replace(_user,    "\"", "\\\"");
	replace(_message, "\"", "\\\"");
	std::string _call =
		"for( _jsti=0; _jsti < _onPrivate.length; _jsti++) _onPrivate[_jsti](\""
		+ _user
		+ "\", \""
		+ _message
		+ "\");"
	;
	callJS(_call);
}

////////////////////////////
// Hub interface
////////////////////////////

void hub_debug(const char* msg) {
	std::string alert = "mudc_console(\"";
	alert += msg;
	alert += "\");";
	callJS(alert);
}

void hub_flushAllEvents() {
	if (! hub) return;

	for(;;) {
		NMDCEvent ev = hub->NextEvent();
		if (ev.type == NMDC_NONE) break;

		switch (ev.type) {
			case NMDC_PUBLIC_CHAT: {
				std::string alert = "<" + ev.data1 + "> " + ev.data2;
				hub_debug(alert.c_str());
				callJsPublic(ev.data1, ev.data2);
			} break;
			case NMDC_USER_JOIN: {
				std::string alert = "* Joins: " + ev.data1;
				hub_debug(alert.c_str());
			} break;
			case NMDC_USER_PART: {
				std::string alert = "* Parts: " + ev.data1;
				hub_debug(alert.c_str());
			} break;
			case NMDC_KICKED: {
				hub_debug("Disconnected from server.");
				hub->Disconnect();
			} break;
			case NMDC_NEED_PASS: {
				hub_debug("Username locked, sending password..");
				hub->Login(hubPass.c_str());
			} break;
			case NMDC_SYSTEM_MSG: {
				std::string system_message = "* " + ev.data2;
				hub_debug(system_message.c_str());
			} break;
			case NMDC_PRIVATE_MSG: {
				std::string alert = "<@@" + ev.data1 + "@@> " + ev.data2;
				hub_debug(alert.c_str());
				callJsPrivate(ev.data1, ev.data2);
			} break;
		}
	};
}

void hub_erase() {
	hubPass.assign("");
	if (hub) {
		hub->Disconnect();
		delete hub;
		hub = 0;
	}
}

////////////////////////////
// Javascript interface (I)
////////////////////////////

void js_Connect(CScriptVar *v, void* data) {
	hub_erase();
	hub_debug("Trying to open a new hub connection...");
	hub = new NMDCHub(
		v->getParameter("address")->getString().c_str(),
		v->getParameter("port")->getInt(),
		v->getParameter("nick")->getString().c_str(),
		v->getParameter("share")->getInt()
	);
	hubPass = v->getParameter("pass")->getString();
	hub_flushAllEvents();
}

void js_Disconnect(CScriptVar *v, void* data) {
	hub_debug("Disconnecting from hub.");
	hub_erase();
}

void js_SayPublic(CScriptVar *v, void* data) {
	if (! hub) return;
	hub->SayPublic(
		v->getParameter("message")->getString().c_str()
	);
}

void js_SayPrivate(CScriptVar *v, void* data) {
	if (! hub) return;
	hub->SayPrivate(
		v->getParameter("user")->getString().c_str(),
		v->getParameter("message")->getString().c_str()
	);
}

void js_IsConnected(CScriptVar *v, void* data) {
	if (! hub) {
		v->getReturnVar()->setInt(0);
	} else {
		v->getReturnVar()->setInt( hub->Connected() ? 1 : 0 );
	}
}

////////////////////////////
// Plugin interface
////////////////////////////

void plugin_onLoad(CTinyJS* js) {
	hub_erase();
	::js = js;

	js->addNative("function nmdc_connect(address, port, nick, pass, share)",
		js_Connect, 0);
	js->addNative("function nmdc_disconnect()",
		js_Disconnect, 0);
	js->addNative("function nmdc_say(message)",
		js_SayPublic, 0);
	js->addNative("function nmdc_pm(user, message)",
		js_SayPrivate, 0);
	js->addNative("function nmdc_connected()",
		js_IsConnected, 0);

	callJS("_onPublic = []; _onPrivate = [];");
}

void plugin_onTick(CTinyJS* js) {
	hub_flushAllEvents();
}

void plugin_onExit(CTinyJS* js) {
	hub_erase();
}