#include "WimpLib:EventSrc.h"

#include <string.h>
#include "WimpLib:Exception.h"
#include "WimpLib:List.h"
#include "WimpLib:Log.h"
#include "WimpLib:mem.h"
#include "WimpLib:Task.h"

typedef struct item_event_listener
{
	Listener_FOnEvent  pEventHandler;
	void*              pHandle;
} item_event_listener;

static void throw_EvDispatcher_EvDispatcher(EvDispatcher* This, int type)
{
	This->m_type = type;
	This->m_pHandlers = NULL;
	This->m_pHandlers = New_List();
}

static void EvDispatcher_NotEvDispatcher(EvDispatcher* This)
{
	item_event_listener* pitem;

	if (This->m_pHandlers)
	{
		while(List_Count(This->m_pHandlers))
		{
			pitem = List_Get(This->m_pHandlers, 0);
//			Log("Unreleased Listener %p\n", pitem->pEventHandler);
			List_Remove(This->m_pHandlers, 0);
			mem_free(pitem);
		}

		Delete_List(This->m_pHandlers);
		This->m_pHandlers = NULL;
	}
}

static void Delete_EvDispatcher(EvDispatcher* This)
{
	if (This)
	{
		EvDispatcher_NotEvDispatcher(This);
		mem_free(This);
	}
}

static EvDispatcher* throw_New_EvDispatcher(int type)
{
	EvDispatcher* This = throw_mem_alloc(sizeof(*This));

	try
	{
		throw_EvDispatcher_EvDispatcher(This, type);
	}
	catch
	{
		Delete_EvDispatcher(This);
		throw_current();
	}
	catch_end

	return This;
}

static void throw_EvDispatcher_AddListener(EvDispatcher* This, Listener_FOnEvent pEventHandler, void* pHandle, bool bFirst)
{
	item_event_listener* pitem = throw_mem_alloc(sizeof(item_event_listener));

	try
	{
		pitem->pEventHandler = pEventHandler;
		pitem->pHandle = pHandle;

		if (bFirst)
			List_InsertAfter(This->m_pHandlers, NULL, pitem);
		else
			List_InsertBefore(This->m_pHandlers, NULL, pitem);
	}
	catch
	{
		mem_free(pitem);
		throw_current();
	}
	catch_end
}

static bool EvDispatcher_RemoveListener(EvDispatcher* This, Listener_FOnEvent pEventHandler, void* pHandle)
{
	ListNode* pNode = NULL;
	item_event_listener* pitem;

	while ((pNode = List_GetSuccessor(This->m_pHandlers, pNode)) != NULL)
	{
		pitem = List_GetNodeData(This->m_pHandlers, pNode);

		if ((pitem->pEventHandler == pEventHandler)
		&&  (pitem->pHandle == pHandle))
		{
			List_RemoveNode(This->m_pHandlers, pNode);
			mem_free(pitem);

			return true;
		}
	}

	return false;
}

static bool EvDispatcher_FindListener(EvDispatcher* This, Listener_FOnEvent pEventHandler, void* pHandle)
{
	ListNode* pNode = NULL;
	item_event_listener* pitem;

	while ((pNode = List_GetSuccessor(This->m_pHandlers, pNode)) != NULL)
	{
		pitem = List_GetNodeData(This->m_pHandlers, pNode);

		if ((pitem->pEventHandler == pEventHandler)
		&&  (pitem->pHandle == pHandle))
			return true;
	}

	return false;
}

static bool EvDispatcher_Dispatch(EvDispatcher* This, const Event* e)
{
	ListNode* pNode = NULL;
	item_event_listener* pitem;

	if (e->Type != This->m_type) return false;

	while ((pNode = List_GetSuccessor(This->m_pHandlers, pNode)) != NULL)
	{
		pitem = List_GetNodeData(This->m_pHandlers, pNode);

		if (pitem->pEventHandler(pitem->pHandle, e))
			return true;
	}

	return false;
}

static void throw_EvDispatcher_AddGlobalListener(EvDispatcher* This, Listener_FOnEvent pEventHandler, void* pHandle, bool bFirst)
{
	item_event_listener* pitem = throw_mem_alloc(sizeof(item_event_listener));

	try
	{
		pitem->pEventHandler = pEventHandler;
		pitem->pHandle = pHandle;

		if (bFirst)
			List_InsertAfter(This->m_pHandlers, NULL, pitem);
		else
			List_InsertBefore(This->m_pHandlers, NULL, pitem);
	}
	catch
	{
		mem_free(pitem);
		throw_current();
	}
	catch_end
}

static bool EvDispatcher_RemoveGlobalListener(EvDispatcher* This, Listener_FOnEvent pEventHandler, void* pHandle)
{
	item_event_listener* pitem;
	ListNode* pNode = NULL;

	while ((pNode = List_GetSuccessor(This->m_pHandlers, pNode)) != NULL)
	{
		pitem = List_GetNodeData(This->m_pHandlers, pNode);

		if ((pitem->pEventHandler == pEventHandler)
		&&  (pitem->pHandle == pHandle))
		{
			List_RemoveNode(This->m_pHandlers, pNode);
			mem_free(pitem);

			return true;
		}
	}

	return false;
}

static bool EvDispatcher_FindGlobalListener(EvDispatcher* This, Listener_FOnEvent pEventHandler, void* pHandle)
{
	item_event_listener* pitem;
	ListNode* pNode = NULL;

	while ((pNode = List_GetSuccessor(This->m_pHandlers, pNode)) != NULL)
	{
		pitem = List_GetNodeData(This->m_pHandlers, pNode);

		if ((pitem->pEventHandler == pEventHandler)
		&&  (pitem->pHandle == pHandle))
			return true;
	}

	return false;
}

static bool EvDispatcher_GlobalDispatch(EvDispatcher* This, const Event* e)
{
	item_event_listener* pitem;
	ListNode* pNode = NULL;

	while ((pNode = List_GetSuccessor(This->m_pHandlers, pNode)) != NULL)
	{
		pitem = List_GetNodeData(This->m_pHandlers, pNode);

		if (pitem->pEventHandler(pitem->pHandle, e))
			return true;
	}

	return false;
}

// Constructor and destructor
void throw_EventSource_EventSource(EventSource* This)
{
	memset(This, 0, sizeof(*This));

	throw_EvDispatcher_EvDispatcher(&This->m_PreDispatcher, 0);
	This->m_pDispatchers = New_List();
	throw_EvDispatcher_EvDispatcher(&This->m_PostDispatcher, 0);
}

void EventSource_NotEventSource(EventSource* This)
{
	EvDispatcher* pitem;

	if (This->m_pDispatchers)
	{
		while(List_Count(This->m_pDispatchers))
		{
			pitem = List_Get(This->m_pDispatchers, 0);
			List_Remove(This->m_pDispatchers, 0);
			Delete_EvDispatcher(pitem);
		}

		Delete_List(This->m_pDispatchers);
		This->m_pDispatchers = NULL;
	}

	EvDispatcher_NotEvDispatcher(&This->m_PreDispatcher);
	EvDispatcher_NotEvDispatcher(&This->m_PostDispatcher);
}

static EvDispatcher* EventSource_FindEvDispatcher(EventSource* This, int type)
{
	EvDispatcher* pitem;
	ListNode* pNode = NULL;

	while ((pNode = List_GetSuccessor(This->m_pDispatchers, pNode)) != NULL)
	{
		pitem = List_GetNodeData(This->m_pDispatchers, pNode);

		if (pitem->m_type == type)
			return pitem;
	}

	return NULL;
}

bool EventSource_ProcessEvent(EventSource* This, const Event* e)
{
	EvDispatcher* pEvDispatcher;

	if (EvDispatcher_GlobalDispatch(&This->m_PreDispatcher, e))
		return true;

	if ((pEvDispatcher = EventSource_FindEvDispatcher(This, e->Type)) != NULL)
	{
		if (EvDispatcher_Dispatch(pEvDispatcher, e))
			return true;
	}

	return EvDispatcher_GlobalDispatch(&This->m_PostDispatcher, e);
}

bool EventSource_PreProcessEvent(EventSource* This, const Event* e)
{
	return EvDispatcher_GlobalDispatch(&This->m_PreDispatcher, e);
}

bool EventSource_PostProcessEvent(EventSource* This, const Event* e)
{
	EvDispatcher* pEvDispatcher;

	if ((pEvDispatcher = EventSource_FindEvDispatcher(This, e->Type)) != NULL)
	{
		if (EvDispatcher_Dispatch(pEvDispatcher, e))
			return true;
	}
	return EvDispatcher_GlobalDispatch(&This->m_PostDispatcher, e);
}

void throw_EventSource_AddGlobalListener(EventSource* This, Listener_FOnEvent pEventHandler, void* pHandle, bool bFirst)
{
	if (bFirst)
		throw_EvDispatcher_AddGlobalListener(&This->m_PreDispatcher, pEventHandler, pHandle, true);
	else
		throw_EvDispatcher_AddGlobalListener(&This->m_PostDispatcher, pEventHandler, pHandle, false);
}

void EventSource_RemoveGlobalListener(EventSource* This, Listener_FOnEvent pEventHandler, void* pHandle)
{
	if (EvDispatcher_RemoveGlobalListener(&This->m_PreDispatcher, pEventHandler, pHandle))
		return;

	EvDispatcher_RemoveGlobalListener(&This->m_PostDispatcher, pEventHandler, pHandle);
}

bool EventSource_FindGlobalListener(EventSource* This, Listener_FOnEvent pEventHandler, void* pHandle)
{
	if (EvDispatcher_FindGlobalListener(&This->m_PreDispatcher, pEventHandler, pHandle))
		return true;

	return EvDispatcher_FindGlobalListener(&This->m_PostDispatcher, pEventHandler, pHandle);
}

void throw_EventSource_AddListener(EventSource* This, int event, Listener_FOnEvent pEventHandler, void* pHandle, bool bFirst)
{
	EvDispatcher* volatile pEvDispatcher = EventSource_FindEvDispatcher(This, event);

	if (!pEvDispatcher)
	{
		pEvDispatcher = throw_New_EvDispatcher(event);

		try
		{
			List_InsertBefore(This->m_pDispatchers, NULL, pEvDispatcher);
		}
		catch
		{
			Delete_EvDispatcher(pEvDispatcher);
			throw_current();
		}
		catch_end
	}

	throw_EvDispatcher_AddListener(pEvDispatcher, pEventHandler, pHandle, bFirst);
}

void EventSource_RemoveListener(EventSource* This , int event, Listener_FOnEvent pEventHandler, void* pHandle)
{
	EvDispatcher* pEvDispatcher = EventSource_FindEvDispatcher(This, event);

	if (pEvDispatcher)
		EvDispatcher_RemoveListener(pEvDispatcher, pEventHandler, pHandle);
}

bool EventSource_FindListener(EventSource* This , int event, Listener_FOnEvent pEventHandler, void* pHandle)
{
	EvDispatcher* pEvDispatcher = EventSource_FindEvDispatcher(This, event);

	if (pEvDispatcher)
		return EvDispatcher_FindListener(pEvDispatcher, pEventHandler, pHandle);

	return false;
}
