//===========================================================================//
// File:	event.cc                                                            //
// Project: MUNGA           Brick: Event Manager                             //
// Contents: implementation details of event processing                      //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 10/19/94  JMA  Initial coding.                                            //
// 11/03/94  ECH  Made compatible with BC4.0                                 //
// 11/05/94  JMA  Made compatible with GNU C++                               //
// 11/30/94  JMA  Adapted to new style conventions                           //
// 12/01/94  JMA  Made compatible with SGI CC                                //
//---------------------------------------------------------------------------//
// Copyright (C) 1994, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//

#include "AdeptHeaders.hpp"

#include "EventStatistics.hpp"
#include "Network.hpp"

//#############################################################################
//###########################    AbstractEvent    #############################
//#############################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AbstractEvent::AbstractEvent(
	ClassData *class_data,
	const Receiver::Message *message,
	Time time
):
	Plug(class_data)
{
	Verify(message);
	alarmTime = time;

	//
	//--------------------------------------------------------
	// Allocate some memory to store the message parameters in
	//--------------------------------------------------------
	//
	size_t long_size = (message->messageLength+3)>>2;
	messageToSend = Cast_Pointer(Receiver::Message*, new long[long_size]);
	Register_Object(messageToSend);

	Mem_Copy(
		messageToSend,
		message,
		message->messageLength,
		long_size*sizeof(long)
	);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AbstractEvent::~AbstractEvent()
{
	if (messageToSend)
	{
		Unregister_Pointer(messageToSend);
		delete messageToSend;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	AbstractEvent::Process()
{
	STOP(("AbstractEvent::Process should not be called!\n"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	AbstractEvent::DumpData()
{
	STOP(("AbstractEvent::DumpData should not be called!\n"));
}

//#############################################################################
//###############################    Event    #################################
//#############################################################################

MemoryBlock*
	Event::AllocatedMemory = NULL;
Event::ClassData*
	Event::DefaultData = NULL;

static DWORD
	Event_Count;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Event::InitializeClass(
		size_t block_count,
		size_t block_delta
	)
{
	Verify(!AllocatedMemory);
	AllocatedMemory =
		new MemoryBlock(
			sizeof(Event),
			block_count,
			block_delta,
			"Event",
			g_LibraryHeap
		);
	Register_Object(AllocatedMemory);

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			EventClassID,
			"Adept::Event",
			AbstractEvent::DefaultData
		);
	Register_Object(DefaultData);

	Event_Count = 0;
#if !defined(NO_STATS)
	AddStatistic("Queued Events", "events", gos_DWORD, &Event_Count, 0);
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Event::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	Unregister_Object(AllocatedMemory);
	delete AllocatedMemory;
	AllocatedMemory = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Event::Event(
	Receiver *target,
	const Receiver::Message *message,
	Time time
):
	AbstractEvent(DefaultData, message, time)
{
	targetReceiver.Add(target);
	Set_Statistic(Event_Count, Event_Count+1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Event::~Event()
{
	Set_Statistic(Event_Count, Event_Count-1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Event::Repost(
		EventQueue* queue,
		Time new_time
	)
{
	Check_Object(this);
	Check_Object(queue);

	//
	//-------------------------------------------
	// Delete the link to the current event queue
	//-------------------------------------------
	//
	STOP(("Not functional"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Event::Process()
{
	Check_Object(this);
	Receiver *target = targetReceiver.GetCurrent();
	if (target)
	{
		Check_Object(target);
		target->Receive(this);
	}
	else
	{
		delete this;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Event::DumpData()
{
	Check_Object(this);
	#if defined(_ARMOR)
		Receiver *target = targetReceiver.GetCurrent();
		if (target)
		{
			Check_Object(target);
			Receiver::ClassData *class_data = target->GetClassData();
			Check_Object(class_data);
			const Receiver::MessageEntry *entry =
				target->GetMessageEntry(messageToSend->messageID);
			Check_Object(entry);
			Check_Pointer(entry->messageName);
			SPEW((
				GROUP_ADEPT_EVENT,
				"%s::%s",
				class_data->GetClassName(),
				entry->messageName
			));
		}
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Event::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

//#############################################################################
//###########################    NetworkEvent    ##############################
//#############################################################################

NetworkEvent::ClassData*
	NetworkEvent::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NetworkEvent::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			NetworkEventClassID,
			"Adept::NetworkEvent",
			AbstractEvent::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NetworkEvent::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NetworkEvent::NetworkEvent(
	Connection *connection,
	InBox *box,
	EventStyle event_style,
	const Receiver::Message *message,
	Time time
):
	AbstractEvent(DefaultData, message, time)
{
	eventConnection = connection;
	eventBox = box;
	eventStyle = event_style;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NetworkEvent::~NetworkEvent()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NetworkEvent::Process()
{
	Check_Object(this);
	switch (eventStyle)
	{
	case SendStyle:
		Check_Object(Network::GetInstance());
		STOP(("Not implemented"));
//		Network::GetInstance()->Send(messageToSend, hostID);
		break;

	case BroadcastStyle:
		Check_Object(Network::GetInstance());
		Network::GetInstance()->Broadcast(messageToSend);
		break;

	case ExclusiveBroadcastStyle:
		Check_Object(Network::GetInstance());
		Network::GetInstance()->ExclusiveBroadcast(messageToSend);
		break;
	}

	Unregister_Object(this);
	delete this;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	NetworkEvent::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

//#############################################################################
//#######################    GeneralEventQueue    ############################
//#############################################################################

//
// Shared Data Support
//
GeneralEventQueue::ClassData*
	GeneralEventQueue::DefaultData = NULL;

GeneralEventQueue*
	GeneralEventQueue::Instance = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void GeneralEventQueue::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			GeneralEventQueueClassID,
			"Adept::GeneralEventQueue",
			Plug::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void GeneralEventQueue::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GeneralEventQueue::GeneralEventQueue(ClassData *class_data):
	Plug(class_data)
{
	priorityLevels = -1;
	eventCount = 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GeneralEventQueue::~GeneralEventQueue()
{
	pendingEvents.DeletePlugs();
	timedEvents.DeletePlugs();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GeneralEventQueue*
	GeneralEventQueue::Make(
		int priorities,
		const char* count_trace_name,
		const char* delay_trace_name
	)
{
	gos_PushCurrentHeap(g_Heap);
	GeneralEventQueue *queue = new GeneralEventQueue[priorities];
	Register_Object(queue);

	queue->priorityLevels = priorities;
	gos_PopCurrentHeap();
	return queue;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AbstractEvent*
	GeneralEventQueue::Post(
		Receiver *target,
		const Receiver::Message *message,
		Time time
	)
{
	Check_Object(this);
	Check_Object(target);
	Check_Object(message);

	//
	//------------------------------------------------------
	// Create a new event, then insert it in the event queue
	//------------------------------------------------------
	//
	Event *event = new Event(target, message, time);
	Register_Object(event);

	Enqueue(event);
	return event;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AbstractEvent*
	GeneralEventQueue::SendEvent(
		Connection *connection,
		InBox *box,
		const Receiver::Message *message,
		Time when
	)
{
	Check_Object(this);
	Check_Object(message);

	NetworkEvent *event =
		new NetworkEvent(
			connection,
			box,
			NetworkEvent::SendStyle,
			message,
			when
		);
	Register_Object(event);
	Enqueue(event);
	return event;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AbstractEvent*
	GeneralEventQueue::BroadcastEvent(
		InBox *box,
		const Receiver::Message *message,
		Time when
	)
{
	Check_Object(this);
	Check_Object(message);

	//
	// HACK - What is a null network address?
	//
	NetworkEvent *event =
		new NetworkEvent(
			NULL,
			box,
			NetworkEvent::BroadcastStyle,
			message,
			when
		);
	Register_Object(event);
	Enqueue(event);
	return event;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AbstractEvent*
	GeneralEventQueue::ExclusiveBroadcastEvent(
		InBox *box,
		const Receiver::Message *message,
		Time when
	)
{
	Check_Object(this);
	Check_Object(message);

	//
	// HACK - What is a null network address?
	//
	NetworkEvent *event =
		new NetworkEvent(
			NULL,
			box,
			NetworkEvent::ExclusiveBroadcastStyle,
			message,
			when
		);
	Register_Object(event);
	Enqueue(event);
	return event;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GeneralEventQueue::Enqueue(AbstractEvent *event)
{
	Check_Object(event);
	Check_Pointer(event->messageToSend);
	int priority = event->messageToSend->messagePriority;
	Verify(static_cast<unsigned>(priority) < priorityLevels);

	//
	//------------------------------------------------------------
	// If this is not a timed event, stuff it in the pending queue
	//------------------------------------------------------------
	//
	if (event->alarmTime == -1.0)
	{
		this[priority].pendingEvents.Add(event);
	}

	//
	//----------------------------------------
	// Otherwise, sort it into the timed queue
	//----------------------------------------
	//
	else
	{
		ChainIteratorOf<AbstractEvent*>
			timed_events(&this[priority].timedEvents);
		timed_events.Last();
		AbstractEvent* next_event = timed_events.GetCurrent();

		//
		//---------------------------------------------------------------------
		// If the timed queue is empty, or if we are later than the last entry,
		// just add the event to the end
		//---------------------------------------------------------------------
		//
		if (!next_event || next_event->alarmTime <= event->alarmTime)
		{
			this[priority].timedEvents.Add(event);
		}

		//
		//--------------------------------------------------------------------
		// Find the event to insert before.  If we run off the beginning, stop
		// checking and set the iterator to the first event in the queue
		//--------------------------------------------------------------------
		//
		else
		{
			do
			{
				timed_events.Previous();
				next_event = timed_events.GetCurrent();
				if (!next_event)
				{
					timed_events.First();
					break;
				}
			} while (next_event->alarmTime > event->alarmTime);

			//
			//--------------------------------------------------------------------
			// If we didn't run off the queue, we need to insert after the current
			// event, so bump the iterator forward one event
			//--------------------------------------------------------------------
			//
			if (next_event)
			{
				timed_events.Next();
			}

			//
			//------------------------------------------------------------------
			// Insert the new event ahead of where the iterator currently points
			//------------------------------------------------------------------
			//
			timed_events.Insert(event);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AbstractEvent*
	GeneralEventQueue::PeekAtNextEvent(int min_priority)
{
	Check_Object(this);

	//
	//--------------------------------------------------------------------------
	// Start at the top of the priority queue, searching backwards until we find
	// an available event or run off the minimum queue priority
	//--------------------------------------------------------------------------
	//
	Verify(min_priority >= 0);
	int priority = priorityLevels;
	while (--priority >= min_priority)
	{
		//
		//-------------------------------------------------------------------
		// See if the first event in the timed queue is ready to go.  If not,
		// none of the other timed events will be ready to go either
		//-------------------------------------------------------------------
		//
		ChainIteratorOf<AbstractEvent*>
			timed_events(&this[priority].timedEvents);
		AbstractEvent
			*event = timed_events.GetCurrent();
		if (event)
		{
			Check_Object(event);
			if (event->alarmTime <= gos_GetElapsedTime())
			{
				return event;
			}
		}

		//
		//------------------------------
		// Now look in the untimed queue
		//------------------------------
		//
		ChainIteratorOf<AbstractEvent*>
			events(&this[priority].pendingEvents);
		event = events.GetCurrent();
		if (event)
		{
			Check_Object(event);
			return event;
		}
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	GeneralEventQueue::IsPriorityEmpty(int priority)
{
	Check_Object(this);

	Verify(priority >= 0 && priority < priorityLevels);
	ChainIteratorOf<AbstractEvent*> i(&this[priority].pendingEvents);
	ChainIteratorOf<AbstractEvent*> j(&this[priority].timedEvents);
	return !i.GetCurrent() && !j.GetCurrent();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	GeneralEventQueue::ProcessOneEvent(int min_priority)
{
	Check_Object(this);
	Verify(min_priority >= 0);

	AbstractEvent *event=PeekAtNextEvent(min_priority);
	if (event)
	{
		event->Process();
		return true;
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GeneralEventQueue::ProcessAllEvents(int min_priority)
{
	Check_Object(this);
	Verify(min_priority >= 0);
	while (ProcessOneEvent(min_priority));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GeneralEventQueue::FlushAllEvents(int max_priority)
{
	Check_Object(this);
	Verify(max_priority < priorityLevels);

	//
	//-----------------------------------------
	// Make sure that max_priority is set right
	//-----------------------------------------
	//
	if (max_priority < 0)
	{
		max_priority = priorityLevels - 1;
	}

	//
	//---------------------------------------------------------------------
	// Step through each of the priority queues and delete any events found
	// within
	//---------------------------------------------------------------------
	//
	for (int priority=max_priority; priority>=0; --priority)
	{

		//
		//---------------------------------------
		// Delete the events from the timed queue
		//---------------------------------------
		//
		ChainIteratorOf<AbstractEvent*> i(&this[priority].timedEvents);
		AbstractEvent *event;
		while ((event = i.ReadAndNext()) != NULL)
		{
			Unregister_Object(event);
			delete event;
		}

		//
		//-----------------------------------------
		// Delete the events from the regular queue
		//-----------------------------------------
		//
		ChainIteratorOf<AbstractEvent*> j(&this[priority].pendingEvents);
		while ((event = j.ReadAndNext()) != NULL)
		{
			Unregister_Object(event);
			delete event;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GeneralEventQueue::FlushMatchingEvents(
		Receiver::MessageID target_message,
		int max_priority
	)
{
	Check_Object(this);
	Verify(max_priority < priorityLevels);

	//
	//-----------------------------------------
	// Make sure that max_priority is set right
	//-----------------------------------------
	//
	if (max_priority < 0)
	{
		max_priority = priorityLevels - 1;
	}

	//
	//---------------------------------------------------------------------
	// Step through each of the priority queues and delete any events found
	// within
	//---------------------------------------------------------------------
	//
	for (int p=max_priority; p>=0; --p)
	{
		//
		//-------------------------------------------
		// Delete events from the regular queue first
		//-------------------------------------------
		//
		ChainIteratorOf<AbstractEvent*> i(&this[p].pendingEvents);
		AbstractEvent *event;
		while ((event = i.ReadAndNext()) != NULL)
		{

			//
			//------------------------------
			// Make sure the message matches
			//------------------------------
			//
			if (
				target_message == Receiver::AnyMessageID
				 || target_message == event->messageToSend->messageID
			)
			{
				Unregister_Object(event);
				delete event;
			}
		}

		//
		//-----------------------------------
		// Delete events from the timed queue
		//-----------------------------------
		//
		ChainIteratorOf<AbstractEvent*> j(&this[p].timedEvents);
		while ((event = j.ReadAndNext()) != NULL)
		{

			//
			//------------------------------
			// Make sure the message matches
			//------------------------------
			//
			if (
				target_message == Receiver::AnyMessageID
				 || target_message == event->messageToSend->messageID
			)
			{
				Unregister_Object(event);
				delete event;
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GeneralEventQueue::DumpEventQueue()
{
	Check_Object(this);

	//
	//----------------------------------------------------------------------
	// Step through each of the priority queues and display any events found
	// within
	//----------------------------------------------------------------------
	//
	unsigned total_count = 0;
	for (int priority=priorityLevels - 1; priority>=0; --priority)
	{
		SPEW((GROUP_ADEPT_EVENT, "EventQueue priority %d", priority));
		SPEW((
			GROUP_ADEPT_EVENT,
			"--------------------------------------------------------------"
		));

		ChainIteratorOf<AbstractEvent*> i(&this[priority].pendingEvents);
		AbstractEvent *event;
		size_t count=0;
		while ((event = i.ReadAndNext()) != NULL)
		{
			++count;
			event->DumpData();
		}
		SPEW((
			GROUP_ADEPT_EVENT,
			"%d priority %d events",
			count,
			priority
		));
		count = 0;
		ChainIteratorOf<AbstractEvent*> j(&this[priority].timedEvents);
		while ((event = j.ReadAndNext()) != NULL)
		{
			++count;
			event->DumpData();
		}
		SPEW((
			GROUP_ADEPT_EVENT,
			"%d priority %d timed events",
			count,
			priority
		));
		total_count += count;

	}
	SPEW((
		GROUP_ADEPT_EVENT, "%d total events in queues", total_count));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GeneralEventQueue::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

//#############################################################################
//############################    EventQueue    ###############################
//#############################################################################

//
// Shared Data Support
//
EventQueue::ClassData*
	EventQueue::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EventQueue::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			EventQueueClassID,
			"Adept::EventQueue",
			GeneralEventQueue::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EventQueue::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EventQueue::EventQueue():
	GeneralEventQueue(DefaultData)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EventQueue::~EventQueue()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EventQueue*
	EventQueue::Make(
		int priorities,
		const char* trace_name,
		const char* delay_trace_name
	)
{
	gos_PushCurrentHeap(g_Heap);
	EventQueue *queue = new EventQueue[priorities];
	Register_Object(queue);

	queue->priorityLevels = priorities;
	gos_PopCurrentHeap();
	return queue;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EventQueue::ProcessMatchingEvents(
		Receiver *receiver,
		Receiver::MessageID message_ID
	)
{
	Check_Object(this);
	Check_Object(receiver);

	//
	//----------------------------------------------------------------
	// Step through each of the priority queues and process any events
	// found within
	//----------------------------------------------------------------
	//
	for (int p=priorityLevels-1; p>=0; --p)
	{
		Event *event;
		AbstractEvent *abstract_event;
		ChainIteratorOf<AbstractEvent*> i(&this[p].pendingEvents);
		while ((abstract_event = i.ReadAndNext()) != NULL)
		{
			Check_Object(abstract_event);
			event = Cast_Object(Event*, abstract_event);

			//
			//------------------------------
			// Make sure the message matches
			//------------------------------
			//
			if (
				(event->targetReceiver.GetCurrent() == receiver) &&
				(message_ID == Receiver::AnyMessageID ||
				 message_ID == event->messageToSend->messageID)
			)
			{
				event->Process();
			}
		}

		ChainIteratorOf<AbstractEvent*> j(&this[p].timedEvents);
		while ((abstract_event = j.ReadAndNext()) != NULL)
		{
			Check_Object(abstract_event);
			event = Cast_Object(Event*, abstract_event);

			//
			//------------------------------
			// Make sure the message matches
			//------------------------------
			//
			if (
				(event->targetReceiver.GetCurrent() == receiver) &&
				(message_ID == Receiver::AnyMessageID ||
				 message_ID == event->messageToSend->messageID)
			)
			{
				event->Process();
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EventQueue::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}
