/*************************************************************************
  Crytek Source File.
  Copyright (C), Crytek Studios, 2001-2004.
 -------------------------------------------------------------------------
  $Id$
  $DateTime$
  
 -------------------------------------------------------------------------
  History:
  - 4:12:2005: Created by Filippo De Luca
*************************************************************************/

#include "StdAfx.h"
#include "Game.h"
#include "Scout.h"
#include "GameUtils.h"

#include <IViewSystem.h>
#include <IItemSystem.h>
#include <IPhysics.h>
#include <ICryAnimation.h>
#include <ISerialize.h>
#include <IRenderAuxGeom.h>

void CScout::Revive(bool fromInit)
{
	CAlien::Revive(fromInit);

	//FIXME:deactivate transrot2k until the assets gets fixed
	if (m_pAnimatedCharacter)
	{
		SAnimatedCharacterParams acFlags = m_pAnimatedCharacter->GetParams();
		acFlags.flags |= eACF_NoTransRot2k;

		m_pAnimatedCharacter->SetParams(acFlags);
	}
}

void CScout::ProcessRotation(float frameTime)
{
	//rotation
	//6 degree of freedom
	//FIXME:put mouse sensitivity here!
	//TODO:use radians
	float rotSpeed(0.3f);

//	if (m_stats.inAir && IsZeroG())
	{		

		// Mikko: Separated the look and movement directions. This is a HACK! The reason is below (moved from the SetActorMovement):
		// >> Danny - old code had desired direction using vLookDir but this caused spinning behaviour
		// >> when it was significantly different to vMoveDir

		if (m_input.viewVector.len2()>0.0f)
		{
			m_eyeMtx.SetRotationVDir(m_input.viewVector.GetNormalizedSafe());
		}

		if (m_input.viewVector.len2()>0.0f)
		{
			m_viewMtx.SetRotationVDir(m_input.viewVector.GetNormalizedSafe());
		}
		else
		{
			Ang3 desiredAngVel(m_input.deltaRotation.x * rotSpeed,0,m_input.deltaRotation.z * rotSpeed);
					
			//rollage
			if (m_input.actions & ACTION_LEANLEFT)
				desiredAngVel.y -= 10.0f * rotSpeed;
			if (m_input.actions & ACTION_LEANRIGHT)
				desiredAngVel.y += 10.0f * rotSpeed;

			Interpolate(m_angularVel,desiredAngVel,3.5f,frameTime);

			Matrix33 yawMtx;
			Matrix33 pitchMtx;
			Matrix33 rollMtx;

			//yaw
			yawMtx.SetRotationZ(m_angularVel.z * gf_PI/180.0f);
			//pitch
			pitchMtx.SetRotationX(m_angularVel.x * gf_PI/180.0f);
			//roll
			if (fabs(m_angularVel.y) > 0.001f)
				rollMtx.SetRotationY(m_angularVel.y * gf_PI/180.0f);
			else
				rollMtx.SetIdentity();
			//
			
			m_viewMtx = m_viewMtx * yawMtx * pitchMtx * rollMtx;
			m_viewMtx.NoScale();
		}
	}
}

void CScout::ProcessMovement(float frameTime)
{
	//movement
	Vec3 move(m_input.movementVector);//usually 0, except AIs

	move += m_viewMtx.GetColumn(0) * m_input.deltaMovement.x;
	move += m_viewMtx.GetColumn(1) * m_input.deltaMovement.y;
	move += m_viewMtx.GetColumn(2) * m_input.deltaMovement.z;
	
	move += m_viewMtx.GetColumn(1) * m_params.approachLookat;

	//cap the movement vector to max 1
	float moveModule(move.len());
	if (moveModule > 1.0f)
	{
		move /= moveModule;
	}

	move *= GetStanceInfo( m_stance )->maxSpeed;

	if (m_stats.sprintLeft)
		move *= m_params.sprintMultiplier;

  //if (m_input.posTarget.len2()>0.0f)
 	  m_desiredVelocity = move;
  //else
  //  Interpolate(m_desiredVelocity,move,3.0f,frameTime);

	Matrix33 velMtx;
	Vec3 vecRefRoll;

	Vec3 tempVel;
	//a bit hacky: needed when the alien is forced to look in some direction
	if (m_input.dirTarget.len2()>0.0f)
		tempVel = m_viewMtx.GetColumn(1);
	else
		tempVel = m_viewMtx.GetColumn(1)*max(0.1f,m_params.forceView) + m_stats.velocity;

	// A little bit more hacky, need to aling the alien to some up vector too.
	Vec3 up;
	if (m_input.upTarget.len2()>0.0f)
	{
		Vec3	diff = m_input.upTarget - m_input.posTarget;
		diff.NormalizeSafe();
		up = diff;
	}
	else
		up = m_viewMtx.GetColumn(2);

	Vec3 forward = tempVel.GetNormalized();
	Vec3 right = (forward % up).GetNormalized();

	velMtx.SetFromVectors(right,forward,right % forward);
	vecRefRoll = tempVel;

	m_desiredVeloctyQuat = GetQuatFromMat33(velMtx);

	//rollage
	if (m_input.upTarget.len2()>0.0f)
	{
		// No rollage, when the up vector is forced!
	}
	else
	{
		float	rollAmt = 0.0f;
		float dotRoll(vecRefRoll * m_baseMtx.GetColumn(0));
		if (fabs(dotRoll)>0.001f)
			rollAmt = max(min(gf_PI*0.30f,dotRoll*m_params.rollAmount),-gf_PI*0.30f);

		Interpolate(m_roll,rollAmt,m_params.rollSpeed,frameTime);

		m_desiredVeloctyQuat *= Quat::CreateRotationY(m_roll);
	}

	if (m_params.movingBend)
	{
		float bend(m_stats.speedModule/GetStanceInfo(m_stance)->maxSpeed);
		bend *= m_stats.velocity.GetNormalizedSafe(ZERO) * m_baseMtx.GetColumn(1);

		m_desiredVeloctyQuat *= Quat::CreateRotationX(-bend * DEG2RAD(m_params.movingBend));
	}

	float rotSpeed = m_params.rotSpeed_min + (1.0f - (max(GetStanceInfo( m_stance )->maxSpeed - max(m_stats.speedModule - m_params.speed_min,0.0f),0.0f) / GetStanceInfo( m_stance )->maxSpeed)) * (m_params.rotSpeed_max - m_params.rotSpeed_min);

	Interpolate(m_turnSpeed,rotSpeed,3.0f,frameTime);

	Quat currQuat(m_baseMtx);
	m_baseMtx = Matrix33(Quat::CreateSlerp( currQuat.GetNormalized(), m_desiredVeloctyQuat, frameTime * m_turnSpeed ));
	m_baseMtx.NoScale();
	
	float desiredVelMod(m_desiredVelocity.len());

	if (m_params.idealSpeed > -0.001f)
		desiredVelMod = min(desiredVelMod,m_params.idealSpeed);

	/*Vec3 desiredVel;
	//a bit hacky: needed when the alien is forced to move in some position
	if (m_input.posTarget.len2()>0.0f)
		desiredVel = m_desiredVelocity;
	else
		desiredVel = m_baseMtx.GetColumn(1)*desiredVelMod;
	
  // make sure alien reaches destination, ignore speedInertia
  if (m_input.posTarget.len2()>0.0f)
    m_velocity = desiredVel;
  else
	  Interpolate(m_velocity,desiredVel,m_params.speedInertia,frameTime);*/

	Interpolate(m_velocity,m_desiredVelocity,m_params.speedInertia,frameTime);

	SCharacterMoveRequest moveRequest;
	moveRequest.movement = m_velocity;
	moveRequest.type = eCMT_Fly;

	//FIXME:sometime
	m_stats.desiredSpeed = m_stats.speedModule;

	if (m_pAnimatedCharacter)
		m_pAnimatedCharacter->AddMovement( moveRequest );

	//FIXME:client view shake when the scout get closer, put this somewhere else.
	IActor *pClient = g_pGame->GetIGameFramework()->GetClientActor();
	if (pClient && m_stats.speedModule>0.5f)
	{
		float dist2ToClient((pClient->GetEntity()->GetWorldPos() - GetEntity()->GetWorldPos()).len2());
		float maxRange(m_params.cameraShakeRange * m_params.cameraShakeRange);
		if (dist2ToClient<maxRange)
		{
			IView *pView = g_pGame->GetIGameFramework()->GetIViewSystem()->GetViewByEntityId(pClient->GetEntityId());
			IView *pViewActive = g_pGame->GetIGameFramework()->GetIViewSystem()->GetActiveView();
			if (pView)
			{
				float shake = min(m_stats.speedModule*0.0015f,0.05f);
				float strenght = (1.0f - (dist2ToClient/maxRange)) * m_params.cameraShakeMultiplier;
				pView->SetViewShake(ZERO,Vec3(shake*strenght,0,shake*strenght),0.1f,0.0225f,1.5f,1);
			}
		}
	}
}
