#include "MW4Headers.hpp"

#include "MFB.hpp"
#include "gameinfo.hpp"
#include "Mech.hpp"
#include "Weapon.hpp"
#include "MWDamageObject.hpp"
#include "CombatAI.hpp"
#include "HeatManager.hpp"

#include <Adept\EntityManager.hpp>
#include <Adept\DamageObject.hpp>
#include <Adept\Effect.hpp>

//#############################################################################
//###############################    MFB    ###################################
//#############################################################################

const StateEngine::StateEntry
	MFB::ExecutionStateEngine::StateEntries[]=
{
	STATE_ENTRY(MFB__ExecutionStateEngine, Repair),
	STATE_ENTRY(MFB__ExecutionStateEngine, OpeningMotion)
};


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

ChainOf<MFB*>
	*MFB::s_MFBs = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::ExecutionStateEngine::InitializeClass()
{
	Check_Object(BaseClass::DefaultData);
	Verify(!DefaultData);

	DefaultData =
		new ClassData(
			MFB__ExecutionStateEngineClassID,
			"MFB::ExecutionStateEngine",
			BaseClass::DefaultData,
			ELEMENTS(StateEntries), StateEntries,
			(Entity::ExecutionStateEngine::Factory)Make,
			(Entity::ExecutionStateEngine::FactoryRequest::Factory)
				&FactoryRequest::ConstructFactoryRequest
		);
	Register_Object(DefaultData);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB::ExecutionStateEngine*
	MFB::ExecutionStateEngine::Make(
		MFB *vehicle,
		FactoryRequest *request
	)
{
	Check_Object(vehicle);
	Check_Object(request);
	gos_PushCurrentHeap(Heap);
	MFB::ExecutionStateEngine *engine =
		new MFB::ExecutionStateEngine(DefaultData, vehicle, request);
	gos_PopCurrentHeap();
	return engine;
		
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MFB::ExecutionStateEngine::RequestState(
		int new_state,
		void* data
	)
{
	Check_Object(this);
	Check_Object(owningEntity);

	//
	//-------------------------------------------
	// If there is no change of state just return
	//-------------------------------------------
	//
	if (new_state == currentState)
		return currentState;

#if 0
	MFB *mfb = Cast_Object(MFB*, owningEntity);
	Check_Object(mfb);
#endif
	switch (BaseClass::RequestState(new_state, data))
	{
	case OpeningMotionState:
	//	mfb->animStateEngine->RequestState(MechAnimationStateEngine::OpeningState);
		break;
	}

	return currentState;
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB::ClassData*
	MFB::DefaultData = NULL;

DWORD MechWarrior4::Executed_MFB_Count = 0;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::InitializeClass()
{
	Check_Object(MWMover::ExecutionStateEngine::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			MFBClassID,
	        "MechWarrior4::MFB",
			BaseClass::DefaultData,
			0, NULL,
			(Entity::Factory)Make,
			(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
			ExecutionStateEngine::DefaultData,
			(Entity::GameModel::Factory)GameModel::ConstructGameModel,
			(Entity::GameModel::Factory)GameModel::ConstructOBBStream,
			(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
			(Entity::GameModel::ModelWrite)GameModel::WriteToText,
			(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
			AnimationStateEngine::Make
		);
	Register_Object(DefaultData);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		MFB__GameModel,
		RepairEffectResource,
		m_repairEffectResource,
		ResourceID
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		MFB__GameModel,
		RepairCenterOffset,
		m_repairCenterOffset,
		Stuff::Vector3D
	);

	DIRECT_GAME_MODEL_ATTRIBUTE(
		MFB__GameModel,
		RepairDistance,
		m_repairDistance,
		Stuff::Scalar
	);

	s_MFBs = new ChainOf<MFB*>(NULL);
	Check_Pointer(s_MFBs);
	
}

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

	s_MFBs->DeletePlugs();
	delete s_MFBs;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB*
	MFB::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(message);
	gos_PushCurrentHeap(Heap);
	MFB *new_entity = new
		MFB(DefaultData, message, base_id, NULL);
	gos_PopCurrentHeap();
	Check_Object(new_entity);
	
	
	Check_Object(EntityManager::GetInstance());
	EntityManager::GetInstance()->AddMover(new_entity);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB::MFB(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Vehicle(class_data, message, base_id, element),
	m_repairEffect(NULL),
	m_repairingMech(NULL)
{
	Check_Pointer(this);
	Check_Object(message);

	HookUpSubsystems();
	CommonCreation(message);

	s_MFBs->Add(this);
}

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

MFB::~MFB()
{
	if(m_weaponIterator)
	{
		delete m_weaponIterator;
	}
	if(m_internalIterator)
	{
		delete m_internalIterator;
	}
	if(m_armorIterator)
	{
		delete m_armorIterator;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::Reuse(
		const CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(this);
	Check_Object(message);

	STOP(("Not implemented"));
	BaseClass::Reuse(message, base_id);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::CommonCreation(CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);

	//m_RepairDistance = 18.0f;
	m_CurrentMechUsing = NULL;
	m_currentState = FixingGimpState;
	m_nextRepairTime = gos_GetElapsedTime();
	m_weaponIterator = NULL;
	m_internalIterator = NULL;
	m_armorIterator = NULL;
}

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

	BaseClass::Respawn(message);
	CreateMessage *vehicle_message = Cast_Pointer(CreateMessage *, message);
	CommonCreation(vehicle_message);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MFB::TurnOn (void)
{
	lastParameterization = gos_GetElapsedTime ();
	executionState->RequestState(ExecutionStateEngine::DrivingMotionState);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::PreCollisionExecute(Time till)
{
	Check_Object(this);

	PRECOLLISION_LOGIC("MFB");
	#if defined(LAB_ONLY)
		if (!instanceName)
			MWGameInfo::g_LastVehicle[0] = '\0';
		else
		{
			strncpy(MWGameInfo::g_LastVehicle, instanceName, sizeof(MWGameInfo::g_LastVehicle)-1);
			MWGameInfo::g_LastVehicle[sizeof(MWGameInfo::g_LastVehicle)-1] = '\0';
		}
		MWGameInfo::g_LastVehiclePos = GetLocalToWorld ();
	#endif

	//
	//-------------------------------------------------------------
	// Let the base class deal with setting up the motion variables
	//-------------------------------------------------------------
	//
	BaseClass::PreCollisionExecute(till);
	Set_Statistic(Executed_MFB_Count, Executed_MFB_Count+1);

	//
	//----------------------------------------------------------------------
	// Any executing vehicle should not be in never execute state and should
	// always use post collision
	//----------------------------------------------------------------------
	//
	Check_Object(executionState);
	int pre_state = executionState->GetState();
	Verify(pre_state != ExecutionStateEngine::NeverExecuteState);
	
	//
	//-------------------------------------------------------------------------
	// Do the appropriate type of simulation.  Animated motion state is for BRB
	//-------------------------------------------------------------------------
	//
	Scalar time_slice = GetTimeSlice(till);
	switch (pre_state)
	{
		//
		//----------------------------------------------------
		// Always execute state is for the normal driving code
		//----------------------------------------------------
		//
		case ExecutionStateEngine::DrivingMotionState:
//			ComputeForwardSpeed(time_slice);
//			UpdateVehiclePosition(time_slice);
			break;
		//
		//-----------------------------------------------------------
		//We need to repair now...some mech powered down inside of us
		//-----------------------------------------------------------
		//
		case ExecutionStateEngine::RepairState:
			if(gos_GetElapsedTime() > m_nextRepairTime)
				RepairTargetMech(time_slice);
			break;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::PostCollisionExecute(Time till)
{
	Check_Object(this);
	Check_Object(executionState);
	POSTCOLLISION_LOGIC("MFB");

	switch(executionState->GetState())
	{
	case ExecutionStateEngine::RepairState:
		CreateRepairEffect();
		break;
	}
	
	BaseClass::PostCollisionExecute(till);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MFB::CollisionHandler(
		Stuff::LinearMatrix4D *new_position,
		Stuff::DynamicArrayOf<CollisionData> *collisions
	)
{
	Check_Object(this);
	Verify(GetInterestLevel() != DormantInterestLevel);
	Verify(EntityManager::GetInstance()->IsInPostCollisionExecution(this));

	Verify(!newCollisions);
	delete collisions;
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::RepairTargetMech(Scalar time_slice)
{
	Check_Object(this);
	
	Mech *mech = m_repairingMech.GetCurrent();
	if(mech)
	{
		if (mech->vehicleInterface)
		{
			mech->vehicleInterface->ReactToEvent (VehicleInterface::MISSILE_LOCKLOST);
		}
		Check_Object(mech);
		if(!mech->vehicleShutDown)
		{
			ShutMFBDown();
			return;
		}
		switch(m_currentState)
		{
			case FixingGimpState:
			{
			//First check to see if we fix gimp
				if(mech->IsGimped())
				{
                    mech->RepairGimp();
					m_nextRepairTime = gos_GetElapsedTime() + 6.0f;
					m_currentState = ReloadingAmmoState;
					return;
				}
			}
			case ReloadingAmmoState:
			{
				Weapon *weapon;
				m_currentState = ReloadingAmmoState;
				while((weapon = m_weaponIterator->ReadAndNext()) != NULL)
				{
					Check_Object(weapon);
					if(weapon->HasFiredAmmo())
					{
						weapon->Reload();
						m_nextRepairTime = gos_GetElapsedTime() + 2.0f;
						return;
					}
				}
			}
			case ReloadingCoolantState:
			{
				m_currentState = ReloadingCoolantState;
				Check_Object(mech->m_heatManager);

				if(mech->m_heatManager->GetCoolant() < mech->m_heatManager->GetMaxCoolant())
				{
					mech->m_heatManager->AddCoolant(mech->m_heatManager->GetMaxCoolant());
					m_nextRepairTime = gos_GetElapsedTime() + 2.0f;
					return;
				}
			}
			case FixingInternalState:
			{
				MWInternalDamageObject *internal;
				m_currentState = FixingInternalState;
				while((internal = m_internalIterator->ReadAndNext()) != NULL)
				{
					Check_Object(internal);
					if(internal->CanRepair())
					{
						internal->Reuse();
						m_nextRepairTime = gos_GetElapsedTime() + 0.10f;
						return;
					}
				}
			}
			case FixingArmorState:
			{
				DamageObject *armor;
				m_currentState = FixingArmorState;
				while((armor = m_armorIterator->ReadAndNext()) != NULL)
				{
					Check_Object(armor);
					if(armor->CanRepair())
					{
						armor->Reuse();
// MSL 5.05 Changed Repair time for external armor
//						m_nextRepairTime = gos_GetElapsedTime() + 0.10f;
						m_nextRepairTime = gos_GetElapsedTime() + 1.0f;
						return;
					}
				}
			}
		}
		mech->ShutDownRequest(false);
		if (mech->vehicleInterface)
		{
			mech->vehicleInterface->ReactToEvent (VehicleInterface::REPAIR_STOP);
		}
		ShutMFBDown();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::StartNewMech(Mech *new_mech)
{
	Check_Object(this);
	Check_Object(new_mech);

	m_repairingMech.Remove();
	m_repairingMech.Add(new_mech);

	m_currentState = FixingGimpState;

	if(m_weaponIterator)
	{
		delete m_weaponIterator;
	}
	m_weaponIterator = new WeaponIteratorType(&new_mech->weaponChain);
	if(m_internalIterator)
	{
		delete m_internalIterator;
	}
	m_internalIterator = new InternalIteratorType(&new_mech->internalDamageObjects);
	if(m_armorIterator)
	{
		delete m_armorIterator;
	}
	m_armorIterator = new ArmorIteratorType(&new_mech->damageObjects);

	Check_Object(executionState);
	executionState->RequestState(ExecutionStateEngine::RepairState);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::ShutMFBDown()
{
	Check_Object(this);
	Check_Object(executionState);

	executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
	if(m_repairEffect.GetCurrent())
	{
		m_repairEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);
		m_repairEffect.Remove();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::CreateRepairEffect()
{
	Check_Object(this);
	
	if(!m_repairEffect.GetCurrent())
	{
		const GameModel *model = GetGameModel();
		Check_Object(model);

		Effect *effect =
			CreateLoopingEffect(model->m_repairEffectResource, this);

		if(effect)
			m_repairEffect.Add(effect);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MFB::IsWithin(
		Entity *target,
		Vector3D offset,
		Scalar distance,
		bool ignorey
	)
{
	Check_Object(this);
	Check_Object(target);
	
	Vector3D target_line = Vector3D::Identity;
	Point3D our_position;
	Point3D target_position;

	target_position = target->GetLocalToWorld();
	our_position.Multiply(offset, GetLocalToWorld());

	Point3D our_translation;
	our_translation = GetLocalToWorld();
	our_position.Add(our_position, our_translation);
	if ((our_position.y < -1500) || (target_position.y < -1500))				// units in hell are never near each other
		return false;

	if (ignorey)
	{
		our_position.y = 0;
		target_position.y = 0;
	}

	target_line.Subtract(target_position, our_position);

	return target_line.GetLengthSquared() <= (distance * distance);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB*
	MFB::IsWithinMFB(Mech *mech)
{
	Check_Object(mech);

	MFB *mfb;
	ChainIteratorOf<MFB *> iterator(s_MFBs);

	while((mfb = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(mfb);

		//make sure the mfb is not destroyed and the mech is within 10.0 meters
		if(!mfb->IsDestroyed())
		{
			if(mfb->IsWithin(mech, mfb->GetGameModel()->m_repairCenterOffset, mfb->GetGameModel()->m_repairDistance))
			{//if(mech->IsWithin(mfb, mfb->GetGameModel()->m_repairDistance))
// MSL 5.03 Checks for Team Alignment
// If your not the same alignment as the MFB you can't use it
// MSL 5.05 Checks and allows for No Team
				if ((mfb->GetAlignment() == mech->GetAlignment()) ||
					(mech->GetTeam() == MWApplication::No_Team))
				{
					if (mech->vehicleInterface)
					{
						mech->vehicleInterface->ReactToEvent (VehicleInterface::REPAIR_START);
					}
					mfb->StartNewMech(mech);
					return mfb;
				}
			}
		}
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MFB::ReactToDestruction(int damage_mode, int damage_type)
{
	Check_Object(this);

	if(!IsDestroyed())
	{
		if(m_AI)
		{
			Check_Object(m_AI);
			m_AI->Die();
		}
		
		SetDestroyedFlag(damage_mode);

		if (damage_type != SplashDamageType)
			DealSplashDamage();

		MW4AI::g_Rect4DHash->RemovePermRect (this);
		const GameModel *model = GetGameModel();
		Check_Object(model);
				
		CreateEffect(model->destroyedEffectResource, (Point3D)GetLocalToWorld());
		Entity *death_entity = CreateStaticHermitEntity(model->deathEntityResource);
		if((death_entity) && (!m_deathEntity.GetCurrent()))
		{
			m_deathEntity.Remove();
			m_deathEntity.Add(death_entity);
		}	
		

		RemoveFromExecution();

		
	}

	Entity::ReactToDestruction(damage_mode, damage_type);
}

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