//===========================================================================//
// File:	CameraShip.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 "CameraShip.hpp"
#include "MWObject.hpp"
//#include "CombatAI.hpp"
#include "MWGUIManager.hpp"
#include "MWVideoRenderer.hpp"
#include "VehicleInterface.hpp"
#include "Mech.hpp"
#include "aiutils.hpp"

#include <Adept\EntityManager.hpp>
#include <Adept\DamageObject.hpp>
#include <Adept\Effect.hpp>
#include <Adept\Map.hpp>
#include <Adept\VideoRenderer.hpp>
#include <Adept\CameraComponent.hpp>
#include <Adept\Player.hpp>
#include <Adept\CollisionGrid.hpp>

CameraShipManager *CameraShipManager::Instance = NULL;

//#############################################################################
//##################    CameraShip::ExecutionStateEngine    ########################
//#############################################################################

const StateEngine::StateEntry
	CameraShip::ExecutionStateEngine::StateEntries[]=
{
	STATE_ENTRY(CameraShip__ExecutionStateEngine, CameraMotion)
};

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraShip::ExecutionStateEngine::InitializeClass()
{



	Check_Object(StateEngine::DefaultData);
	Verify(!DefaultData);

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraShip::ExecutionStateEngine::TerminateClass()
{

	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

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

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


//#############################################################################
//###############################    CameraShipManager
//#############################################################################


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

void CameraShipManager::TurnOnCameras()
{
	
	Verify(!cinemaStarted);

	{
		

		CameraComponent *cam_comp = VideoRenderer::Instance->GetSceneCamera();
		Check_Object(cam_comp);

		cam_comp->GetPerspective(
			nearClip,
			farClip,
			horFOV,
			aspectRatio
		);

		ToggleHud(false);

		Stuff::ChainIteratorOf<CameraShip*> iterator(&camerasInWorld);
		CameraShip *camera;
		while ((camera = iterator.GetCurrent()) != NULL)
		{
			camera->TurnOn();
			iterator.ReadAndNext();
		}

		cinemaStarted = true;
	}

}

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

void CameraShipManager::TurnOffCameras()
{

	if (cinemaStarted)
	{

		cinemaStarted = false;

		Stuff::ChainIteratorOf<CameraShip*> iterator(&camerasInWorld);
		CameraShip *camera;
		while ((camera = iterator.GetCurrent()) != NULL)
		{
			camera->TurnOff();
			iterator.ReadAndNext();
		}
		activeCamera.Remove();

		MWVideoRenderer *vid_rend = Cast_Object(MWVideoRenderer *, VideoRenderer::Instance);
		Stuff::RGBColor color;
		color = RGBColor(0.0f, 0.0f, 0.0f);
		vid_rend->SetFadeLevel(0.0f, color);

		ToggleHud(true);
		ToggleInternal(true);
		if (Player::GetInstance()->playerInterface)
		{
			VehicleInterface *v_interface = Cast_Object(VehicleInterface *, Player::GetInstance()->playerInterface);
			v_interface->RestorePreferredCameraPosition();
			v_interface->SetCameraViewLevel(v_interface->GetCameraViewLevel());
			v_interface->SetCameraMode(v_interface->cameraView);
		}
		CameraComponent *cam_comp = VideoRenderer::Instance->GetSceneCamera();
		Check_Object(cam_comp);

		cam_comp->SetPerspective(
			nearClip,
			farClip,
			horFOV,
			aspectRatio
		);
	
	}
}

//-----------------------------------------------------------------------------

void CameraShipManager::ToggleHud(bool active)
{
	
	// turn on
	if (active)
	{
		if (!hudOn) // if hud off
		{
			hudOn = true;

			MWVideoRenderer *vid_rend = Cast_Object(MWVideoRenderer *, VideoRenderer::Instance);
			vid_rend->TurnOffCinemaScope();
		}
		if(MWGUIManager::GetInstance())
		{
			MWGUIManager::GetInstance()->Show();
		}
	}
	else // turn off
	{
		if (hudOn) // if hud on
		{
			hudOn = false;

			MWVideoRenderer *vid_rend = Cast_Object(MWVideoRenderer *, VideoRenderer::Instance);

			vid_rend->TurnOnCinemaScope();
			vid_rend->SetCinemaScopeWidthRatio(2.35f);
		}
		if(MWGUIManager::GetInstance())
		{
			MWGUIManager::GetInstance()->Hide();
		}
	}
}

//-----------------------------------------------------------------------------

void CameraShipManager::ToggleInternal(bool active)
{

	if (active)
	{
		internalCamera = true;

		MWVideoRenderer *vid_rend = Cast_Object(MWVideoRenderer *, VideoRenderer::Instance);
		CameraComponent *cam_comp = VideoRenderer::Instance->GetSceneCamera();
			Check_Object(cam_comp);

			cam_comp->SetPerspective(
				nearClip,
				farClip,
				horFOV,
				aspectRatio
			);

				
		Stuff::RGBColor color;
		color = RGBColor(0.0f, 0.0f, 0.0f);
		vid_rend->SetFadeLevel(0.0f, color);

		if (Player::GetInstance()->playerInterface)
		{
			VehicleInterface *v_interface = Cast_Object(VehicleInterface *, Player::GetInstance()->playerInterface);
			v_interface->SetCameraMode(VehicleInterface::InMechCameraView);
		}

	}
	else
	{
		internalCamera = false;
		
		if (Player::GetInstance()->playerInterface)
		{
			VehicleInterface *v_interface = Cast_Object(VehicleInterface *, Player::GetInstance()->playerInterface);
			v_interface->SetCameraMode(VehicleInterface::ExternalCameraAttachedView);
		}
	}

}



//-----------------------------------------------------------------------------

void CameraShipManager::InterruptCinema()
{
	if (cinemaStarted)
	{
		cinemaStarted = false;


		ToggleHud(true);
		
		MWVideoRenderer *vid_rend = Cast_Object(MWVideoRenderer *, VideoRenderer::Instance);
		Stuff::RGBColor color;
		color = RGBColor(0.0f, 0.0f, 0.0f);
		vid_rend->SetFadeLevel(0.0f, color);

		activeCamera.Remove();

		CameraComponent *cam_comp = VideoRenderer::Instance->GetSceneCamera();
		if (cam_comp)
		{
			Check_Object(cam_comp);

			cam_comp->SetPerspective(
				nearClip,
				farClip,
				horFOV,
				aspectRatio
			);
		}

	}
}


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


void CameraShipManager::SetInternalCamera(bool show_hud)
{
	CameraShip *last_camera = activeCamera.GetCurrent();
	if (last_camera != NULL)
	{
		last_camera->ClearActive();
	}

	activeCamera.Remove();


	ToggleInternal(true);

	if (show_hud)
	{
		ToggleHud(true);
	}
	else
	{
		ToggleHud(false);
	}

}

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

void CameraShipManager::SetActiveCamera(CameraShip *camera)
{

		
	ToggleHud(false);
	ToggleInternal(false);


	CameraShip *last_camera = activeCamera.GetCurrent();
	if (last_camera != NULL)
	{
		last_camera->ClearActive();
	}

	activeCamera.Remove();
	activeCamera.Add(camera);

	camera->SetActive();

}

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

CameraShip *CameraShipManager::GetActiveCamera(void)
{
	return activeCamera.GetCurrent();
}


//#############################################################################
//###############################    CameraShip
//#############################################################################

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraShip::InitializeClass()
{
	CameraShipManager::Instance = new CameraShipManager();
	Register_Object(CameraShipManager::Instance);


	Check_Object(ExecutionStateEngine::DefaultData);
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			CameraShipClassID,
			"MechWarrior4::CameraShip",
			BaseClass::DefaultData,
			NULL, 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
		);
	Register_Object(DefaultData);

	INDIRECT_STATE_ATTRIBUTE(
		CameraShip,
		ExecutionState,
		executionState,
		CameraShip__ExecutionStateEngine
	);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraShip::TerminateClass()
{
	Unregister_Object(CameraShipManager::Instance);
	delete CameraShipManager::Instance;
	CameraShipManager::Instance = NULL;

	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

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

	CameraShipManager::Instance->AddCamera(new_entity);

	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CameraShip::CameraShip(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Entity(class_data, message, base_id, element),
	m_ShakeDistance(0),
	m_ShakeSource(-2)
{
	Check_Pointer(this);
	Check_Object(message);




	cameraMovement = FollowInitialPositionMovementType;
	targetMovement = FollowInitialPositionMovementType;

	cameraFollowObject = NULL;
	targetFollowObject = NULL;

	cameraPath = NULL;
	targetPath = NULL;

	cameraOffsetLocal = true;
	targetOffsetLocal = true;

	cameraOffset.Init(Point3D::Identity, Point3D::Identity, 0.0f);
	targetOffset.Init(Point3D::Identity, Point3D::Identity, 0.0f);

	
	Point3D current_position;




	current_position = 	GetElement()->GetNewLocalToParent();
	cameraPosition.Init(current_position, current_position, 0.0f);
	targetPosition.Init(current_position, current_position, 0.0f);
	
	overridePitchFlag = false;
	overrideYawFlag = false;
	overrideRollFlag = false;

	cameraPitch.Init(0.0f, 0.0f, 0.0f);
	cameraYaw.Init(0.0f, 0.0f, 0.0f);
	cameraRoll.Init(0.0f, 0.0f, 0.0f);
		
	cameraFOV.Init(0.0f, 0.0f, 0.0f);
	fadeLevel.Init(0.0f, 0.0f, 0.0f);

	fadeState = FadeBlackType;

	cameraCurrentWayPoint = 0;
	targetCurrentWayPoint = 0;
	cameraPathSpeed = 0.0f;
	targetPathSpeed = 0.0f;

	cameraWaypointAnimation.Init(Point3D::Identity, Point3D::Identity, 0.0f);
	targetWaypointAnimation.Init(Point3D::Identity, Point3D::Identity, 0.0f);


	activeCamera = false;

	currentTargetPosition = Point3D::Identity;

	UsePostCollision();
}

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

CameraShip::~CameraShip()
{
	CameraShipManager::Instance->InterruptCinema();

}

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

void
	CameraShip::PostCollisionExecute(Time till)
{
	Check_Object(this);
	POSTCOLLISION_LOGIC("CameraShip");

	Scalar time_slice = GetTimeSlice(till);

	cameraOffset.UpdateTime(time_slice);
	targetOffset.UpdateTime(time_slice);
	cameraPitch.UpdateTime(time_slice);
	cameraYaw.UpdateTime(time_slice);
	cameraRoll.UpdateTime(time_slice);
	cameraFOV.UpdateTime(time_slice);
	fadeLevel.UpdateTime(time_slice);
	cameraPosition.UpdateTime(time_slice);
	targetPosition.UpdateTime(time_slice);
	cameraWaypointAnimation.UpdateTime(time_slice);
	targetWaypointAnimation.UpdateTime(time_slice);


	// position the target
	Point3D camera_position = Point3D::Identity;
	YawPitchRoll camera_rotation = YawPitchRoll::Identity;

	switch(cameraMovement)
	{
	case FollowInitialPositionMovementType:
		// stay where we are.  This is the initial position
		camera_position = cameraPosition.GetCurrentValue();
		break;

	case FollowObjectMovementType:
		{
			Check_Object(cameraFollowObject);
			Point3D offset = cameraOffset.GetCurrentValue();

			LinearMatrix4D camera_object_mat = cameraFollowObject->GetLocalToWorld();
			if (cameraOffsetLocal)
			{
				// take into account rotation
				camera_position.Multiply(offset, camera_object_mat);
			}
			else
			{
				// leave rotation out of it
				Point3D world_point;
				world_point = camera_object_mat;
				camera_position.Add(offset, world_point);
			}

		}
		break;

	case FollowPathMovementType:
		{

			// set the position..

			

			
			Point3D offset = cameraOffset.GetCurrentValue();
			camera_position.Add(offset, cameraWaypointAnimation.GetCurrentValue());
			

			if (cameraWaypointAnimation.AnimDone())
			{
				// go to the next waypoint if there is one...

				++cameraCurrentWayPoint;

				if (cameraCurrentWayPoint < cameraPath->pathPoints.size())
				{
					Scalar time_to_next_waypoint = 0.0f;

					Point3D to, from;
					to = cameraPath->pathPoints[cameraCurrentWayPoint];
					from = cameraPath->pathPoints[cameraCurrentWayPoint-1];


					Vector3D length_vector;
					length_vector.Subtract(to, from);

					if (!Small_Enough(length_vector) && !Small_Enough(cameraPathSpeed))
					{
						Scalar length = length_vector.GetLength();
						time_to_next_waypoint = length / cameraPathSpeed;
					}

					cameraWaypointAnimation.Init(from, to, time_to_next_waypoint);
				}

			}

		}
		break;

	case FollowPositionMovemntType:
		// stay where we are.  The call made set this position and now we just sit
		camera_position = cameraPosition.GetCurrentValue();
		break;
	}

	// position the camera
	Point3D target_position = Point3D::Identity;

	switch(targetMovement)
	{
	case FollowInitialPositionMovementType:
		break;
	case FollowObjectMovementType:
		{
			Check_Object(targetFollowObject);
			Point3D offset = targetOffset.GetCurrentValue();

			LinearMatrix4D target_object_mat = targetFollowObject->GetLocalToWorld();
			
			if (targetOffsetLocal)
			{	
				// take into account rotation
				target_position.Multiply(offset, target_object_mat);
			}
			else
			{
				// leave rotation out of it
				Point3D world_point;
				world_point = target_object_mat;
				target_position.Add(offset, world_point);
			}
		}
		break;
	case FollowPathMovementType:
		{
			// set the position..

		
			Point3D offset = targetOffset.GetCurrentValue();

			
			target_position.Add(offset, targetWaypointAnimation.GetCurrentValue());

			if (targetWaypointAnimation.AnimDone())
			{
				// go to the next waypoint if there is one...

				++targetCurrentWayPoint;

				if (targetCurrentWayPoint < targetPath->pathPoints.size())
				{
					Scalar time_to_next_waypoint = 0.0f;

					Point3D to, from;
					to = targetPath->pathPoints[targetCurrentWayPoint];
					from = targetPath->pathPoints[targetCurrentWayPoint-1];


					Vector3D length_vector;
					length_vector.Subtract(to, from);

					if (!Small_Enough(length_vector) && !Small_Enough(targetPathSpeed))
					{
						Scalar length = length_vector.GetLength();
						time_to_next_waypoint = length / targetPathSpeed;
					}

					targetWaypointAnimation.Init(from, to, time_to_next_waypoint);
				}

			}
		}
		break;
	case FollowPositionMovemntType:
		target_position = targetPosition.GetCurrentValue();
		break;
	}


	Stuff::Line3D line;
	line.m_length = 100.0f;
	line.m_direction = Vector3D::Down;
	line.m_origin = camera_position;
	line.m_origin.y += 50.0f;
	Stuff::Normal3D normal;
	Entity::CollisionQuery query(&line,&normal,Entity::CanBeWalkedOnFlag,NULL);
	Check_Object(CollisionGrid::Instance);
	if (CollisionGrid::Instance->ProjectLine(&query) == Adept::Map::GetInstance())
	{
		Stuff::Point3D hit_spot;
		line.FindEnd(&hit_spot);

		if (camera_position.y < hit_spot.y + 1.0f)
		{
			camera_position.y = hit_spot.y + 1.0f;
		}
	}

	// point the camera

	if (targetMovement != FollowInitialPositionMovementType)
	{
		if (target_position == camera_position)
		{
			camera_rotation = GetLocalToWorld();
		}
		else
		{
			Stuff::Vector3D vec_to_target;
			vec_to_target.Subtract(target_position, camera_position);
			Stuff::YawPitchRange ypr(vec_to_target);

			camera_rotation.yaw = ypr.yaw;
			camera_rotation.pitch = ypr.pitch;
			camera_rotation.roll = 0.0f;
		}
	}
	else
	{
		camera_rotation = GetLocalToWorld();
	}

	currentTargetPosition = target_position;

	if (overridePitchFlag)
	{
		camera_rotation.pitch = cameraPitch.GetCurrentValue();
	}

	if (overrideYawFlag)
	{
		camera_rotation.yaw = cameraYaw.GetCurrentValue();
	}

	if (overrideRollFlag)
	{
		camera_rotation.roll = cameraRoll.GetCurrentValue();
	}

	UpdateFootShaking(camera_rotation);

	//set the camera position
	
	LinearMatrix4D new_position(true);
	new_position.BuildRotation(camera_rotation);
	new_position.BuildTranslation(camera_position);


	SetNewLocalToParent(new_position);
	SyncMatrices(true);



	if (activeCamera)
	{
		CameraComponent *cam_comp = VideoRenderer::Instance->GetSceneCamera();
		Check_Object(cam_comp);

		cam_comp->SetPerspective(
			CameraShipManager::Instance->nearClip,
			CameraShipManager::Instance->farClip,
			cameraFOV.GetCurrentValue(),
			CameraShipManager::Instance->aspectRatio
		);


		MWVideoRenderer *vid_rend = Cast_Object(MWVideoRenderer *, VideoRenderer::Instance);


		Stuff::RGBColor color;

		if (fadeState == FadeBlackType)
		{
			color = RGBColor(0.0f, 0.0f, 0.0f);
		}
		else
		{
			color = RGBColor(1.0f, 1.0f, 1.0f);
		}

		vid_rend->SetFadeLevel(fadeLevel.GetCurrentValue(), color);
		

	}

	lastParameterization = till;
	initialLocalToParent = GetLocalToParent();

	BaseClass::PostCollisionExecute(till);
}

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

void CameraShip::TurnOn(void)
{
	Check_Object(this);

	executionState->RequestState(ExecutionStateEngine::AlwaysExecuteState);
	entityElement->SetNeverCullMode();

	cameraFOV.Init(CameraShipManager::Instance->horFOV, CameraShipManager::Instance->horFOV, 0.0f);
}

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

void CameraShip::TurnOff(void)
{
	Check_Object(this);
	
	executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
	entityElement->SetAlwaysCullMode();

}

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

void CameraShip::CameraFollowObject(Entity *entity)
{
	Check_Object(this);

	cameraMovement = FollowObjectMovementType;
	cameraFollowObject = entity;

}

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

void CameraShip::CameraFollowPath(float speed_mps, Path *path)
{
	Check_Object(this);

	cameraMovement = FollowPathMovementType;

	cameraPath = path;
	cameraCurrentWayPoint = 0;
	cameraPathSpeed = speed_mps;


	int size = path->pathPoints.size();
	Verify (size>0);
	
	Point3D to, from;

	from = path->pathPoints[0];

	if (size < 2)
		to = path->pathPoints[0];
	else
		to = path->pathPoints[1];

	Scalar time_to_next_waypoint = 0.0f;

	Vector3D length_vector;
	length_vector.Subtract(to, from);

	if (!Small_Enough(length_vector) && !Small_Enough(speed_mps))
	{
		Scalar length = length_vector.GetLength();
		time_to_next_waypoint = length / speed_mps;
	}

	cameraCurrentWayPoint = 1;
	cameraWaypointAnimation.Init(from, to, time_to_next_waypoint);
}

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

void CameraShip::CameraPosition(float x, float y, float z, float time)
{
	Check_Object(this);
	
	cameraMovement = FollowPositionMovemntType;

	Point3D current_position;
	current_position = GetLocalToWorld();

	cameraPosition.Init(current_position, Point3D(x,y,z), time);

}

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

void CameraShip::CameraDetach()
{
	Check_Object(this);

	cameraMovement = FollowPositionMovemntType;

	Stuff::Point3D local_pos;
	local_pos = GetLocalToWorld();
	cameraPosition.Init(local_pos, local_pos, 0.0f);

}

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

void CameraShip::CameraOffset(float x, float y, float z, float time, bool local)
{
	Check_Object(this);

	cameraOffset.Init(cameraOffset.GetCurrentValue(), Point3D(x,y,z), time);
	cameraOffsetLocal = local;
}

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

void CameraShip::OverrideCameraPitch(float degrees, float time)
{
	Check_Object(this);
	overridePitchFlag = true;
	YawPitchRoll camera_rotation;
	camera_rotation = GetLocalToWorld();
	cameraPitch.Init(camera_rotation.pitch, degrees, time);
}

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

void CameraShip::OverrideCameraRoll(float degrees, float time)
{
	Check_Object(this);
	overrideRollFlag = true;
	YawPitchRoll camera_rotation;
	camera_rotation = GetLocalToWorld();
	cameraRoll.Init(camera_rotation.roll, degrees, time);
}

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

void CameraShip::OverrideCameraYaw(float degrees, float time)
{
	Check_Object(this);
	overrideYawFlag = true;
	YawPitchRoll camera_rotation;
	camera_rotation = GetLocalToWorld();
	cameraYaw.Init(camera_rotation.yaw, degrees, time);
}

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

void CameraShip::ResetCameraOverrides(void)
{
	Check_Object(this);
	overrideRollFlag = false;
	overrideYawFlag = false;
	overridePitchFlag = false;
}

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

void CameraShip::TargetFollowObject(Entity *entity)
{
	Check_Object(this);

	targetMovement = FollowObjectMovementType;
	targetFollowObject = entity;

}

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

void CameraShip::TargetFollowPath(float speed_mps, Path *path)
{
	Check_Object(this);

	targetMovement = FollowPathMovementType;

	targetPath = path;
	targetCurrentWayPoint = 0;
	targetPathSpeed = speed_mps;

	int size = path->pathPoints.size();
	Verify (size>0);
	
	Point3D to, from;

	from = path->pathPoints[0];

	if (size < 2)
		to = path->pathPoints[0];
	else
		to = path->pathPoints[1];

	Scalar time_to_next_waypoint = 0.0f;

	Vector3D length_vector;
	length_vector.Subtract(to, from);

	if (!Small_Enough(length_vector) && !Small_Enough(speed_mps))
	{
		Scalar length = length_vector.GetLength();
		time_to_next_waypoint = length / speed_mps;
	}

	targetCurrentWayPoint = 1;

	targetWaypointAnimation.Init(from, to, time_to_next_waypoint);

}

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

void CameraShip::TargetPosition(float x, float y, float z, float time)
{
	Check_Object(this);

	targetMovement = FollowPositionMovemntType;

	Point3D current_position;
	current_position = currentTargetPosition;

	targetPosition.Init(current_position, Point3D(x,y,z), time);

}

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

void CameraShip::TargetDetach()
{
	Check_Object(this);


	targetMovement = FollowPositionMovemntType;

	targetPosition.Init(currentTargetPosition, currentTargetPosition, 0.0f);

}

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

void CameraShip::TargetOffset(float x, float y, float z, float time, bool local)
{
	Check_Object(this);
	targetOffset.Init(targetOffset.GetCurrentValue(), Point3D(x,y,z), time);
	targetOffsetLocal = local;

}

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

void CameraShip::SetCameraFOV(float degrees, float time)
{
	Check_Object(this);

	cameraFOV.Init(cameraFOV.GetCurrentValue(), degrees, time);
	
}

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

void CameraShip::FadeToBlack(float time)
{
	Check_Object(this);
	fadeState = FadeBlackType;
	fadeLevel.Init(fadeLevel.GetCurrentValue(), 1.0f, time);
}

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

void CameraShip::FadeToWhite(float time)
{
	Check_Object(this);
	fadeState = FadeWhiteType;
	fadeLevel.Init(fadeLevel.GetCurrentValue(), 1.0f, time);
}

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

void CameraShip::FadeFromBlack(float time)
{
	Check_Object(this);
	fadeState = FadeBlackType;
	fadeLevel.Init(fadeLevel.GetCurrentValue(), 0.0f, time);
}

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

void CameraShip::FadeFromWhite(float time)
{
	Check_Object(this);
	fadeState = FadeWhiteType;
	fadeLevel.Init(fadeLevel.GetCurrentValue(), 0.0f, time);
}


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


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraShip::SetFootShakeParams(Adept::ObjectID id, int meters)
{
	m_ShakeSource = id;
	m_ShakeDistance = meters;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	CameraShip::UpdateFootShaking(Stuff::YawPitchRoll& camera_rotation)
{
	if ((m_ShakeSource < 0) ||
		(m_ShakeDistance <= 0) ||
		(Adept::NameTable::GetInstance() == 0))
	{
		return;
	}

	Adept::Entity* entity = Adept::NameTable::GetInstance()->FindData(m_ShakeSource);
	if ((entity == 0) ||
		(entity->IsDestroyed() == true) ||
		(entity->IsDerivedFrom(Mech::DefaultData) == false))
	{
		return;
	}

	Mech* mech = Cast_Object(Mech*,entity);

	Stuff::Scalar time_delta((Stuff::Scalar)gos_GetElapsedTime() - mech->GetLastFootFallTime());
	const Stuff::Scalar min_time_delta(0.4f);

	Stuff::Scalar distance = GetApproximateLength((Stuff::Point3D)GetLocalToWorld(),
												  (Stuff::Point3D)mech->GetLocalToWorld());

	if ((time_delta > min_time_delta) ||
		(distance > m_ShakeDistance * 2))
	{
		return;
	}

	Stuff::Scalar magnitude(1 - (time_delta / min_time_delta));
	magnitude *= (m_ShakeDistance / distance);
	magnitude *= 0.005f;

	camera_rotation.yaw += (Stuff::Random::GetFraction() - 0.5f) * magnitude;
	camera_rotation.pitch += (Stuff::Random::GetFraction() - 0.5f) * magnitude;
	camera_rotation.roll += (Stuff::Random::GetFraction() - 0.5f) * magnitude;
}
