
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_Actor_PlayerAnimStateHandler.cpp
// Author : Will Plano
// Date   : 25/05/2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header...
#include "Actor/AVP_Actor_PlayerAnimStateHandler.h"

#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Hierarchy.h"
#include "Core/Asura_Maths.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Logfile.h"
#include "Interface/Asura_CommandConsole.h"
#include "Rendering/Asura_Font_System.h"
#include "Rendering/Asura_Viewport.h"

#include "Actor/AVP_Actor_AnimStateData.h"
#include "Core/AVP_Core.h"
#include "Core/AVP_Game_Core.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"

DVSVAR float dvs_fMaxAcceleration = 10.0; // can accelerate up to 10 m s^{-1} in 1 sec
DVSVAR float dvs_fMaxAngularVelocity = 10.0f * Asura_Maths::Deg180; // takes 0.1 s to change 180 degrees

#ifndef ASURA_NO_DEBUG_OUTPUT
static Asura_Guid dbg_uRenderSpeedOutputGuid = ASURA_GUID_UNREGISTERED;
DVSVAR bool dvs_bRenderDebugInfo = false;
#endif

DEBUGVAR float dbg_fMinSpeedForAngle = 0.1f;

#if defined(ASURA_SUPPORT_COMMAND_CONSOLE)
u_int AVP_Actor_PlayerAnimStateHandler::s_uConsoleVarReferenceCount = 0;
#endif

AVP_Actor_PlayerAnimStateHandler::AVP_Actor_PlayerAnimStateHandler( AVP_ClientEntity_Actor& xClient )
: m_xOwnerActor( xClient )
, m_pxSpeedInterpolator( NULL )
, m_pxAngleInterpolator( NULL )
, m_fSpeedParametric( 0.0f )
, m_fMoveAngle( 0.0f )
{
	if ( !AVP_Game_Core::IsLocalPlayer( xClient.GetGuid() ) )
	{
		m_pxSpeedInterpolator = new AVP_Interpolator< float >( 0.0f );
		m_pxAngleInterpolator = new AVP_Angle_Interpolator( 0.0f );
	}

#if defined(ASURA_DEBUG_VARIABLE_SYSTEM)
	if ( s_uConsoleVarReferenceCount == 0 )
	{
		Asura_CommandConsole::AddVar( L"AVP.PlayerAnimStateHandler.MaxAcceleration", &dvs_fMaxAcceleration, 0, Asura_Maths::LargeFloat, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
		Asura_CommandConsole::AddVar( L"AVP.PlayerAnimStateHandler.MaxAngularVelocity", &dvs_fMaxAngularVelocity, 0, Asura_Maths::LargeFloat, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );

#ifndef ASURA_NO_DEBUG_OUTPUT
		Asura_CommandConsole::AddVar(L"AVP.States.RenderPlayerDebugInfo", &dvs_bRenderDebugInfo, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif
	}
	++s_uConsoleVarReferenceCount;
#endif // ASURA_SUPPORT_COMMAND_CONSOLE
}

AVP_Actor_PlayerAnimStateHandler::~AVP_Actor_PlayerAnimStateHandler()
{
	delete m_pxSpeedInterpolator;
	delete m_pxAngleInterpolator;

#if defined(ASURA_SUPPORT_COMMAND_CONSOLE)
	--s_uConsoleVarReferenceCount;
	if ( s_uConsoleVarReferenceCount == 0 )
	{
		Asura_CommandConsole::RemoveVar( L"AVP.PlayerAnimStateHandler.MaxAcceleration" );
		Asura_CommandConsole::RemoveVar( L"AVP.PlayerAnimStateHandler.MaxAngularVelocity" );

#ifndef ASURA_NO_DEBUG_OUTPUT
	Asura_CommandConsole::RemoveVar(L"AVP.States.RenderPlayerDebugInfo" ); 
#endif
	}
#endif // ASURA_SUPPORT_COMMAND_CONSOLE
}

void AVP_Actor_PlayerAnimStateHandler::Update()
{
#ifndef ASURA_NO_DEBUG_OUTPUT
	if ( dvs_bRenderDebugInfo )
	{
		Asura_Vector_3 xOutputPos;
		if ( AVP_Game_Core::IsLocalPlayer( m_xOwnerActor.GetGuid() ) )
		{
			xOutputPos = Asura_Vector_3( 0.5f * Asura_Viewport::GetWidth(), 0.8f * Asura_Viewport::GetHeight(), 0.0f );
		}
		else
		{
			xOutputPos = m_xOwnerActor.GetCurrentPosition();
			Asura_Camera::ProjectWorldCoordsToScreenSpace( xOutputPos, true );
			xOutputPos.y -= 20.0f;
		}
		if ( xOutputPos.z >= 0.0f )
		{
			const Asura_Handle iFontHandle = AVP_Core::GetFontHandle( AVP_Core::HUD_FONT );
			Asura_Font_System::SetFont( iFontHandle );
			RenderDebugInfo( xOutputPos );
		}
	}
#endif
}

void AVP_Actor_PlayerAnimStateHandler::UpdateMovementParametrics( const float fTargetSpeedParametric, float fTargetAngle )
{
	if ( m_pxSpeedInterpolator )
	{
		m_pxSpeedInterpolator->Update( fTargetSpeedParametric );
	}
	else
	{
		if ( m_fSpeedParametric < dbg_fMinSpeedForAngle)
		{
			m_fMoveAngle = fTargetAngle;
		}
		else if ( fTargetSpeedParametric < dbg_fMinSpeedForAngle )
		{
			fTargetAngle = m_fMoveAngle;
		}

		float fAccln = fTargetSpeedParametric - m_fSpeedParametric;
		if ( fAccln < 0.0f )
		{
			fAccln = Asura_Maths::Max( fAccln, -dvs_fMaxAcceleration * Asura_Timers::GetGameTimeSlice() );
		}
		else
		{
			fAccln = Asura_Maths::Min( fAccln, dvs_fMaxAcceleration * Asura_Timers::GetGameTimeSlice() );
		}
		m_fSpeedParametric += fAccln;
	}

	if ( m_pxAngleInterpolator )
	{
		m_pxAngleInterpolator->Update( fTargetAngle );
	}
	else
	{
		float fAngVel = Asura_Maths::RadiansToZeroCentredRange( fTargetAngle - m_fMoveAngle );
		if ( fAngVel < 0.0f )
		{
			fAngVel = Asura_Maths::Max( fAngVel, -dvs_fMaxAngularVelocity * Asura_Timers::GetGameTimeSlice() );
		}
		else
		{
			fAngVel = Asura_Maths::Min( fAngVel, dvs_fMaxAngularVelocity * Asura_Timers::GetGameTimeSlice() );
		}
		m_fMoveAngle = Asura_Maths::RadiansToZeroMinimumRange( m_fMoveAngle + fAngVel );
	}

#ifndef ASURA_NO_DEBUG_OUTPUT
	if ( dbg_uRenderSpeedOutputGuid == m_xOwnerActor.GetGuid() )
	{
		Asura_OutputToDebugger_Retail( ( "Target speed: %01.2f Current speed: %01.2f Time: %.4f\n", fTargetSpeedParametric, GetSpeedParametric(), Asura_Timers::GetServerAbsoluteTime() ) );
		Asura_OutputToDebugger_Retail( ( "Target angle: %03d  Current angle: %03d  Time: %.4f\n", Asura_Maths::FloatToInt_Near( Asura_Maths::RadToDeg( fTargetAngle ) ), Asura_Maths::FloatToInt_Near( Asura_Maths::RadToDeg( GetMoveAngle() ) ), Asura_Timers::GetServerAbsoluteTime() ) );
	}
#endif
}

float AVP_Actor_PlayerAnimStateHandler::GetSpeedParametric() const
{
	if ( m_pxSpeedInterpolator )
	{
		m_pxSpeedInterpolator->GetCurrent( m_fSpeedParametric );
	}
#ifndef ASURA_NO_DEBUG_OUTPUT
	if ( dbg_uRenderSpeedOutputGuid == m_xOwnerActor.GetGuid() )
	{
		Asura_OutputToDebugger_Retail( ( "Target speed: ---- Current speed: %01.2f Time: %.4f\n", m_fSpeedParametric, Asura_Timers::GetServerAbsoluteTime() ) );
	}
#endif
	return m_fSpeedParametric;
}

float AVP_Actor_PlayerAnimStateHandler::GetMoveAngle() const
{
	if ( m_pxAngleInterpolator )
	{
		m_pxAngleInterpolator->GetCurrent( m_fMoveAngle );
		m_fMoveAngle = Asura_Maths::RadiansToZeroMinimumRange( m_fMoveAngle );
	}
#ifndef ASURA_NO_DEBUG_OUTPUT
	if ( dbg_uRenderSpeedOutputGuid == m_xOwnerActor.GetGuid() )
	{
		Asura_OutputToDebugger_Retail( ( "Target angle: ---  Current angle: %03d  Time: %.4f\n", Asura_Maths::FloatToInt_Near( Asura_Maths::RadToDeg( m_fMoveAngle ) ), Asura_Timers::GetServerAbsoluteTime() ) );
	}
#endif
	return m_fMoveAngle;
}

#ifndef ASURA_NO_DEBUG_OUTPUT
void AVP_Actor_PlayerAnimStateHandler::RenderDebugInfo( Asura_Vector_3& xOutputPos )
{
	Asura_Font_System::Print
	(
		xOutputPos.GetX(),
		xOutputPos.GetY(),
		0.6f,
		0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, 
		"Speed: %1.2f", GetSpeedParametric()
	);
	xOutputPos.y -= 10.0f;

	Asura_Font_System::Print
	(
		xOutputPos.GetX(),
		xOutputPos.GetY(),
		0.6f,
		0xffffffff,
		ASURA_TEXTFORMAT_CENTREJUSTIFIED | ASURA_TEXTFORMAT_FORMATSTRING, 
		"Angle: %3.1f", Asura_Maths::RadToDeg( GetMoveAngle() )
	);
	xOutputPos.y -= 10.0f;
}
#endif

bool AVP_Actor_PlayerAnimStateHandler::GetSpeedFromDirectionalStateData( const AVP_Actor_AnimStateData* pxStateData, float& fSpeed )
{
	bool bRet = false;
	if ( pxStateData && pxStateData->GetNumBlendRegions() > 0 )
	{
		const AVP_Actor_AnimStateData_Directional_Region* pxRegion = pxStateData->GetBlendRegion( 0 );
		if ( pxRegion && pxRegion->GetNumAnims() > 0 )
		{
			const AVP_Actor_AnimStateData* pxAnimData = pxRegion->GetAnimData( 0 );
			if ( pxAnimData )
			{
				Asura_Hash_ID uAnim = pxAnimData->GetAnimHash();
				const Asura_Hierarchy_Anim* pxAnim = Asura_Animation_System::GetAnimByHash( uAnim );
				if ( pxAnim )
				{
					Asura_Vector_3 xVel;
					if ( pxAnim->GetVelocity( NULL, 0.5f, pxAnim->GetDuration(), &xVel ) )
					{
						bRet = true;
						fSpeed = xVel.Magnitude();
					}
				}
			}
		}
	}
	return bRet;
}

/// End of file. ///
 