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

#include "MW4Headers.hpp"

#include "Subsystem.hpp"
#include "MWDamageObject.hpp"
#include "Vehicle.hpp"
#include "SubsystemClassData.hpp"
#include "VehicleInterface.hpp"
#include <Adept\Renderer.hpp>

//#############################################################################
//##########################    ExecutionStateEngine    #######################
//#############################################################################

Subsystem::ExecutionStateEngine::ClassData*
	Subsystem::ExecutionStateEngine::DefaultData = NULL;

const StateEngine::StateEntry
	Subsystem::ExecutionStateEngine::StateEntries[]=
{
	STATE_ENTRY(Subsystem__ExecutionStateEngine, Damaged),
	STATE_ENTRY(Subsystem__ExecutionStateEngine, Destroyed)
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Subsystem::ExecutionStateEngine::InitializeClass()
{
	Check_Object(StateEngine::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			Subsystem__ExecutionStateEngineClassID,
			"Subsystem::ExecutionStateEngine",
			Entity::ExecutionStateEngine::DefaultData,
			ELEMENTS(StateEntries), StateEntries,
			(Entity::ExecutionStateEngine::Factory)Make,
			(Entity::ExecutionStateEngine::FactoryRequest::Factory)
				&FactoryRequest::ConstructFactoryRequest
		);
	Register_Object(DefaultData);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Subsystem::ExecutionStateEngine::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Subsystem::ExecutionStateEngine*
	Subsystem::ExecutionStateEngine::Make(
		Subsystem *subsystem,
		FactoryRequest *request
	)
{
	Check_Object(subsystem);
	Check_Object(request);
	gos_PushCurrentHeap(Heap);
	Subsystem::ExecutionStateEngine *engine =
		new Subsystem::ExecutionStateEngine(DefaultData, subsystem, request);
	gos_PopCurrentHeap();
	return engine;
		
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	Subsystem::ExecutionStateEngine::RequestState(
		int new_state,
		void* data
	)
{
	Check_Object(this);
	Check_Object(owningEntity);

	//
	//------------------------------
	// Quick exit if no state change
	//------------------------------
	//
	if (new_state == currentState)
		return currentState;

	//
	//-------------------------------------------------------------------
	// Once destroyed, always destroyed until we tell it to never execute
	//-------------------------------------------------------------------
	//
	if (currentState == DestroyedState && new_state != NeverExecuteState)
		return currentState;

	//
	//---------------------------------------------------------------------
	// State engine is being called directly here because we don't want the
	// automatic execute/not-execute functionality in entity to operate.
	// What we want is for destroyed state to destroy the subsystem, never
	// execute state to turn it off, and anything else to turn it on
	//---------------------------------------------------------------------
	//
	Subsystem *subsystem;
	subsystem = Cast_Object(Subsystem *, owningEntity);
	switch (BaseClass::BaseClass::RequestState(new_state, data))
	{
	case DestroyedState:
		subsystem->DestroySubsystem();
		break;

	case NeverExecuteState:
		if (oldState != NeverExecuteState)
			owningEntity->EnterNeverExecuteState();
		break;

	default:
		if (oldState == NeverExecuteState)
			owningEntity->LeaveNeverExecuteState();
		break;
	}

	return currentState;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Subsystem::ExecutionStateEngine::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}


//#############################################################################
//###############################    Subsystem    ##############################
//#############################################################################

Subsystem::ClassData*
	Subsystem::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Subsystem::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			SubsystemClassID,
			"MechWarrior4::Subsystem",
			Entity::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);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Subsystem__GameModel,
		TotalSlotsTaken,
		totalSlotsTaken,
		int,
		IntClassID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		Subsystem__GameModel,
		Tonage,
		tonage,
		Scalar
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Subsystem__GameModel,
		BattleValue,
		battleValue,
		int,
		IntClassID
	);

	CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
		Subsystem__GameModel,
		ItemID,
		itemID,
		int,
		IntClassID
	);

}

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

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

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

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

	message->subsystemIndex = subsystemIndex;
	message->locationID = internalLocation;
	message->criticalHitsTaken = critHitsTaken;
	return message;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Subsystem::Subsystem(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Entity(class_data, message, base_id, element)
{
	Check_Pointer(this);
	Check_Object(message);

	subsystemIndex = message->subsystemIndex;
	internalLocation = (char)message->locationID;
	critHitsTaken = message->criticalHitsTaken;
	parentAsVehicle = NULL;
	containedInSlotType = -1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MWObject* Subsystem::GetParentVehicle()
{
	Check_Object(GetParentEntity());
	Verify(GetParentEntity()->IsDerivedFrom(MWObject::DefaultData));
	return (MWObject *)GetParentEntity();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Vehicle* Subsystem::GetParentAsVehicle()
{
	return parentAsVehicle;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Subsystem::~Subsystem()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Subsystem::Reuse(
		const CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(this);
	STOP(("Not implemented"));
	Entity::Reuse(message, base_id);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//Must call this virtual function in the inherited method!!!!
bool
	Subsystem::ConnectSubsystem()
{
	Check_Object(this);

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

	MWObject *parent_vehicle;
	parent_vehicle = GetParentVehicle();
	Check_Object(parent_vehicle);

	int old_level = Stuff::ArmorLevel;
	if (Stuff::ArmorLevel == 4)
		Stuff::ArmorLevel = 3;



	if(parent_vehicle->IsDerivedFrom(Vehicle::DefaultData))
		parentAsVehicle = Cast_Object(Vehicle *, parent_vehicle);
		//  cant call cast object due to armor level four
		//  we are in parent_vehicles contructor
		//  if we call that dynamic cast will fail
	
	Stuff::ArmorLevel = old_level;

	internal_structure = parent_vehicle->internalDamageObjects.Find(internalLocation);

	if (internal_structure != 0)
	{
		Check_Object(internal_structure);
		if(parent_vehicle->DoesHaveAvailableTonage(model->tonage))
		{
			if(internal_structure->AddSubsystem(this)) 
			{
				parent_vehicle->AddTonage(model->tonage);
				parent_vehicle->AddBattleValue(model->battleValue);
				return true;
			}
		}
	}

	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Subsystem::Respawn(Entity::CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);
	
	Entity::Respawn(message);

	//If we were destroyed then we must re add ourselves!
	MWObject *parent_vehicle;
	parent_vehicle = GetParentVehicle();
	Check_Object(parent_vehicle);
	MWInternalDamageObject *internal_structure;
	internal_structure = parent_vehicle->internalDamageObjects.Find(internalLocation);
	Check_Object(internal_structure);
	if(!internal_structure->subsystemChain.IsPlugMember(this))
	{
		internal_structure->subsystemChain.Add(this);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//
bool
	Subsystem::DisconnectSubsystem()
{
	Check_Object(this);
	
	MWObject *parent_vehicle;
	parent_vehicle = GetParentVehicle();
	Check_Object(parent_vehicle);
	const GameModel *model = GetGameModel();
	Check_Object(model);

	parent_vehicle->SubtractTonage(model->tonage);
	parent_vehicle->AddBattleValue(-model->battleValue);

	MWInternalDamageObject *zone = parent_vehicle->internalDamageObjects.Find(internalLocation);
	if(zone)
	{
		zone->FreeAvailableSlots(containedInSlotType, model->totalSlotsTaken);
	}

	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	Subsystem::TakeCriticalHit()
{
	Check_Object(this);

	Check_Object(executionState);

	executionState->RequestState(ExecutionStateEngine::DestroyedState);
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Subsystem::DestroySubsystem()
{
	Check_Object(this);

	MWObject *parent_vehicle;
	parent_vehicle = GetParentVehicle();
	Check_Object(parent_vehicle);
	const GameModel *model = GetGameModel();
	Check_Object(model);
	parent_vehicle->SubtractTonage(model->tonage);
	parent_vehicle->SubtractBattleValue(model->battleValue);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Subsystem::BecomeInteresting(bool render_me)
{
	Check_Object(this);
	LOAD_LOGIC("Become Interesting::Subsystem");

	BaseClass::BecomeInteresting(render_me);

	ElementRenderer::Element *element = GetElement();
	Check_Object(element);

	element->SetAlwaysCullMode();
}

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

	stream->AllocateBytes(sizeof(Subsystem__CreateMessage));

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

	create_message->alignment = Entity::DefaultAlignment;
	create_message->initialAge = 0.0f;
	create_message->replicatorFlags = 0;
	create_message->dataListID = data_list;
	create_message->classID = GetClassIDFromDataListID(data_list);
	create_message->localToParent = LinearMatrix4D::Identity;
	create_message->executionState.currentState = ExecutionStateEngine::NeverExecuteState;
	create_message->nameID = NameTable::NullObjectID;
	create_message->replicatorID.connectionID = 0;
	create_message->replicatorID.localID = 1;
	create_message->subsystemIndex = 0;
	create_message->criticalHitsTaken = 0;
	//This should be updated elsewhere.
	create_message->locationID = 0;
}

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

