//===========================================================================//
// File:	Engine.cpp										
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 09/09/98  JSE  Inital base class based off of Shadowrun/Adept			 //
// 09/24/98  BDB  Inital Engine subsytem class based off of Subsystem		 //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"

#include "Engine.hpp"
#include "Mech.hpp"
#include "HeatManager.hpp"
#include "SubsystemClassData.hpp"

//#############################################################################
//###############################    Engine    ################################
//#############################################################################

Engine::ClassData*
	Engine::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Engine::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			EngineClassID,
			"MechWarrior4::Engine",
			Subsystem::DefaultData,
			0, NULL,
			(Entity::Factory)Make,
			(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
			ExecutionStateEngine::DefaultData,
			(Entity::GameModel::Factory)GameModel::ConstructGameModel,
			NULL,
			(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
			(Entity::GameModel::ModelWrite)GameModel::WriteToText,
			(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
			(Subsystem::StreamCreate) CreateStream
		);
	Register_Object(DefaultData);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Engine__GameModel,
		TonsPerUpgrade,
		m_tonsPerUpgrade,
		Scalar
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Engine__GameModel,
		MPSPerUpgrade,
		m_mpsPerUpgrade,
		Scalar
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Engine__GameModel,
		NumHeatSinks,
		m_numHeatSinks,
		int,
		IntClassID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Engine__GameModel,
		HeatSinkEfficiency,
		m_heatSinkEfficiency,
		Scalar
	);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Engine*
	Engine::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(message);
	gos_PushCurrentHeap(Heap);
	Engine *new_entity = 
		new Engine(DefaultData, message, base_id, NULL);
	gos_PopCurrentHeap();
	Check_Object(new_entity);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Engine::Engine(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Subsystem(class_data, message, base_id, element)
{
	Check_Object(message);
	m_upgrades = message->m_engineUpgrades;

	const GameModel *model = GetGameModel();
	Check_Object(model);
	
	m_engineTonnage = 0.0f;
	for(int i=1; i <= m_upgrades; i++)
		m_engineTonnage += i * model->m_tonsPerUpgrade;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Replicator::CreateMessage*
	Engine::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
{
	Check_Object(this);
	Check_Object(stream);

	stream->AllocateBytes(sizeof(CreateMessage));
	Subsystem::SaveMakeMessage(stream, res_file);
	CreateMessage *message =
		Cast_Pointer(CreateMessage*, stream->GetPointer());
	message->messageLength = sizeof(*message);

	message->m_engineUpgrades = m_upgrades;
	return message;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Engine::~Engine()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void 
	Engine::ConnectHeatManager(HeatManager *heat_manager)
{
	Check_Object(this);
	Check_Object(heat_manager);
	
	const GameModel *model = GetGameModel();
	Check_Object(model);

	if(heat_manager)
	{
		if (Network::GetInstance()->AmIServer())
		{
			for(int i=0;i<model->m_numHeatSinks;i++)
				heat_manager->AddHeatObject(-model->m_heatSinkEfficiency, true);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void 
	Engine::ConnectEngine(Vehicle *parent_mech)
{
	Check_Object(this);
	Check_Object(parent_mech);

	m_parentMech = parent_mech;
	const Vehicle::GameModel *mech_model = parent_mech->GetGameModel();
	Check_Object(mech_model);
	const GameModel *model = GetGameModel();
	Check_Object(model);

	m_mechMaxMaxMPS = mech_model->maxSpeed;
	m_mechMinMaxMPS = mech_model->minMaxSpeed;
	Verify(m_mechMaxMaxMPS >= m_mechMinMaxMPS);
	m_maxUpgrades =(int)((m_mechMaxMaxMPS - m_mechMinMaxMPS) / model->m_mpsPerUpgrade);

	SetNewSpeed();

	//This is to do the tonnage verification and will end up working correctly.
	int number_wanted = m_upgrades;
	m_upgrades = 0;

	for(int i=1; i<=number_wanted; i++)
		Upgrade();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Engine::SetNewSpeed()
{
	Check_Object(this);
	
	const GameModel *model = GetGameModel();
	Check_Object(model);

	m_maxSpeed = m_mechMinMaxMPS + (model->m_mpsPerUpgrade * m_upgrades);
	Clamp(m_maxSpeed, m_mechMinMaxMPS, m_mechMaxMaxMPS);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Engine::Upgrade()
{
	Check_Object(this);
	if(m_upgrades == m_maxUpgrades)
		return;

	const GameModel *model = GetGameModel();
	Check_Object(model);

	Scalar added_tonnage = (m_upgrades + 1) * model->m_tonsPerUpgrade;
	if(m_parentMech->DoesHaveAvailableTonage(added_tonnage))
	{
		m_engineTonnage += added_tonnage;
		m_upgrades ++;
		Max_Clamp(m_upgrades, m_maxUpgrades);
		SetNewSpeed();
		m_parentMech->AddTonage(m_upgrades * model->m_tonsPerUpgrade);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Engine::Degrade()
{
	Check_Object(this);
	if(m_upgrades == 0)
		return;

	const GameModel *model = GetGameModel();
	Check_Object(model);

	m_engineTonnage -= m_upgrades * model->m_tonsPerUpgrade;
	m_parentMech->SubtractTonage(m_upgrades * model->m_tonsPerUpgrade);
	m_upgrades --;
	Min_Clamp(m_upgrades, 0);
	SetNewSpeed();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Engine::CreateStream(ResourceID data_list, MemoryStream *stream)
{
	Check_Pointer(stream);

	stream->AllocateBytes(sizeof(Engine__CreateMessage));
	Subsystem::CreateStream(data_list, stream);

	Engine__CreateMessage *create_message = Cast_Pointer(
		Engine__CreateMessage *, stream->GetPointer());

	create_message->m_engineUpgrades = 0;
}

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

