#include "munga.h"
#pragma hdrstop

#include "nttmgr.h"
#include "namelist.h"
#include "app.h"
#include "entity.h"
#include "hostmgr.h"
#include "dropzone.h"
#include "registry.h"

//#############################################################################
//############################    EntityGroup    ##############################
//#############################################################################


//#############################################################################
// Construction and Destruction
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityGroup::EntityGroup():
	groupMembers(NULL)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityGroup::~EntityGroup()
{
}

//#############################################################################
//###########################    EntityManager    #############################
//#############################################################################

Derivation* EntityManager::GetClassDerivations()
{
	static Derivation classDerivations(NetworkClient::GetClassDerivations(), "EntityManager");
	return &classDerivations;
}

EntityManager::SharedData
	EntityManager::DefaultData(
		EntityManager::GetClassDerivations(),
		EntityManager::GetMessageHandlers()
	);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityManager::EntityManager():
	NetworkClient(
		EntityManager::EntityManagerClassID,
		DefaultData,
		NetworkClient::EntityManagerClientID
	)
{
	deathRow = UseGroup("DeathRow");
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityManager::~EntityManager()
{
	//
	//----------------------------------------------
	// Erase all the groups listed in the group list
	//----------------------------------------------
	//
	ObjectNameList::Entry *entry;
	while ((entry = groupList.GetFirstEntry()) != NULL)
	{
		Check(entry);
		EntityGroup *group = (EntityGroup*)entry->GetData();
		groupList.DeleteEntry(entry->GetName());
		Unregister_Object(group);
		delete group;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::FryDeathRow()
{
	Check(this);
	Check(deathRow);

	//
	// ECH 8/23/95 -  Just delete one ... this allows the application to
	// delete as many entities as it can within the current frame
	//
	ChainIteratorOf<Node*> iterator(deathRow->groupMembers);
	Node *node;

	if ((node = iterator.GetCurrent()) != NULL)
	{
		Unregister_Object(node);
		delete node;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::ReceiveNetworkPacket(
		NetworkPacket*,
		Receiver::Message *packet_message
	)
{
	Check(this);
	Check_Pointer(packet_message);

	//
	//-------------------------------------------------------------------------
	// Check to see if the interest zone contained in the message is one we are
	// interested in, and if not, ignore the message
	//-------------------------------------------------------------------------
	//
	Entity::MakeMessage
		*message = Cast_Object(Entity::MakeMessage*, packet_message);

	//
	//-----------------------------------------------------------------------
	// Find the address of the entity.  If it exists, post the message on the
	// application event queue.
	//-----------------------------------------------------------------------
	//
	Entity
		*entity =
			application->GetHostManager()->GetEntityPointer(message->entityID);
	if (entity)
	{
		//
		// HACK -- This hack puts dropzone messages on max priority.  It should
		// really be done with a message priority encoded in the message
		//
		if (
			entity->GetClassID() == DropZoneClassID
			 && message->messageID == DropZone::AssignDropZoneMessageID
		)
		{
			application->Post(MaxEventPriority, entity, message);
		}
		else
		{
			application->Post(EntityManagerEventPriority, entity, message);
		}
	}

	//
	//--------------------------------------------------------------------------
	// If the entity does not exist, check to see if this is a manager message,
	// or see if we have to ask for a remake
	//--------------------------------------------------------------------------
	//
	else
	{
		switch (message->messageID)
		{
		case Entity::MakeMessageID:
			{
			#if DEBUG_LEVEL>0
				Entity *entity = application->GetRegistry()->MakeEntity(message);
				Register_Object(entity);
			#else
				application->GetRegistry()->MakeEntity(message);
			#endif
			}
			break;

		//
		//---------------------------------------------------------------------
		// Send a remake message to the sending host to have our entity rebuilt
		//---------------------------------------------------------------------
		//
		default:
			break;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityGroup*
	EntityManager::UseGroup(const char *name)
{
	EntityGroup *group = FindGroup(name);
	if (!group)
	{
		group = new EntityGroup;
		group->groupName = groupList.AddEntry(name, group);
		Register_Object(group);
	}
	return group;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityManager::RemoveGroup(EntityGroup *group)
{
	Check(group);
	groupList.DeleteEntry(group->groupName);
	Unregister_Object(group);
	delete group;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	EntityManager::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}
