#include "munga.h"
#pragma hdrstop

#include "director.h"
#include "mission.h"
#include "player.h"
#include "app.h"
#include "hostmgr.h"
#include "nttmgr.h"

//#############################################################################
// Shared Data support
//
Derivation* CameraDirector::GetClassDerivations()
{	static Derivation classDerivations(Player::GetClassDerivations(), "CameraDirector");
	return &classDerivations;
}


CameraDirector::SharedData
	CameraDirector::DefaultData(
		CameraDirector::GetClassDerivations(),
		CameraDirector::MessageHandlers,
		CameraDirector::GetAttributeIndex(),
		CameraDirector::StateCount,
		(Entity::MakeHandler) CameraDirector::Make
	);

//#############################################################################
// Messaging Support
//
const Receiver::HandlerEntry
	CameraDirector::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(CameraDirector, AttachCameraShip)
};

CameraDirector::MessageHandlerSet
	CameraDirector::MessageHandlers(
		ELEMENTS(CameraDirector::MessageHandlerEntries),
		CameraDirector::MessageHandlerEntries,
		Player::GetMessageHandlers()
	);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraDirector::AttachCameraShipMessageHandler(
		AttachCameraShipMessage *in_message
	)
{
	Check(in_message);
	Check(application);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Get the cameraship ID to attach to
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	HostManager *host = application->GetHostManager();
	Check(host);
	Entity *entity_ptr = host->GetEntityPointer(in_message->cameraShipID);
	Check(entity_ptr);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Get the cameraShip Pointer Object
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	CameraShip *camera_ship;
	camera_ship = Cast_Object(CameraShip*, entity_ptr);
	Check(this);
	Check(camera_ship);
	cameraShip = camera_ship;
}

//#############################################################################
// Model Support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraDirector::UpdateHUD(
		Scalar time_slice
	)
{
	Check(this);
	Check(application);

	//
	//~~~~~~~~~~~~
	// HUD Support
	//~~~~~~~~~~~~
	//
	if (GetGoalEntity())
	{
		Check(GetGoalEntity());
		Player *goal_player = GetGoalEntity()->GetPlayerLink();
		if (goal_player)
		{
			Check(goal_player);
			goalPlayerIndex = goal_player->playerBitmapIndex;
		}
	}

	if (
		application->GetApplicationState() == Application::StoppingMission ||
		application->GetApplicationState() == Application::EndingMission
	)
	{
		displayRankingWindow = False;
		return;
	}
	else if (application->GetSecondsRemainingInGame() <= 30.0f)
	{
		displayRankingWindow = True;
	}
	else
	{
		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Check times for Ranking Window Display
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		if (displayRankingWindow)
		{
			timeDisplayed += time_slice;
			if(timeDisplayed >= displayIntervalOn)
			{
				timeTillDisplayed = displayIntervalOff;
				displayRankingWindow = False;
			}
		}
		else
		{
			timeTillDisplayed -= time_slice;
			if (timeTillDisplayed <= 0.0f)
			{
				timeDisplayed = 0.0f;
				displayRankingWindow = True;
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraDirector::BeADirector(Scalar time_slice)
{
	Check(this);

	Player::PlayerSimulation(time_slice);
	UpdateHUD(time_slice);

	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Time To Cut To  a new Player?
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	if (
		timeLeftOnPlayer > 0.0f
		 && application->GetApplicationState() == Application::RunningMission)
	{
		timeLeftOnPlayer -= time_slice;
		return;
	}

	Player *top_dog = FindPlayerByRank(0);
	if (top_dog)
	{
		Check(top_dog);
		Entity *vehicle = top_dog->GetPlayerVehicle();
		if (vehicle)
		{
			Check(vehicle);
			if ((GetGoalEntity() != vehicle))
			{
				SetGoalEntity(vehicle);
				timeLeftOnPlayer = 10.0f;
				Check(cameraShip);
				CameraShip::DirectionMessage direction_message(
					CameraShip::DirectionMessageID,
					sizeof(CameraShip::DirectionMessage),
					GetGoalEntity()->GetEntityID(),
               0.0f,
					Point3D::Identity
				);
				Check(cameraShip);
				cameraShip->Dispatch(&direction_message);
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraDirector::CreateCameraShip(Scalar)
{
	CreatePlayerVehicle(localOrigin);
	PlayerLinkMessage player_link_message (
		PlayerLinkMessageID,
		sizeof(PlayerLinkMessage),
		this->GetEntityID()
	);
	Check(playerVehicle);
	playerVehicle->Dispatch(&player_link_message);
	playerVehicle->DispatchToReplicants(&player_link_message);
	SetPerformance(&CameraDirector::BeADirector);
	deathCount = 0;

	CameraDirector::AttachCameraShipMessage
		message(
			CameraDirector::AttachCameraShipMessageID,
			sizeof(CameraDirector::AttachCameraShipMessage),
			playerVehicle->GetEntityID()
		);
	Dispatch(&message);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CameraDirector::CameraDirector(
	CameraDirector::MakeMessage *creation_message,
	CameraDirector::SharedData &virtual_data
) :
	Player(creation_message, virtual_data),
	goalEntity(this)
{
	cameraShip = NULL;
	SetValidFlag();
	timeLeftOnPlayer = -1.0f;
	if (GetInstance() != ReplicantInstance)
	{
		SetPerformance(&CameraDirector::CreateCameraShip);
	}
	//
	//~~~~~~~~~~~~
	// HUD Support
	//~~~~~~~~~~~~
	//
	goalPlayerIndex = -1;
	displayRankingWindow = False;
	displayIntervalOn = 10.0f;
	displayIntervalOff = 15.0f;
	timeDisplayed = 0.0f;
	timeTillDisplayed = displayIntervalOff;
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CameraDirector::~CameraDirector()
{

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Player*
	CameraDirector::FindPlayerByRank(int rank_to_find)
{
	Check(application);

	//
	//---------------------------------------
	// Get all players from the entity group
	//---------------------------------------
	//
	EntityGroup *player_group =
		application->GetEntityManager()->FindGroup("Players");
	if(player_group)
	{
		Player *leading_player;

		//
		//-----------------------------------------------------
		// Iterate through the players find the leading player
		//-----------------------------------------------------
		//
		ChainIteratorOf<Node*> iterator(player_group->groupMembers);
		while ((leading_player = (Player*) iterator.ReadAndNext()) != NULL)
		{
			Check(leading_player);
			if (leading_player->playerRanking == rank_to_find)
			{
				return leading_player;
			}
		}
	}
	return NULL;
}

Logical
	CameraDirector::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}
