#include "MW4Headers.hpp"

#include "Field_Base.hpp"
#include "moverai.hpp"
#include "mech.hpp"
#include <Adept\EntityManager.hpp>

const Scalar DOOR_MOVE_AMOUNT = 6.5f;			//m
const Scalar LIFT_MOVE_AMOUNT = 10.0f;		//m
const Scalar LIFT_OPEN_SPEED = 1.0f;			// mps
const Scalar DOOR_OPEN_SPEED = (DOOR_MOVE_AMOUNT/(LIFT_MOVE_AMOUNT/LIFT_OPEN_SPEED));			// mps

//#############################################################################
//#############################    FieldBase   ################################
//#############################################################################

const StateEngine::StateEntry
	FieldBase::ExecutionStateEngine::StateEntries[]=
{
	STATE_ENTRY(FieldBase__ExecutionStateEngine, OpenDoor),
	STATE_ENTRY(FieldBase__ExecutionStateEngine, CloseDoor)
};


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

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

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

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

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

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

	//
	//-------------------------------------------
	// If there is no change of state just return
	//-------------------------------------------
	//

//	FieldBase *base = Cast_Object (FieldBase *,owningEntity);
	if (new_state == currentState)
		return currentState;

	switch (new_state)
	{
		case RepairState:
			if (currentState != CloseDoorState)
			{
				new_state = CloseDoorState;
			}
			break;
		case NeverExecuteState:
			if ((currentState == RepairState) || (currentState == CloseDoorState))
			{
				new_state = OpenDoorState;
			}
			break;
		break;
	}

	BaseClass::RequestState(new_state, data);

	return currentState;
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void FieldBase::InitializeClass()
{
	Check_Object(MWMover::ExecutionStateEngine::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			FieldBaseClassID,
	    "MechWarrior4::FieldBase",
			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);
}

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

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

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

	CommonCreation(message);
}

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

FieldBase::~FieldBase()
{
}

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

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

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

	m_FirstFrame = true;
	//m_RepairDistance = 9.0f;

	m_DoorAR = FindChildMover ("joint_DoorAR");
	m_DoorFR = FindChildMover ("joint_DoorAL");
	m_DoorAL = FindChildMover ("joint_DoorFR");
	m_DoorFL = FindChildMover ("joint_DoorFL");
	m_Lift = FindChildMover ("joint_Lift");
	m_HasDoors = false;
	if ((m_DoorAR) && (m_DoorFR) && (m_DoorAL) && (m_DoorFL) && (m_Lift))
		m_HasDoors = true;
	m_BaseYDoorAR = 0;
	m_BaseYDoorFR = 0;
	m_BaseYDoorAL = 0;
	m_BaseYDoorFL = 0;
	m_BaseYLift = 0;

	m_LiftAmount =0;
	m_DoorAmount =0;
}

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

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

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

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

	PRECOLLISION_LOGIC("MFB");

	//
	//-------------------------------------------------------------
	// Let the base class deal with setting up the motion variables
	//-------------------------------------------------------------
	//
	if ((m_FirstFrame) && (m_HasDoors))
	{
		Point3D loc;
		loc = (Point3D) m_DoorAR->GetLocalToParent ();
		m_BaseYDoorAR = loc.x;
		loc = (Point3D) m_DoorFR->GetLocalToParent ();
		m_BaseYDoorFR = loc.x;
		loc = (Point3D) m_DoorAL->GetLocalToParent ();
		m_BaseYDoorAL = loc.x;
		loc = (Point3D) m_DoorFL->GetLocalToParent ();
		m_BaseYDoorFL = loc.x;
		loc = (Point3D) m_Lift->GetLocalToParent ();
		m_BaseYLift = loc.y;
	}
	m_FirstFrame = false;

	BaseClass::PreCollisionExecute(till);

	
	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::OpenDoorState:
			DoorOpenAnim (time_slice);
			if ((m_DoorAmount == 0) || (!m_HasDoors))
			{
				executionState->RequestState (ExecutionStateEngine::NeverExecuteState);
				if (m_repairingMech.GetCurrent ())
				{
					AI *ai;
					ai = m_repairingMech.GetCurrent ()->GetAI ();
					if (ai && ai->IsDerivedFrom (MoverAI::DefaultData))
					{
						MoverAI *move;
						move = Cast_Object (MoverAI *,ai);
						move->NextFieldBase ();
					}
				}
			}
			break;
		//
		//-----------------------------------------------------------
		//We need to repair now...some mech powered down inside of us
		//-----------------------------------------------------------
		//
		case ExecutionStateEngine::CloseDoorState:
			DoorCloseAnim (time_slice);
			if ((m_DoorAmount == DOOR_MOVE_AMOUNT) || (!m_HasDoors))
				executionState->RequestState (ExecutionStateEngine::RepairState);
			break;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void FieldBase::DoorOpenAnim (Stuff::Time slice)
{
	if (!m_HasDoors)
		return;
	m_DoorAmount -= (Scalar) (slice / DOOR_OPEN_SPEED);
	Clamp (m_DoorAmount,0,DOOR_MOVE_AMOUNT);
	m_LiftAmount -= (Scalar) (slice / LIFT_OPEN_SPEED);
	Clamp (m_LiftAmount,0,LIFT_MOVE_AMOUNT);

	LinearMatrix4D mat = m_DoorAR->GetLocalToParent ();
	mat(W_Axis, X_Axis) = m_BaseYDoorAR + m_DoorAmount;
	m_DoorAR->SetNewLocalToParent (mat);

	mat = m_DoorFR->GetLocalToParent ();
	mat(W_Axis, X_Axis) = m_BaseYDoorFR - m_DoorAmount;
	m_DoorFR->SetNewLocalToParent (mat);

	mat = m_DoorAL->GetLocalToParent ();
	mat(W_Axis, X_Axis) = m_BaseYDoorAL + m_DoorAmount;
	m_DoorAL->SetNewLocalToParent (mat);

	mat = m_DoorFL->GetLocalToParent ();
	mat(W_Axis, X_Axis) = m_BaseYDoorFL - m_DoorAmount;
	m_DoorFL->SetNewLocalToParent (mat);
	
	mat = m_Lift->GetLocalToParent ();
	mat(W_Axis, Y_Axis) = m_BaseYLift - m_LiftAmount;
	m_Lift->SetNewLocalToParent (mat);

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void FieldBase::DoorCloseAnim (Stuff::Time slice)
{
	if (!m_HasDoors)
		return;
	m_DoorAmount += (Scalar) (slice / DOOR_OPEN_SPEED);
	Clamp (m_DoorAmount,0,DOOR_MOVE_AMOUNT);
	m_LiftAmount += (Scalar) (slice / LIFT_OPEN_SPEED);
	Clamp (m_LiftAmount,0,LIFT_MOVE_AMOUNT);
	
	LinearMatrix4D mat = m_DoorAR->GetLocalToParent ();
	mat(W_Axis, X_Axis) = m_BaseYDoorAR + m_DoorAmount;
	m_DoorAR->SetNewLocalToParent (mat);

	mat = m_DoorFR->GetLocalToParent ();
	mat(W_Axis, X_Axis) = m_BaseYDoorFR - m_DoorAmount;
	m_DoorFR->SetNewLocalToParent (mat);

	mat = m_DoorAL->GetLocalToParent ();
	mat(W_Axis, X_Axis) = m_BaseYDoorAL + m_DoorAmount;
	m_DoorAL->SetNewLocalToParent (mat);

	mat = m_DoorFL->GetLocalToParent ();
	mat(W_Axis, X_Axis) = m_BaseYDoorFL - m_DoorAmount;
	m_DoorFL->SetNewLocalToParent (mat);
	
	mat = m_Lift->GetLocalToParent ();
	mat(W_Axis, Y_Axis) = m_BaseYLift - m_LiftAmount;
	m_Lift->SetNewLocalToParent (mat);

}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void FieldBase::ReactToDestruction(int damage_mode, int damage_type)
{
	if (m_repairingMech.GetCurrent ())
	{
		AI *ai;
		m_repairingMech.GetCurrent ()->ShutDownRequest(false);
		ai = m_repairingMech.GetCurrent ()->GetAI ();
		if (ai && ai->IsDerivedFrom (MoverAI::DefaultData))
		{
			MoverAI *move;
			move = Cast_Object (MoverAI *,ai);
			move->FieldBaseDead ();
		}
	}
	MFB::ReactToDestruction (damage_mode,damage_type);
}


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