// *************************************************************************************
// ! DayZPlayerCamera1stPersonVehicle - first person in vehicle
// *************************************************************************************
class DayZPlayerCamera1stPersonVehicle extends DayZPlayerCamera1stPerson
{
	override void OnUpdate(float pDt, out DayZPlayerCameraResult pOutResult)
	{
		super.OnUpdate(pDt, pOutResult);

		pOutResult.m_fUseHeading 		= 0.0;
		pOutResult.m_bUpdateEveryFrame 	= GetGame().IsPhysicsExtrapolationEnabled();
	}
}

// *************************************************************************************
// ! DayZPlayerCamera3rdPersonVehicle - 3rd person in vehicle (generic)
// *************************************************************************************
class DayZPlayerCamera3rdPersonVehicle extends DayZPlayerCameraBase
{
	static const float 	CONST_UD_MIN	= -85.0;		//!< down limit
	static const float 	CONST_UD_MAX	= 85.0;			//!< up limit
	const float UP_ANGLE_CAP 			= 10;			// max up angle, doesnt require gamecode sync

	static const float 	CONST_LR_MIN	= -160.0;		//!< down limit
	static const float 	CONST_LR_MAX	= 160.0;		//!< up limit
	
	const float CONST_LINEAR_VELOCITY_STRENGTH		= 0.025;
	const float CONST_ANGULAR_VELOCITY_STRENGTH		= 0.025;

	const float CONST_ANGULAR_LAG_YAW_STRENGTH		= 4.0;
	const float CONST_ANGULAR_LAG_PITCH_STRENGTH	= 1.5;
	const float CONST_ANGULAR_LAG_ROLL_STRENGTH		= 0.5;

	void DayZPlayerCamera3rdPersonVehicle( DayZPlayer pPlayer, HumanInputController pInput )
	{
		//Print("new camera: DayZPlayerCamera3rdPersonVehicle");

		m_fDistance 		= 4.0;
		m_CameraOffsetMS	= "0.0 1.3 0.0";
		
		HumanCommandVehicle vehicleCommand = pPlayer.GetCommand_Vehicle();
		if ( vehicleCommand )
		{
			Transport transport = vehicleCommand.GetTransport();
			if ( transport )
			{
				m_Transport			= transport;
				m_fDistance			= transport.GetTransportCameraDistance();
				m_CameraOffsetMS	= transport.GetTransportCameraOffset();
			}
		}
	}
	
	override void OnActivate( DayZPlayerCamera pPrevCamera, DayZPlayerCameraResult pPrevCameraResult )
	{
		if (pPrevCamera)
		{
			vector baseAngles		= pPrevCamera.GetBaseAngles();
			m_fUpDownAngle			= baseAngles[0]; 
			m_fLeftRightAngle		= baseAngles[1]; 

			vector addAngles		= pPrevCamera.GetAdditiveAngles();
			m_fUpDownAngleAdd		= addAngles[0];
			m_fLeftRightAngleAdd	= addAngles[1];
		}
				
		m_LagOffsetPosition = vector.Zero;
		m_fLagOffsetVelocityX[0] = 0;
		m_fLagOffsetVelocityY[0] = 0;
		m_fLagOffsetVelocityZ[0] = 0;
		
		m_fLagOffsetVelocityYaw[0] = 0;
		m_fLagOffsetVelocityPitch[0] = 0;
		m_fLagOffsetVelocityRoll[0] = 0;
		
		m_LagOffsetOrientation[0] = 0;
		m_LagOffsetOrientation[1] = 0;
		m_LagOffsetOrientation[2] = 0;
		
		if (m_pPlayer)
		{
			vector rotDiffLS = m_pPlayer.GetOrientation() * Math.DEG2RAD;
			m_LagOffsetOrientation[0] = rotDiffLS[0];
			m_LagOffsetOrientation[1] = rotDiffLS[1];
			m_LagOffsetOrientation[2] = rotDiffLS[2];
		}
	}

	override void OnUpdate( float pDt, out DayZPlayerCameraResult pOutResult )
	{
		//! get player transform
		vector playerTransformWS[4];
		m_pPlayer.GetTransform(playerTransformWS);

		//! get vehicle and set it as ignore entity for camera collision solver
		IEntity vehicle = m_Transport;
		pOutResult.m_CollisionIgnoreEntity = vehicle;
		
		//! update camera offset
		vector cameraPosition = vector.Zero;
		if ( vehicle )
		{
			vector vehiclePositionWS = vehicle.GetOrigin();
			vector vehiclePositionMS = vehiclePositionWS.InvMultiply4(playerTransformWS);
			cameraPosition = vehiclePositionMS + m_CameraOffsetMS;
		}

		//! orientation LS
		m_CurrentCameraYaw		= UpdateLRAngleUnlocked(m_fLeftRightAngle, m_fLeftRightAngleAdd, CONST_LR_MIN, CONST_LR_MAX, pDt);
		m_CurrentCameraPitch	= UpdateUDAngleUnlocked(m_fUpDownAngle, m_fUpDownAngleAdd, CONST_UD_MIN, CONST_UD_MAX, pDt);
		m_CurrentCameraRoll		= 0;
				
		//! create LS lag from vehicle velocities	
		vector posDiffLS = vector.Zero;
		vector rotDiffLS = vector.Zero;
		
		vector rotWS = m_pPlayer.GetOrientation();
		float maxRoll = 10;
		rotWS[2] = Math.Clamp(rotWS[2], -maxRoll, maxRoll) / maxRoll;
		rotWS[2] = rotWS[2] * rotWS[2] * rotWS[2];
		rotWS[2] = rotWS[2] * maxRoll;
		rotWS[2] = 0;
		
		rotWS = rotWS * Math.DEG2RAD;
		
		vector velocity = GetVelocity(vehicle);
		
		if (vehicle)
		{
			vector posDiffWS = velocity * CONST_LINEAR_VELOCITY_STRENGTH;
			posDiffLS = posDiffLS + posDiffWS.InvMultiply3(playerTransformWS);

			vector rotDiffWS = dBodyGetAngularVelocity(vehicle) * CONST_ANGULAR_VELOCITY_STRENGTH;
			rotDiffLS = rotDiffLS + rotDiffWS.InvMultiply3(playerTransformWS);
			
			vector tempRot1 = rotDiffLS;
			rotDiffLS[0] = tempRot1[1];
			rotDiffLS[1] = tempRot1[2];
			rotDiffLS[2] = tempRot1[0];
			
			rotWS[0] = rotWS[0] + rotDiffLS[0];
			rotWS[1] = rotWS[1] + rotDiffLS[1];
			rotWS[2] = rotWS[2] + rotDiffLS[2];
		}		
			
		//! smooth it!
		m_LagOffsetPosition[0] = Math.SmoothCD(m_LagOffsetPosition[0], posDiffLS[0], m_fLagOffsetVelocityX, 0.3, 1000, pDt);
		m_LagOffsetPosition[1] = Math.SmoothCD(m_LagOffsetPosition[1], posDiffLS[1], m_fLagOffsetVelocityY, 0.3, 1000, pDt);
		m_LagOffsetPosition[2] = Math.SmoothCD(m_LagOffsetPosition[2], posDiffLS[2], m_fLagOffsetVelocityZ, 0.3, 1000, pDt);

		m_LagOffsetOrientation[0] = Math.SmoothCDPI2PI(m_LagOffsetOrientation[0], rotWS[0], m_fLagOffsetVelocityYaw, 0.3, 1000, pDt);
		m_LagOffsetOrientation[1] = Math.SmoothCDPI2PI(m_LagOffsetOrientation[1], rotWS[1], m_fLagOffsetVelocityPitch, 0.3, 1000, pDt);
		m_LagOffsetOrientation[2] = Math.SmoothCDPI2PI(m_LagOffsetOrientation[2], rotWS[2], m_fLagOffsetVelocityRoll, 0.3, 1000, pDt);
		
		//! setup orientation
		vector rot = m_LagOffsetOrientation * Math.RAD2DEG;
		if (vehicle)
		{
			vector rotTM[3];
			Math3D.YawPitchRollMatrix(rot, rotTM);
			
			Math3D.MatrixInvMultiply3(playerTransformWS, rotTM, rotTM);
			
			// because we don't have m_fIgnoreParentYaw set
			vector tempRot = Math3D.MatrixToAngles(rotTM);
			rot[0] = tempRot[0];
		}
		
		rot[0] = rot[0] * (1.0 + (Math.InverseLerp(0.0, 1.0 + CONST_ANGULAR_LAG_YAW_STRENGTH, Math.AbsFloat(rotDiffLS[0])) * CONST_ANGULAR_LAG_YAW_STRENGTH));
		rot[1] = rot[1] * (1.0 + (Math.InverseLerp(0.0, 1.0 + CONST_ANGULAR_LAG_PITCH_STRENGTH, Math.AbsFloat(rotDiffLS[1])) * CONST_ANGULAR_LAG_PITCH_STRENGTH));
		rot[2] = rot[2] * (1.0 + (Math.InverseLerp(0.0, 1.0 + CONST_ANGULAR_LAG_ROLL_STRENGTH, Math.AbsFloat(rotDiffLS[2])) * CONST_ANGULAR_LAG_ROLL_STRENGTH));
				
		rot = GetCurrentOrientation() + rot;
		
		float distance = m_fDistance;		
		distance -= (2 * Math.Clamp(Math.InverseLerp(UP_ANGLE_CAP * 0.5, CONST_UD_MAX, rot[1]), 0, 1));
		
		if (rot[1] > UP_ANGLE_CAP)
		{
			rot[1] = UP_ANGLE_CAP;
		}
		
		Math3D.YawPitchRollMatrix(rot, pOutResult.m_CameraTM);
		
		//! setup position		
		pOutResult.m_CameraTM[3] = cameraPosition - m_LagOffsetPosition;
		
		//! setup rest
		pOutResult.m_fDistance 			= distance;
		pOutResult.m_fUseHeading 		= 0.0;
		pOutResult.m_fInsideCamera 		= 0.0;
		pOutResult.m_fIgnoreParentRoll 	= 1.0;
		pOutResult.m_fIgnoreParentPitch = 1.0;
		//pOutResult.m_fIgnoreParentYaw   = 1.0;
		pOutResult.m_bUpdateEveryFrame 	= GetGame().IsPhysicsExtrapolationEnabled();
		
		StdFovUpdate(pDt, pOutResult);
		
		super.OnUpdate(pDt, pOutResult);
	}

	override vector GetBaseAngles()
	{
		vector a;
		a[0] = m_fUpDownAngle;
		a[1] = m_fLeftRightAngle;
		a[2] = 0;
		return a;
	}

	override vector GetAdditiveAngles()
	{
		vector a;
		a[0] = m_fUpDownAngleAdd;
		a[1] = m_fLeftRightAngleAdd;
		a[2] = 0;
		return a;
	}

	//! runtime config
	protected 	vector  m_CameraOffsetMS;	//!< model space offset
	protected 	float 	m_fDistance;		//!< distance from start
	
	//! runtime values 
	protected 	float 	m_fUpDownAngle;			//!< up down angle in rad
	protected 	float 	m_fUpDownAngleAdd;		//!< up down angle in rad
	protected 	float 	m_fLeftRightAngle;		//!< left right angle in rad
	protected 	float 	m_fLeftRightAngleAdd;	//!< left right angle in rad
	
	//! lag offsets
	protected	vector	m_LagOffsetPosition;
	protected	vector	m_LagOffsetOrientation;
	protected	float	m_fLagOffsetVelocityX[1];
	protected	float	m_fLagOffsetVelocityY[1];
	protected	float	m_fLagOffsetVelocityZ[1];
	protected	float	m_fLagOffsetVelocityYaw[1];
	protected	float	m_fLagOffsetVelocityPitch[1];
	protected	float	m_fLagOffsetVelocityRoll[1];
	
	//! cache
	protected Transport m_Transport;
}