
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_Human_PlayerAnimStateHandler.cpp
// Author : Andy Tate
// Date   : 14-Dec-2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "Actor/Vertebrates/Human/AVP_Human_PlayerAnimStateHandler.h"

#include "Animation/Asura_Animation.h"
#include "Rendering/Asura_Font_System.h"

#include "Actor/AVP_Actor_AnimStateData.h"
#include "Actor/Vertebrates/AVP_Vertebrate_AnimationHandler.h"
#include "Actor/Vertebrates/Human/AVP_Human_PlayerAnimStateData.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_Game_Core.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ClientActor_Human.h"
#include "Inventory/AVP_Inventory_Group.h"
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_Item.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Moves/AVP_Moves.h"
#include "Physics/AVP_Collision_Descriptor.h"

DEBUGVAR float dbg_fCrouchTweenTime = 0.2f;
DEBUGVAR float dbg_fAimTweenTime = 0.2f;

void AVP_Human_PlayerAnimStateHandler::Initialise()
{
	const AVP_Inventory_System* pxInvSys = m_xOwnerActor.GetInventorySystem();
	const AVP_Inventory_Group* pxWeaponGroup = pxInvSys != NULL ? pxInvSys->GetGroup( uAVP_INVENTORY_GROUPHASH_WEAPON ) : NULL;
	if ( pxWeaponGroup != NULL )
	{
		Asura_Hash_ID uWeaponItr( pxWeaponGroup->FindLowestPriorityItem() );
		const AVP_Inventory_Item* pxItem = pxWeaponGroup->GetItem( uWeaponItr );
		if ( pxItem )
		{
			const AVP_Actor_AnimStateData* pxStateData = AVP_Human_PlayerAnimStateData::GetSingleton().GetAnimStateData( m_xOwnerActor.GetAnimStateBlueprintType(), pxItem->GetBlueprintHash(), AVP_Game_Core::IsLocalPlayer( m_xOwnerActor.GetGuid() ), false, false, false, false, MOVESPEED_SLOW );
			if ( pxStateData )
			{
				GetSpeedFromDirectionalStateData( pxStateData, m_fWalkSpeed );
			}

			pxStateData = AVP_Human_PlayerAnimStateData::GetSingleton().GetAnimStateData( m_xOwnerActor.GetAnimStateBlueprintType(), pxItem->GetBlueprintHash(), AVP_Game_Core::IsLocalPlayer( m_xOwnerActor.GetGuid() ), false, false, false, false, MOVESPEED_MEDIUM );
			if ( pxStateData )
			{
				GetSpeedFromDirectionalStateData( pxStateData, m_fRunSpeed );
			}

			pxStateData = AVP_Human_PlayerAnimStateData::GetSingleton().GetAnimStateData( m_xOwnerActor.GetAnimStateBlueprintType(), pxItem->GetBlueprintHash(), AVP_Game_Core::IsLocalPlayer( m_xOwnerActor.GetGuid() ), false, false, false, false, MOVESPEED_FAST );
			if ( pxStateData )
			{
				GetSpeedFromDirectionalStateData( pxStateData, m_fSprintSpeed );
			}

			pxStateData = AVP_Human_PlayerAnimStateData::GetSingleton().GetAnimStateData( m_xOwnerActor.GetAnimStateBlueprintType(), pxItem->GetBlueprintHash(), AVP_Game_Core::IsLocalPlayer( m_xOwnerActor.GetGuid() ), false, true, false, false, MOVESPEED_SLOW );
			if ( pxStateData )
			{
				GetSpeedFromDirectionalStateData( pxStateData, m_fCrouchSpeed );
			}
		}
	}

	if (m_xOwnerActor.IsJumping())
	{
		m_eCurrentFallingState = FALLINGSTATE_JUMPING;
	}
	else if (m_xOwnerActor.IsFalling())
	{
		m_eCurrentFallingState = FALLINGSTATE_FALLING;
	}
	else if (m_xOwnerActor.IsLanding())
	{
		m_eCurrentFallingState = FALLINGSTATE_LANDING;
	}
	else
	{
		m_eCurrentFallingState = FALLINGSTATE_NONE;
	}
}

DEBUGVAR float dbg_fHumanFallTweenTime = DEFAULT_TWEEN_TIME;

void AVP_Human_PlayerAnimStateHandler::Update()
{
	PARENT::Update();

	AVP_ClientActor_Human& xHuman = static_cast< AVP_ClientActor_Human& >( m_xOwnerActor );

	const AVP_Inventory_System* pxInvSys = m_xOwnerActor.GetInventorySystem();
	const AVP_Inventory_Group* pxWeaponGroup = pxInvSys != NULL ? pxInvSys->GetGroup( uAVP_INVENTORY_GROUPHASH_WEAPON ) : NULL;
	Asura_Assert( pxWeaponGroup != NULL, "No weapon group" );
	if ( pxWeaponGroup != NULL )
	{
		Asura_Hash_ID uCurrentWeaponBPHash = ASURA_HASH_ID_UNSET;
		const AVP_Inventory_Item* pxItem = pxWeaponGroup->GetCurrentInventoryItem();
		if ( pxItem )
		{
			uCurrentWeaponBPHash = pxItem->GetBlueprintHash();
		}
		const bool bFirstPerson = AVP_Game_Core::IsLocalPlayer( m_xOwnerActor.GetGuid() );
		const bool bAiming = xHuman.IsAiming();
		const bool bCrouch = xHuman.IsCrouching();
		const bool bJumping = xHuman.IsJumping();
		const bool bFalling = xHuman.IsFalling();
		const bool bLanding = xHuman.IsLanding();
		
		float fParametricLimit = 2.0f;
		if (m_xOwnerActor.GetMovementSystem() && m_xOwnerActor.GetMovementSystem()->GetCurrentMove())
		{
			const AVP_Move* pxCurrentMove = static_cast<const AVP_Move*>(m_xOwnerActor.GetMovementSystem()->GetCurrentMove());
			if (pxCurrentMove && pxCurrentMove->IsRestrictedMovement())
			{
				fParametricLimit = 0.5f; // Disable running
			}
		}
		const float fSpeedParametric = Asura_Maths::ClampToRange( GetSpeedParametric(), 0.0f, fParametricLimit );
		if ( fSpeedParametric > m_fUBSpeedParametric )
		{
			m_fUBSpeedParametric += 2.0f * Asura_Timers::GetGameTimeSlice() / AVP_Vertebrate_AnimationHandler::GetUpperBodyAnimTweenTime();
			m_fUBSpeedParametric = Asura_Maths::Min( fSpeedParametric, m_fUBSpeedParametric );
		}
		else
		{
			m_fUBSpeedParametric -= 2.0f * Asura_Timers::GetGameTimeSlice() / AVP_Vertebrate_AnimationHandler::GetUpperBodyAnimTweenTime();
			m_fUBSpeedParametric = Asura_Maths::Max( fSpeedParametric, m_fUBSpeedParametric );
		}

		MovementSpeed eMoveSpeedLBSlow;
		MovementSpeed eMoveSpeedLBFast;
		float fBlendLBSpeed;
		if ( bCrouch )
		{
			eMoveSpeedLBSlow = MOVESPEED_IDLE;
			eMoveSpeedLBFast = MOVESPEED_SLOW;
			fBlendLBSpeed = Asura_Maths::ClampToRange( fSpeedParametric, 0.0f, 1.0f );
		}
		else if ( fSpeedParametric <= 0.5f )
		{
			eMoveSpeedLBSlow = MOVESPEED_IDLE;
			eMoveSpeedLBFast = MOVESPEED_SLOW;
			fBlendLBSpeed = Asura_Maths::ClampToRange( fSpeedParametric * 2.0f, 0.0f, 1.0f );
		}
		else if ( fSpeedParametric <= 1.0f )
		{
			eMoveSpeedLBSlow = MOVESPEED_SLOW;
			eMoveSpeedLBFast = MOVESPEED_MEDIUM;
			fBlendLBSpeed = Asura_Maths::ClampToRange( ( fSpeedParametric - 0.5f ) * 2.0f, 0.0f, 1.0f );
		}
		else // max is 2.0f
		{
			eMoveSpeedLBSlow = MOVESPEED_MEDIUM;
			eMoveSpeedLBFast = MOVESPEED_FAST;
			fBlendLBSpeed = Asura_Maths::ClampToRange( fSpeedParametric - 1.0f, 0.0f, 1.0f );
		}

		MovementSpeed eMoveSpeedUBSlow;
		MovementSpeed eMoveSpeedUBFast;
		float fBlendUBSpeed;
		if ( bCrouch )
		{
			eMoveSpeedUBSlow = MOVESPEED_IDLE;
			eMoveSpeedUBFast = MOVESPEED_SLOW;
			fBlendUBSpeed = Asura_Maths::ClampToRange( m_fUBSpeedParametric, 0.0f, 1.0f );
		}
		else if ( m_fUBSpeedParametric <= 0.5f )
		{
			eMoveSpeedUBSlow = MOVESPEED_IDLE;
			eMoveSpeedUBFast = MOVESPEED_SLOW;
			fBlendUBSpeed = Asura_Maths::ClampToRange( m_fUBSpeedParametric * 2.0f, 0.0f, 1.0f );
		}
		else if ( m_fUBSpeedParametric <= 1.0f )
		{
			eMoveSpeedUBSlow = MOVESPEED_SLOW;
			eMoveSpeedUBFast = MOVESPEED_MEDIUM;
			fBlendUBSpeed = Asura_Maths::ClampToRange( ( m_fUBSpeedParametric - 0.5f ) * 2.0f, 0.0f, 1.0f );
		}
		else // max is 2.0f
		{
			eMoveSpeedUBSlow = MOVESPEED_MEDIUM;
			eMoveSpeedUBFast = MOVESPEED_FAST;
			fBlendUBSpeed = Asura_Maths::ClampToRange( m_fUBSpeedParametric - 1.0f, 0.0f, 1.0f );
		}

		m_eBestMoveSpeed = ( fBlendLBSpeed < 0.5f ) ? eMoveSpeedLBSlow : eMoveSpeedLBFast;

		const AVP_Actor_AnimStateData* pxAnimLBSlow	= AVP_Human_PlayerAnimStateData::GetSingleton().GetAnimStateData( m_xOwnerActor.GetAnimStateBlueprintType(), uCurrentWeaponBPHash, bFirstPerson, bAiming, bCrouch, bJumping, bFalling || bLanding, eMoveSpeedLBSlow );
		const AVP_Actor_AnimStateData* pxAnimLBFast	= AVP_Human_PlayerAnimStateData::GetSingleton().GetAnimStateData( m_xOwnerActor.GetAnimStateBlueprintType(), uCurrentWeaponBPHash, bFirstPerson, bAiming, bCrouch, bJumping, bFalling || bLanding, eMoveSpeedLBFast );
		const AVP_Actor_AnimStateData* pxAnimUBSlow	= AVP_Human_PlayerAnimStateData::GetSingleton().GetAnimStateData( m_xOwnerActor.GetAnimStateBlueprintType(), uCurrentWeaponBPHash, bFirstPerson, bAiming, bCrouch, bJumping, bFalling || bLanding, eMoveSpeedUBSlow );
		const AVP_Actor_AnimStateData* pxAnimUBFast	= AVP_Human_PlayerAnimStateData::GetSingleton().GetAnimStateData( m_xOwnerActor.GetAnimStateBlueprintType(), uCurrentWeaponBPHash, bFirstPerson, bAiming, bCrouch, bJumping, bFalling || bLanding, eMoveSpeedUBFast );
	
		float fTweenTime = 0.0f;
		// We need to use a tween if we're starting falling or if we're finishing falling but not going into a landing.
		if ((bJumping && (m_eCurrentFallingState != FALLINGSTATE_JUMPING))
			|| (bFalling && (m_eCurrentFallingState != FALLINGSTATE_FALLING))
			|| (!bJumping && !bFalling && (m_eCurrentFallingState == FALLINGSTATE_JUMPING))
			|| (!bFalling && !bLanding && (m_eCurrentFallingState == FALLINGSTATE_FALLING)))
		{
			fTweenTime = dbg_fHumanFallTweenTime;
		}
		if (bJumping)
		{
			m_eCurrentFallingState = FALLINGSTATE_JUMPING;
		}
		else if (bFalling)
		{
			m_eCurrentFallingState = FALLINGSTATE_FALLING;
		}
		else if (bLanding)
		{
			m_eCurrentFallingState = FALLINGSTATE_LANDING;
		}
		else
		{
			m_eCurrentFallingState = FALLINGSTATE_NONE;
		}

		if ( bCrouch != m_bCurrentCrouch )
		{
			fTweenTime = dbg_fCrouchTweenTime;
		}
		m_bCurrentCrouch = bCrouch;

		if ( bAiming != m_bCurrentAim )
		{
			fTweenTime = dbg_fAimTweenTime;
		}
		m_bCurrentAim = bAiming;

		xHuman.SetFootSoundVolume( Asura_Maths::ClampToRange( fSpeedParametric, 0.0f, 1.0f ) );

		Asura_Assert( pxAnimLBSlow && pxAnimLBFast && pxAnimUBSlow && pxAnimUBFast, "One or more of these animation pointers are invalid"); 
		if ( pxAnimLBSlow && pxAnimLBFast && pxAnimUBSlow && pxAnimUBFast )
		{
			m_xOwnerActor.UpdateAnimStateData( pxAnimLBSlow, pxAnimLBFast, pxAnimUBSlow, pxAnimUBFast, !bJumping, fBlendLBSpeed, fBlendUBSpeed, GetMoveAngle(), fTweenTime, fTweenTime == 0.0f );

			Asura_Assert( ( pxAnimLBSlow->GetCollisionDescriptorHash() == pxAnimLBFast->GetCollisionDescriptorHash() ), "The collision descriptors should be the same" );
			m_xOwnerActor.SetCollisionDescriptor( pxAnimLBSlow->GetCollisionDescriptorHash() );
		}
	}
}

#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Human_PlayerAnimStateHandler::RenderDebugInfo( Asura_Vector_3& xOutputPos )
{
	PARENT::RenderDebugInfo( xOutputPos );
	
	Asura_Font_System::Print
	(
		xOutputPos.GetX(),
		xOutputPos.GetY(),
		0.6f,
		0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, 
		"Crouching State: %s", m_bCurrentCrouch ? "Crouching" : "Standing"
	);
	xOutputPos.y -= 10.0f;

	Asura_Font_System::Print
	(
		xOutputPos.GetX(),
		xOutputPos.GetY(),
		0.6f,
		0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, 
		"Aiming State: %s", m_bCurrentAim ? "Aiming" : "Neutral"
	);
	xOutputPos.y -= 10.0f;

	const AVP_Inventory_System* pxInvSys = m_xOwnerActor.GetInventorySystem();
	const AVP_Inventory_Group* pxWeaponGroup = pxInvSys != NULL ? pxInvSys->GetGroup( uAVP_INVENTORY_GROUPHASH_WEAPON ) : NULL;
	Asura_Assert( pxWeaponGroup != NULL, "Unable to get wepaon group" );
	if ( pxWeaponGroup != NULL )
	{

		Asura_Font_System::Print
		(
			xOutputPos.GetX(),
			xOutputPos.GetY(),
			0.6f,
			0xffffffff,
			ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, 
			"Weapon: 0x%08x", pxWeaponGroup->GetCurrentItem()
		);
		xOutputPos.y -= 10.0f;
	}
}
#endif
