#include "rp.h"
#pragma hdrstop

#include "booster.h"
#include "..\munga\controls.h"
#include "rpcnsl.h"
#include "vtv.h"
#include "vtvmppr.h"
#include "..\munga\app.h"
#include "..\munga\hostmgr.h"

//#############################################################################
// Shared Data Support
//
Booster::SharedData
	Booster::DefaultData(
		Booster::GetClassDerivations(),
		Booster::MessageHandlers,
		Booster::GetAttributeIndex(),
		Booster::StateCount
	);

Derivation* Booster::GetClassDerivations()
{
	static Derivation classDerivations(VTVSubsystem::GetClassDerivations(), "Booster");
	return &classDerivations;
}

//#############################################################################
// Messaging Support
//
const Receiver::HandlerEntry
	Booster::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(Booster, Activate),
	MESSAGE_ENTRY(Booster, ConfigureMappables),
	MESSAGE_ENTRY(Booster, ChooseButton)
};

Receiver::MessageHandlerSet
	Booster::MessageHandlers(
		ELEMENTS(Booster::MessageHandlerEntries),
		Booster::MessageHandlerEntries,
		VTVSubsystem::GetMessageHandlers()
	);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Booster::ConfigureMappablesMessageHandler(
		ReceiverDataMessageOf<ControlsButton> *message
	)
{
	Check(this);
	Check(message);


	//---------------------------------------------------------------------
	// If the hardwired button was pressed, process presses of the mappable
	// buttons, otherwise it was released, so erase any temporary mappings
	//---------------------------------------------------------------------
	if (message->dataContents > 0)
	{
		EnterConfiguration(
			NULL,									// direct target (none here)
			this,									// receiver
			ActivateMessageID,				// activation message ID
			ChooseButtonMessageID			// configuration message ID
		);
	}
	else
	{
		ExitConfiguration();
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Booster::ChooseButtonMessageHandler(
		ReceiverDataMessageOf<ControlsButton> *message
	)
{
	if (message->dataContents > 0)
	{
		//
		//---------------------------------------------------------------------
		// Find the control manager of the vehicle, and have it turn off an
		// existing mapping of a given type if it is there, otherwise it should
		// create a new one
		//---------------------------------------------------------------------
		//
		VTV* vtv = GetEntity();
		Check(vtv);
		VTVControlsMapper* controls =
			Cast_Object(
				VTVControlsMapper*,
				vtv->GetSubsystem(VTV::ControlsMapperSubsystem)
			);
		Check(controls);

		controls->AddOrErase(message->dataContents, this, ActivateMessageID);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Booster::ActivateMessageHandler(
		ReceiverDataMessageOf<ControlsButton> *message
	)
{
	Check(this);
	Check(message);

	VTV* vtv = GetEntity();
	Check(vtv);
	Check(application);
	if (
		vtv->GetSimulationState() == VTV::BurningState
		 || application->GetApplicationState() != Application::RunningMission
	)
	{
		Check_Fpu();
		return;
	}

	if (
		GetSimulationState() != BoosterBurning
		 && message->dataContents > 0
		 && boostersRemaining > 0
	)
	{
		SetSimulationState(BoosterBurning);
		AlwaysExecute();
		lastPerformance = Now();
		burnTimeRemaining = burnTimeMax;
		boostersRemaining -= 1;
		ForceUpdate();
		vtv->ForceUpdate();

		//
		//---------------------------------------------------------------
		// Tell the console (if there is one) that we turned on a booster
		//---------------------------------------------------------------
		//
		Check(application);
		HostManager *host_manager = application->GetHostManager();
		Check(host_manager);
		Host *console_host = host_manager->GetConsoleHost();
		if (console_host)
		{
			Check(console_host);

			ConsolePlayerVTVBoosterMessage booster_message(vtv->ownerID, 1);

			application->SendMessage(
				console_host->GetHostID(),
				NetworkClient::ConsoleClientID,
				&booster_message
			);
		}
	}
	Check_Fpu();
}

//#############################################################################
// Attribute Support
//
const Booster::IndexEntry
	Booster::AttributePointers[]=
{
	ATTRIBUTE_ENTRY(Booster, BurnTimeRemaining,	burnTimeRemaining),
	ATTRIBUTE_ENTRY(Booster, BoostersRemaining,	boostersRemaining),
	ATTRIBUTE_ENTRY(Booster, ScaleFactor,			scaleFactor),
	ATTRIBUTE_ENTRY(Booster, SmokeDensity,			smokeDensity),
	ATTRIBUTE_ENTRY(Booster, PercentDone,			percentDone)
};

Booster::AttributeIndexSet& Booster::GetAttributeIndex()
{
	static Booster::AttributeIndexSet attributeIndex(ELEMENTS(Booster::AttributePointers), Booster::AttributePointers, VTVSubsystem::GetAttributeIndex());
	return attributeIndex;
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Booster::BoosterSimulation(Scalar time_slice)
{
	Check(this);

	//
	//--------------------------------------------------------------------------
	// Get pointers to the other subsystems we will have to deal with inside the
	// VTV
	//--------------------------------------------------------------------------
	//
	VTV* vtv = GetEntity();
	Check(vtv);

	//
	//-------------------------
	// Apply the Booster
	//-------------------------
	//
	percentDone = (burnTimeMax - burnTimeRemaining) / burnTimeMax;
	if(percentDone > 1.0f)
	{
		percentDone = 1.0f;
	}
	if(percentDone < 0.0f)
	{
		percentDone = 0.0f;
	}

	switch (GetSimulationState())
	{
	case BoosterBurning:
		if (burnTimeRemaining > 0.0)
		{
			scaleFactor = Vector3D(1.0f, 1.0f, 1.0f);
			Scalar phase = burnTimeMax - burnTimeRemaining;
			if (phase < 0.2f)
			{
				scaleFactor *= phase/0.2f;
			}
			else if (burnTimeRemaining < 0.2f)
			{
				scaleFactor *= burnTimeRemaining;
			}
			smokeDensity = 1.0f;
			burnTimeRemaining -= time_slice;
			vtv->localAcceleration.linearMotion.z += acceleration;
			vtv->boosterOn = True;
			vtv->boosterScale = scaleFactor;
			vtv->boosterSmokeDensity = smokeDensity;
		}
		else
		{
			SetSimulationState(DefaultState);
			ForceUpdate();
			vtv->ForceUpdate();
			scaleFactor = Vector3D::Identity;
			smokeDensity = 0.0f;
			NeverExecute();

			//
			//----------------------------------------------------------------
			// Tell the console (if there is one) that we turned off a booster
			//----------------------------------------------------------------
			//
			Check(application);
			HostManager *host_manager = application->GetHostManager();
			Check(host_manager);
			Host *console_host = host_manager->GetConsoleHost();
			if (console_host)
			{
				Check(console_host);

				ConsolePlayerVTVBoosterMessage booster_message(vtv->ownerID, 0);

				application->SendMessage(
					console_host->GetHostID(),
					NetworkClient::ConsoleClientID,
					&booster_message
				);
			}
		}
		break;
	}
	Check_Fpu();
}

//#############################################################################
// Damage Support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Booster::DeathReset(int reset_command)
{
	Check(this);

	//
	//--------------------------------------------------------------------------
	// Get pointers to the other subsystems we will have to deal with inside the
	// VTV
	//--------------------------------------------------------------------------
	//
	VTV* vtv = GetEntity();
	Check(vtv);

	if (GetSimulationState() == BoosterBurning)
	{
		//
		//----------------------------------------------------------------
		// Tell the console (if there is one) that we turned off a booster
		//----------------------------------------------------------------
		//
		Check(application);
		HostManager *host_manager = application->GetHostManager();
		Check(host_manager);
		Host *console_host = host_manager->GetConsoleHost();
		if (console_host)
		{
			Check(console_host);

			ConsolePlayerVTVBoosterMessage booster_message(vtv->ownerID, 0);

			application->SendMessage(
				console_host->GetHostID(),
				NetworkClient::ConsoleClientID,
				&booster_message
			);
		}
	}
	burnTimeRemaining = 0.0f;
	scaleFactor = Vector3D::Identity;
	SetSimulationState(DefaultState);
	if (reset_command == VTV::FootballReset)
	{
		boostersRemaining = maxBoosters;
	}
	NeverExecute();
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Booster::DeathShutdown(int)
{
	//
	//---------------------------------------------------
	// Don't shutdown anything until we are reincarnated
	//---------------------------------------------------
	//
	Check_Fpu();
}

//#############################################################################
//   Constructor and Destructor
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Booster::Booster(
	VTV *entity,
	int subsystem_ID,
	SubsystemResource *subsystem_resource
):
	VTVSubsystem(
		entity,
		subsystem_ID,
		subsystem_resource,
		DefaultData,
		NULL,								// not 'direct-mapped'
		Booster::ActivateMessageID	// message ID to trigger booster
	)
{
	//-------------------------------------------
	// Set values
	//-------------------------------------------
	maxBoosters = subsystem_resource->boosterCount;
	boostersRemaining = maxBoosters;
	burnTimeMax = subsystem_resource->burnTime;
	acceleration = -subsystem_resource->boosterAcceleration;
	burnTimeRemaining = 0.0f;	// In Seconds
	scaleFactor = Vector3D::Identity;
	smokeDensity = 0.0f;
	percentDone = 1.0f;
	NeverExecute();

	if (entity->GetInstance() != VTV::ReplicantInstance)
	{
		SetPerformance(&Booster::BoosterSimulation);
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Booster::~Booster()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	Booster::CreateStreamedSubsystem(
		NotationFile					*model_file,
		const char						*model_name,
		const char						*subsystem_name,
		SubsystemResource				*subsystem_resource,
		NotationFile					*subsystem_file,
		const ResourceDirectories	*directories
	)
{
	Check_Pointer(subsystem_name);
	Check_Pointer(subsystem_resource);
	Check(subsystem_file);

	if (
		!Subsystem::CreateStreamedSubsystem(
			model_file,
			model_name,
			subsystem_name,
			subsystem_resource,
			subsystem_file,
			directories
		)
	)
	{
		return False;
	}

	subsystem_resource->subsystemModelSize = sizeof(*subsystem_resource);
	subsystem_resource->classID = RegisteredClass::BoosterClassID;

	//
	// Figure out who our voltage source is
	//

	if (
		!subsystem_file->GetEntry(
			subsystem_name,
			"BoosterCount",
			&subsystem_resource->boosterCount
		)
	)
	{
		DEBUG_STREAM << subsystem_name << " missing BoosterCount!\n" << std::flush;
		Check_Fpu();
		return False;
	}

	if (
		!subsystem_file->GetEntry(
			subsystem_name,
			"BurnTime",
			&subsystem_resource->burnTime
		)
	)
	{
		DEBUG_STREAM << subsystem_name << " missing BurnTime!\n" << std::flush;
		Check_Fpu();
		return False;
	}

	if (
		!subsystem_file->GetEntry(
			subsystem_name,
			"BoosterAcceleration",
			&subsystem_resource->boosterAcceleration
		)
	)
	{
		DEBUG_STREAM << subsystem_name << " missing BoosterAcceleration!\n" << std::flush;
		Check_Fpu();
		return False;
	}
	if (subsystem_resource->boosterAcceleration < 0.0f)
	{
		DEBUG_STREAM << subsystem_name<<" must have a positive acceleration!\n" << std::flush;
		Check_Fpu();
		return False;
	}

	Check_Fpu();
	return True;
}

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