#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
//#include <unistd.h>
//#include <pthread.h>

#include "..\ActionEventEngine\monitor.h"
#include "..\ActionEventEngine\compat.h"
#include "Events.h"

struct thread_struct
{
	PRUNTIME pRuntime;
	void * pArgs;
};

struct mon_time_entry {
	unsigned int(__cdecl *callback)(void *);
	void *args;
	int flags;
	time_t time;
	int iter;
	int delay;
	struct mon_time_entry *next;
	struct mon_time_entry *prev;
};

unsigned int WINAPI mon_time_run(void *args);
static void mon_time_dump(PRUNTIME pRuntime);

static thread_t t;
static mutex_t m = PTHREAD_MUTEX_INITIALIZER;
static struct mon_time_entry *list = NULL;
static int running = 0;

unsigned int WINAPI mon_time_run(void *args)
{
	struct mon_time_entry *p, *e;
	time_t now;
	thread_t ct;

	PRUNTIME pRuntime = (PRUNTIME)args;

	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	while (1) {
		//MUTEX_LOCK(m);
		API_ENTERCRITICALSECTION(&pVariables->pTimerCriticalSection);

		if (!list) {
			running = 0;
			//MUTEX_UNLOCK(m);
			API_LEAVECRITICALSECTION(&pVariables->pTimerCriticalSection);
			return 0;
		}

		now = API_TIME(NULL);
		p = list;
		while (p) {
			if (p->time <= now) {
				//THREAD_CREATE(ct, p->callback, p->args);
				struct thread_struct *t = (struct thread_struct*)malloc(sizeof(struct thread_struct));
				t->pRuntime = pRuntime;
				t->pArgs = p->args;
				THREAD_CREATE(ct, p->callback, t);
				THREAD_DETACH(ct);
				if ((p->flags & MONITOR_FLAG_PERM) && (!p->iter || (p->iter-- > 1))) {
					p->time = now + p->delay;
					p = p->next;
				}
				else {
					if (p == list) list = p->next;
					if (p->next) p->next->prev = p->prev;
					if (p->prev) p->prev->next = p->next;
					e = p;
					p = p->next;
					API_FREE(pRuntime, e);
				}
			}
			else {
				p = p->next;
			}
		}

		//mon_time_dump(pRuntime);

		//MUTEX_UNLOCK(m);
		API_LEAVECRITICALSECTION(&pVariables->pTimerCriticalSection);

		sleep(1);
	}
}


void mon_time_start(PRUNTIME pRuntime, void *args)
{
	//MUTEX_LOCK(m);

	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);
	API_ENTERCRITICALSECTION(&pVariables->pTimerCriticalSection);

	if (!running)
		if (!THREAD_CREATE(t, mon_time_run, pRuntime)) running = 1;

	//MUTEX_UNLOCK(m);
	API_LEAVECRITICALSECTION(&pVariables->pTimerCriticalSection);
}

void* time_addwatch(PRUNTIME pRuntime, void* (*callback)(void *), void *args, int flags, ...)
{
	struct mon_time_entry *p = NULL;
	va_list va;

	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);

	API_ENTERCRITICALSECTION(&pVariables->pTimerCriticalSection);

	//if (m.SpinCount == 0)
	//	MUTEX_INIT(m);

	//MUTEX_LOCK(m);

	do {
		if (!list) {
			if (!(list = API_ALLOC(pRuntime, sizeof(struct mon_time_entry)))) break;
			list->prev = NULL;
			p = list;
		}
		else {
			for (p = list; p->next; p = p->next);
			if (!(p->next = API_ALLOC(pRuntime, sizeof(struct mon_time_entry)))) break;
			p->next->prev = p;
			p = p->next;
		}
		p->next = NULL;
		p->callback = callback;
		p->args = args;
		p->flags = flags;

		va_start(va, flags);
		p->time = va_arg(va, int);
		if ((p->delay = va_arg(va, int)) < 1) p->delay = 1;
		if ((p->iter = va_arg(va, int)) < 0) p->iter = 0;
		va_end(va);

		if (!running) if (!THREAD_CREATE(t, mon_time_run, pRuntime)) running = 1;
	} while (0);

	mon_time_dump(pRuntime);

	//MUTEX_UNLOCK(m);

	API_LEAVECRITICALSECTION(&pVariables->pTimerCriticalSection);

	return p;
}

void * time_delwatch(PRUNTIME pRuntime, void *entry)
{
	struct mon_time_entry *p = NULL, *e = (struct mon_time_entry *)entry;

	if (!list || !e) return NULL;

	//MUTEX_LOCK(m);
	PVARIABLES_EVENTS pVariables = (PVARIABLES_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpVariables);
	PSTRINGS_EVENTS pStrings = (PSTRINGS_EVENTS)(pRuntime->pModuleSpecificData[INDEX_EVENTS]->lpStrings);
	API_ENTERCRITICALSECTION(&pVariables->pTimerCriticalSection);

	for (p = list; p && (p != e); p = p->next);
	if (p)
	{
		if (p == list)
			list = p->next;

		if (p->next)
			p->next->prev = p->prev;

		if (p->prev)
			p->prev->next = p->next;

		//free(p); //bug on process end(?)
		API_FREE(pRuntime, p);
	}

	//MUTEX_UNLOCK(m);
	API_LEAVECRITICALSECTION(&pVariables->pTimerCriticalSection);

	return NULL;
}

void mon_time_dump(PRUNTIME pRuntime)
{

	struct mon_time_entry *p = NULL;

	OUTPUTDEBUG("\n--- TIME MONITOR LIST ---\n");
	for (p = list; p; p = p->next) 
		OUTPUTDEBUG("- %u %u (at %p)\n", (unsigned int)p->time, p->iter, (void *)p);
	OUTPUTDEBUG("\n");


	return;
}
