#include "stinc.h"

bool EventManager::m_eventTypeSort[EVT_COUNT_TYPE][EVT_COUNT_SUBTYPE];
aVector<EventHandler *> EventManager::m_list;
EventHandler *EventManager::ProcessEvent(gzEventBase &evt)
{
	aList<EventNotifier> evtList;
	for (unsigned int i = 0; i < EventManager::m_list.Count(); i++)
	{
		if (!EventManager::m_list[i]->IsDeletePending())
			EventManager::m_list[i]->GetEventList(evtList, evt.GetEventType());
	}

	for (aListNode<EventNotifier> *objList = evtList.GetHead(); objList != NULL; objList = objList->GetNext())
	{
		EventHandler *obj = objList->GetData()->GetOwner();

		if (obj == NULL)
			continue;

		switch (evt.GetEventType())
		{
			// Game events
			case EVT_GAME_LEVEL_LOADED:
				obj->Level_Loaded();
				break;

			case EVT_GAME_LEVEL_ENDED:
				obj->Level_Ended();
				break;

			case EVT_GAME_THINK:
				obj->Think();
				break;


			// Player events
			case EVT_PLAYER_JOIN:
				obj->Player_Joined(*(gzEventPlayerBase *)&evt);
				break;

			case EVT_PLAYER_LEFT:
				obj->Player_Left(*(gzEventPlayerBase *)&evt);
				break;

			case EVT_PLAYER_BANDWIDTH:
				obj->Player_BandwidthChange(*(gzEventPlayerBandwidth *)&evt);
				break;

			case EVT_PLAYER_SUICIDE:
				obj->Player_Suicide(*(gzEventPlayerBase *)&evt);
				break;

			case EVT_PLAYER_CHAT:
				obj->Player_Chat(*(gzEventPlayerChat *)&evt);
				break;

			case EVT_PLAYER_RADIO:
				obj->Player_Radio(*(gzEventPlayerRadio *)&evt);
				break;

			case EVT_PLAYER_TEAMCHANGE:
				obj->Player_TeamChange(*(gzEventPlayerBase *)&evt);
				break;

			case EVT_PLAYER_SERIALHASH:
				obj->Player_SerialHash(*(gzEventPlayerSerialHash *)&evt);
				break;

			case EVT_PLAYER_RAWDAMAGE:
				obj->Player_RawDamage(*(gzEventObjectDamage *)&evt);
				break;

			case EVT_PLAYER_PURCHASE:
				obj->Player_Purchase(*(gzEventPlayerPurchase *)&evt);
				break;


			// Object events
			case EVT_OBJECT_CREATE:
				obj->Object_Created(*(gzEventObjectCreate *)&evt);
				break;

			case EVT_OBJECT_INIT:
				obj->Object_Init(*(gzEventObjectInit *)&evt);
				break;

			case EVT_OBJECT_DAMAGE:
				obj->Object_Damaged(*(gzEventObjectDamage *)&evt);
				break;

			case EVT_OBJECT_KILL:
				obj->Object_Killed(*(gzEventObjectKill *)&evt);
				break;

			case EVT_OBJECT_SQUISH:
				obj->Object_SoldierSquished(*(gzEventObjectKill *)&evt);
				break;

			case EVT_OBJECT_TRANSITION:
				obj->Object_Transition(*(gzEventObjectTransition *)&evt);
				break;

			case EVT_OBJECT_JUMP_START:
				obj->Object_JumpStart(*(gzEventObjectJump *)&evt);
				break;

			case EVT_OBJECT_JUMP_COMPLETE:
				obj->Object_JumpComplete(*(gzEventObjectJump *)&evt);
				break;

			case EVT_OBJECT_C4_CREATE:
				obj->Object_C4Creation(*(gzEventObjectC4Creation *)&evt);
				break;

			case EVT_OBJECT_C4_DETONATE:
				obj->Object_C4Detonate(*(gzEventObjectC4Detonation *)&evt);
				break;

			case EVT_OBJECT_POWERUP_GRANT:
				obj->Object_PowerupGrant(*(gzEventObjectPowerupGrant *)&evt);
				break;

			case EVT_OBJECT_FIRE:
				obj->Object_Fire(*(gzEventObjectFire *)&evt);
				break;


			// Network events
			case EVT_NET_WOLPAGE:
				obj->Net_WolPage(*(gzEventNetWolPage *)&evt);
				break;

			case EVT_NET_SERIAL_AUTH:
				obj->Net_SerialAuth(*(gzEventNetSerialAuth *)&evt);
				break;


			// BHS.dll events
			case EVT_BHS_POKE:
				obj->Bhs_Poke(*(gzEventBhsPoke *)&evt);
				break;

			case EVT_BHS_VERSION:
				obj->Bhs_Version(*(gzEventBhsVersion *)&evt);
				break;
		}

		if (evt.Skippable() && !evt.CanContinue())
			return obj;
	}
	return NULL;
}


/******************/
/* Event notifier */
/******************/
EventNotifier::EventNotifier()
{
	this->m_priority = EVT_PRIORITY_DEFAULT;
	this->m_type     = 0;
	this->m_owner    = NULL;
}


/*****************/
/* Event handler */
/*****************/
EventHandler::EventHandler()
{
	EventManager::m_list.Add(this);
}
EventHandler::~EventHandler()
{
	for (unsigned int i = 0; i < this->m_eventList.Count(); i++)
		delete this->m_eventList[i];
	this->m_eventList.Clear();

	for (unsigned int i = 0; i < EventManager::m_list.Count(); i++)
	{
		if (EventManager::m_list[i] == this)
		{
			EventManager::m_list.Delete(i);
			break;
		}
	}
}
void EventHandler::RegisterEvent(int type, int priority)
{
	EventNotifier *notifier = new EventNotifier;
	notifier->SetEventType(type);
	notifier->SetPriority(priority);
	notifier->SetOwner(this);
	this->m_eventList.Add(notifier);

	// Priority is not default, event need to be sorted
	if (priority != EVT_PRIORITY_DEFAULT)
		EventManager::m_eventTypeSort[type / 4096 - 1][type % 4096] = true;
}
void EventHandler::GetEventList(aList<EventNotifier> &ref, int type)
{
	for (unsigned int i = 0; i < this->m_eventList.Count(); i++)
	{
		if (this->m_eventList[i]->GetEventType() == type)
		{
			if (EventManager::m_eventTypeSort[type / 4096 - 1][type % 4096]) // Need to sort
			{
				// Empty list
				if (ref.Count() == 0)
					ref.AddTail(this->m_eventList[i]);

				// List have items and the current event priority is higher than the top data already
				else if (ref.Count() > 0 && this->m_eventList[i]->GetPriority() > ref.GetHead()->GetData()->GetPriority())
					ref.AddHead(this->m_eventList[i]);

				else
				{
					for (aListNode<EventNotifier> *refData = ref.GetHead(); refData != NULL; refData = refData->GetNext())
					{
						if (refData->GetData()->GetPriority() < this->m_eventList[i]->GetPriority())
						{
							ref.InsertBefore(this->m_eventList[i], refData->GetData());
							goto nextEvent;
						}
					}
					ref.AddTail(this->m_eventList[i]);
				}
			}
			else
				ref.AddTail(this->m_eventList[i]);
		}
nextEvent:;
	}
}
