#include "rp.h"
#pragma hdrstop

#include "scorzone.h"
#include "..\munga\fileutil.h"
#include "..\munga\nttmgr.h"
#include "..\munga\app.h"
#include "..\munga\hostmgr.h"
#include "..\munga\notation.h"

//#############################################################################
//###############################    ScoreZone    #############################
//#############################################################################

//#############################################################################
// Shared Data Support
//

Derivation* ScoreZone::GetClassDerivations()
{
	static Derivation classDerivations(Entity::GetClassDerivations(), "ScoreZone");
	return &classDerivations;
}

ScoreZone::SharedData
	ScoreZone::DefaultData(
		ScoreZone::GetClassDerivations(),
		ScoreZone::MessageHandlers,
		ScoreZone::GetAttributeIndex(),
		ScoreZone::StateCount,
		(Entity::MakeHandler)ScoreZone::Make
	);
//#############################################################################
// Attribute Support
//

const ScoreZone::IndexEntry
	ScoreZone::AttributePointers[]=
{
	ATTRIBUTE_ENTRY(ScoreZone, ScoreZoneType, scoreZoneType),
	ATTRIBUTE_ENTRY(ScoreZone, PointValue, pointValue)
};

ScoreZone::AttributeIndexSet& ScoreZone::GetAttributeIndex()
{
	static ScoreZone::AttributeIndexSet attributeIndex(ELEMENTS(ScoreZone::AttributePointers),
		ScoreZone::AttributePointers,
		Entity::GetAttributeIndex()
	);
	return attributeIndex;
}

//#############################################################################
// Message Support
//
const Receiver::HandlerEntry
	ScoreZone::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(ScoreZone, ProcessScoreZoneRequest)
};

Receiver::MessageHandlerSet
	ScoreZone::MessageHandlers(
		ELEMENTS(ScoreZone::MessageHandlerEntries),
		ScoreZone::MessageHandlerEntries,
		Entity::GetMessageHandlers()
	);

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

void
	ScoreZone::ProcessScoreZoneRequestMessageHandler(
		ProcessScoreZoneRequestMessage	*message
	)
{
	 Check(this);
	 Check_Pointer(message);

	 //
	 //----------------------------------------
	 // Make sure that the score zone is active
	 //----------------------------------------
	 //
	 EntityGroup *score_zone_group;
	 if (GetSimulationState() == Active)
	 {
		//
		//-----------------------------------------------------------
		//	 If Sequential, hunt for ourselves in the sequential group
		//-----------------------------------------------------------
		//
		Check(application);
		EntityManager *entity_mgr = application->GetEntityManager();
		Check(entity_mgr);
		if (
			scoreZoneType == LocalSequential
			 || scoreZoneType == GlobalSequential
		)
		{
			int new_zone = sequenceNumber + 1;
			score_zone_group = entity_mgr->FindGroup("SequentialScoreZones");
			Check(score_zone_group);
			ChainIteratorOf<Node*> iterator(score_zone_group->groupMembers);
			ScoreZone
				*score_zone,
				*first_score_zone = NULL;

			//
			//------------------------------------------------------------------
			// Store the first score zone in case we run off the end of the list
			//------------------------------------------------------------------
			//
			while ((score_zone = (ScoreZone*)iterator.ReadAndNext()) != NULL)
			{
				Check(score_zone);
				if (score_zone->sequenceNumber == 1)
				{
					first_score_zone = score_zone;
				}
				if (score_zone->sequenceNumber == new_zone)
				{
					break;
				}
			}
			if (!score_zone)
			{
				score_zone = first_score_zone;
			}

			if (score_zone->GetSimulationState() == InActive)
			{
				score_zone->SetSimulationState(Active);
				score_zone_group = entity_mgr->FindGroup("ActiveScoreZones");
				Check(score_zone_group);
				score_zone_group->Add(score_zone);
			}
		}

		//
		//----------------------------------------------------------------
		// Set the zone as inactive, remove ourselves from the active list
		//----------------------------------------------------------------
		//
		SetSimulationState(InActive);
		score_zone_group = entity_mgr->FindGroup("ActiveScoreZones");
		Check(score_zone_group);
		ChainIteratorOf<Node*> iterator(score_zone_group->groupMembers);
		ScoreZone *score_zone;
		while ((score_zone = (ScoreZone*)iterator.GetCurrent()) != NULL)
		{
			if (score_zone == this)
			{
				iterator.Remove();
				break;
			}
			iterator.Next();
		}

		//
		//-----------------------------------
		// Send the points back to the player
		//-----------------------------------
		//
		ReplyMessage
			reply(
				message->replyMessageID,
				sizeof(ReplyMessage),
				pointValue
			);
		Check(application);
		HostManager *host = application->GetHostManager();
		Check(host);
		Entity *entity = host->GetEntityPointer(message->requestingEntity);
		Check(entity);
		entity->Dispatch(&reply);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ScoreZone::Dispatch(Receiver::Message *message)
{
	Check(this);
	Check(message);

	ProcessScoreZoneRequestMessage *score_zone_message =
		(ProcessScoreZoneRequestMessage*) message;
	if (score_zone_message->messageID == ProcessScoreZoneRequestMessageID)
	{
		if (!extentBox.Contains(score_zone_message->linearPosition))
		{
			Check_Fpu();
			return;
		}
	}
	Entity::Dispatch(score_zone_message);
	Check_Fpu();
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ScoreZone::ScoreZone(
	ScoreZone::MakeMessage *creation_message,
	ScoreZone::SharedData &virtual_data
):
	Entity(creation_message, virtual_data)
{
	Check_Pointer(this);
	Check_Pointer(creation_message);

	SetValidFlag();

	//-------------------------------------------------
	// Find the drop zone resource in the resource file
	//-------------------------------------------------
	ResourceFile
		*res_file = application->GetResourceFile();
	Check(res_file);
	ResourceDescription *res =
		res_file->SearchList(
			resourceID,
			ResourceDescription::GameModelResourceType
		);
	Check(res);
	res->Lock();

	//
	// Get ScoreZone Info
	//
	sequenceNumber = creation_message->sequenceNumber;

	ModelResource
		*score_res = (ModelResource*)res->resourceAddress;

	scoreZoneType = score_res->scoreZoneType;
	pointValue = score_res->pointValue;
	extentBox = score_res->extentBox;

	extentBox.minX  += localOrigin.linearPosition.x;
	extentBox.minY  += localOrigin.linearPosition.y;
	extentBox.minZ  += localOrigin.linearPosition.z;

	extentBox.maxX  += localOrigin.linearPosition.x;
	extentBox.maxY  += localOrigin.linearPosition.y;
	extentBox.maxZ  += localOrigin.linearPosition.z;

	SetSimulationState(InActive);

	res->Unlock();
	//---------------------------------------------------------------------
	// Make the sequential score zone list.  Only make the first sequential
	// score zone active
	//---------------------------------------------------------------------
	EntityManager *entity_manager = application->GetEntityManager();
	Check(entity_manager);
	EntityGroup *score_zones;

	if (scoreZoneType == LocalSequential || scoreZoneType == GlobalSequential)
	{
		score_zones = entity_manager->UseGroup("SequentialScoreZones");
		Check(score_zones);
		score_zones->Add(this);
		if (sequenceNumber == 1)
		{
			goto Active_Zone;
		}
	}

	//
	//------------------------------------------------
	// If it isn't a sequential, always make it active
	//------------------------------------------------
	//
	else
	{
Active_Zone:
		score_zones = entity_manager->UseGroup("ActiveScoreZones");
		Check(score_zones);
		SetSimulationState(Active);
		score_zones->Add(this);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ScoreZone*
	ScoreZone::Make(ScoreZone::MakeMessage *creation_message)
{
	return new ScoreZone(creation_message);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	ScoreZone::CreateMakeMessage(
		MakeMessage *creation_message,
		NotationFile *model_file,
		const ResourceDirectories *directories
	)
{
	Check(creation_message);
	Check(model_file);

	if (!Entity::CreateMakeMessage(creation_message, model_file, directories))
	{
		Check_Fpu();
		return False;
	}

	creation_message->classToCreate = RegisteredClass::ScoreZoneClassID;
	creation_message->messageLength = sizeof(*creation_message);
	creation_message->instanceFlags =
		IndependantInstance|DynamicFlag|MapFlag|TrappedFlag;

	char *p;
	creation_message->sequenceNumber = -1;
	if ((p = strtok(NULL, " ")) != NULL)
	{
		creation_message->sequenceNumber = atoi(p);
	}

	Check_Fpu();
	return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ScoreZone::~ScoreZone()
{
	Check(this);

	if (scoreZoneType == LocalSequential || scoreZoneType == GlobalSequential)
	{
		Check(application);
		EntityManager *entity_mgr = application->GetEntityManager();
		Check(entity_mgr);
		EntityGroup *score_zone_group =
			entity_mgr->FindGroup("SequentialScoreZones");
		if (score_zone_group)
		{
			ChainIteratorOf<Node*> iterator(score_zone_group->groupMembers);
			ScoreZone *score_zone = (ScoreZone*)iterator.GetCurrent();
			if (score_zone == this)
			{
				iterator.Remove();
				if (!iterator.GetCurrent())
				{
					entity_mgr->RemoveGroup(score_zone_group);
				}
			}
		}
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription::ResourceID
	ScoreZone::CreateModelResource(
		ResourceFile *resource_file,
		const char* model_name,
		NotationFile * model_file,
		const ResourceDirectories *directories,
		ModelResource *model
	)
{
	Check(resource_file);
	Check_Pointer(model_name);
	Check_Pointer(directories);

	//
	// If we were not provided a buffer to write the model data into, we must
	// create it ourselves.  Then make sure that the model stuff is read in
	//
	ModelResource *local_model = model;
	if (!local_model)
	{
		local_model = new ModelResource;
		Register_Pointer(local_model);
	}


	//
	// Get PointValue assigned to this ScoreZone
	//
	if(
	!model_file->GetEntry(
		"gamedata",
		"PointValue",
		&local_model->pointValue
		)
	)
	{
		std::cerr << model_name <<  "Missing PointValue" <<  std::endl;
Dump_And_Die:
		if (!model)
		{
			Unregister_Pointer(local_model);
			delete local_model;
		}
		Check_Fpu();
		return -1;
	}
	//
	//	Get The ScoreZone Type
	//
	const char* score_zone_type;
	if(
	!model_file->GetEntry(
		"gamedata",
		"ScoreZoneType",
		&score_zone_type
		)
	)
	{
		std::cerr << model_name <<  "Missing ScoreZoneType" <<  std::endl;
		goto Dump_And_Die;
	}
	else
	{
		if(strcmp(score_zone_type,"GlobalOneTime") == 0)
		{
			local_model->scoreZoneType = GlobalOneTime;
		}
		else
		if(strcmp(score_zone_type,"LocalOneTime") == 0)
		{
			 local_model->scoreZoneType = LocalOneTime;
		}
		else
		if(strcmp(score_zone_type,"GlobalSequential") == 0)
		{
			 local_model->scoreZoneType = GlobalSequential;
		}
		else
		if(strcmp(score_zone_type,"LocalSequential") == 0)
		{
			local_model->scoreZoneType = LocalSequential;
		}
	}
	//
	// Read In the Extent Box from the solid file determined by the
	// collision data
	//
	const char* sld_entry;
	if(
	!model_file->GetEntry(
		"gamedata",
		"Extents",
		&sld_entry
		)
	)
	{
		std::cerr << model_name <<  "Missing .sld Specifications " << std::endl;
		goto Dump_And_Die;
	}
	else
	{
		char *sld_filename =
		MakePathedFilename(directories->collisionDirectory, sld_entry);
		Register_Pointer(sld_filename);

		NotationFile *sld_file = new NotationFile(sld_filename);
		Register_Object(sld_file);
		if(sld_file->PageCount() == 0)
		{
			std::cerr <<sld_filename<<" is has no Pages"<<std::endl;
         Unregister_Pointer(sld_filename);
			delete sld_filename;
			Unregister_Object(sld_file);
			delete sld_file;
			goto Dump_And_Die;
		}

		NameList *sld_namelist = sld_file->MakeEntryList("volume 0","extm");
		Register_Object(sld_namelist);
		if (sld_namelist->EntryCount() == 0)
		{
Dump_And_Die_2:
			Unregister_Pointer(sld_filename);
			delete sld_filename;
			Unregister_Object(sld_file);
			delete sld_file;
			Unregister_Object(sld_namelist);
			delete sld_namelist;
			goto Dump_And_Die;
		}
		NameList::Entry *extent_entry = sld_namelist->GetFirstEntry();
		while(extent_entry)
		{
			Scalar value = extent_entry->GetAtof();
			if (strcmp(extent_entry->GetName(),"extminX") == 0)
			{
				  local_model->extentBox.minX = value;
			}
			else
			if (strcmp(extent_entry->GetName(),"extminY") == 0)
			{
				  local_model->extentBox.minY = value;
			}
			else
			if (strcmp(extent_entry->GetName(),"extminZ") == 0)
			{
				  local_model->extentBox.minZ = value;
			}
			else
			if (strcmp(extent_entry->GetName(),"extmaxX") == 0)
			{
				  local_model->extentBox.maxX = value;
			}
			else
			if (strcmp(extent_entry->GetName(),"extmaxY") == 0)
			{
				  local_model->extentBox.maxY = value;
			}
			else
			if (strcmp(extent_entry->GetName(),"extmaxZ") == 0)
			{
				  local_model->extentBox.maxZ = value;
			}
			else
			{
				  std::cerr << sld_filename <<  "Improper Extent Format" <<  std::endl;
				  goto Dump_And_Die_2;
			}
			extent_entry = extent_entry->GetNextEntry();
		}
		Unregister_Pointer(sld_filename);
		delete sld_filename;
		Unregister_Object(sld_file);
		delete sld_file;
		Unregister_Object(sld_namelist);
		delete sld_namelist;
	}
	//
	//---------------------------
	// Write out the new resource
	//---------------------------
	//
   if (!model)
	{
		ResourceDescription *new_res =
			resource_file->AddResource(
				model_name,
				ResourceDescription::GameModelResourceType,
				1,
				ResourceDescription::Preload,
				local_model,
				sizeof(*local_model)
			);
		Unregister_Pointer(local_model);
		delete local_model;
		Check(new_res);
		Check_Fpu();
		return new_res->resourceID;
	}
	else
	{
		Check_Fpu();
		return 0;
	}
}

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