
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_Actor_FidgetAnimHandler.cpp
// Author : Emil Popovic
// Date   : 30-Oct-2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

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

// Asura independent header-files...
#include "Animation/Asura_AnimationController_Animation.h"
#include "Debugging/Asura_DebugLineRender.h"
#include "Interface/Asura_CommandConsole.h"
#include "Network/Asura_Network.h"

// Project independent header-files...
#include "Actor/AVP_Actor_AnimationHandler.h"
#include "Actor/AVP_Actor_InputHandler.h"
#include "Actor/Vertebrates/Human/AVP_Human_AIAnimStateData.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_Maths.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_Actor_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"
#include "Entity/PhysicalObject/Interact/AVP_ClientEntity_InteractObject.h"
#include "Entity/Triggers/AVP_ClientEntity_InteractionTrigger.h"
#include "Inventory/AVP_Inventory_Item.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Moves/AVP_Movement_System.h"
#include "Moves/AVP_Moves.h"
#include "Moves/AVP_Move_Interaction.h"

// AVP Axon
#include "Core/BrainTypes/AVP_Brain_Actor_Marine.h"

////////////////////////////////////////////////////////////////////////////////
// Constant declarations...

const float g_fDefaultTimeBetweenAnimationsMin = 5.0f;
const float g_fDefaultTimeBetweenAnimationsMax = 15.0f;

////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////


class AVP_Blueprint_ParameterEnumerator_IdleAnimSelector : public Asura_Blueprint_ParameterEnumerator
{
	typedef Asura_Blueprint_ParameterEnumerator PARENT;

public:
	AVP_Blueprint_ParameterEnumerator_IdleAnimSelector();

	Asura_Hash_ID SelectRandomAnim() const;

	virtual void BeginEnumeration();
	virtual bool Enumerate(const u_int uIndex, char const* const szName, const Asura_Value& xValue);
	virtual void EndEnumeration(const bool bSuccess);

protected:
	enum { MAX_IDLE_ANIMS = 20, };

	Asura_Hash_ID m_auIdleAnims[MAX_IDLE_ANIMS];
	u_int m_uNumIdleAnims;
};




AVP_Blueprint_ParameterEnumerator_IdleAnimSelector::AVP_Blueprint_ParameterEnumerator_IdleAnimSelector()
: PARENT()
, m_uNumIdleAnims(0)
{
}

Asura_Hash_ID AVP_Blueprint_ParameterEnumerator_IdleAnimSelector::SelectRandomAnim() const
{
	if (m_uNumIdleAnims == 0) { return ASURA_HASH_ID_UNSET; }

	const u_int uIndex = Asura_Random::GetIntU(0, m_uNumIdleAnims-1);
	return m_auIdleAnims[uIndex];
}

void AVP_Blueprint_ParameterEnumerator_IdleAnimSelector::BeginEnumeration()
{
	m_uNumIdleAnims = 0;
}

bool AVP_Blueprint_ParameterEnumerator_IdleAnimSelector::Enumerate(const u_int uIndex, char const* const szName, const Asura_Value& xValue)
{
	if (m_uNumIdleAnims >= MAX_IDLE_ANIMS)
	{
		Asura_Assert(false, "Too many fidget idle anims, ignoring the rest.");
		return false;
	}

	m_auIdleAnims[m_uNumIdleAnims++] = xValue.GetHashIDValue();
	return true;
}

void AVP_Blueprint_ParameterEnumerator_IdleAnimSelector::EndEnumeration(const bool bSuccess)
{
	// Nothing to do here, as the caller needs to call the selection function.
	// Success is irrelevant, as we should still have a valid array of animations, we just happened to find more than we could buffer up.
}


//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////


AVP_Actor_FidgetAnimHandler::AVP_Actor_FidgetAnimHandler( AVP_ClientEntity_Actor& xOwner )
: m_xOwner( xOwner )
, m_uLastFrameFidgetBlueprintHash( ASURA_HASH_ID_UNSET )
, m_uCurrentPlayedAnimControlerHash( ASURA_HASH_ID_UNSET )
, m_fTimeBetweenAnimationsMin( g_fDefaultTimeBetweenAnimationsMin )
, m_fTimeBetweenAnimationsMax( g_fDefaultTimeBetweenAnimationsMax )
, m_fFidgetTimer( 0.0f )
{
	// Set it to random, so we can wait some time until we start to play some random animation.
	m_fTimeToStartNextFidget = Asura_Random::GetFloat( m_fTimeBetweenAnimationsMin, m_fTimeBetweenAnimationsMax );
}

AVP_Actor_FidgetAnimHandler::~AVP_Actor_FidgetAnimHandler()
{
}


void AVP_Actor_FidgetAnimHandler::Update()
{
	Asura_Hash_ID uFidgetBlueprintHash = GetFidgetBlueprintHash();


	AVP_Actor_AnimationHandler*				pxAnimationHandler	= m_xOwner.GetAnimationHandler();
	Asura_Animation*						pxAnimation			= pxAnimationHandler != NULL ? pxAnimationHandler->GetAnimation() : NULL;
	Asura_AnimationController_Animation*	pxAACA				= pxAnimation != NULL && m_uCurrentPlayedAnimControlerHash != ASURA_HASH_ID_UNSET ? pxAnimation->GetControllerAnimationFromHash( m_uCurrentPlayedAnimControlerHash ) : NULL;



	// Check if the current fidget is the same as the last one. If they differ, that means, state has been changed
	// and new animation need to be triggered. (Old one stop.)
	if( m_uLastFrameFidgetBlueprintHash!=uFidgetBlueprintHash )
	{
		// Tween out currently played fidget. Calling stop is enough, because the controller will then automatically destroy the object itself.
		if( pxAACA && pxAACA->GetTimeUntilFinished() > DEFAULT_TWEEN_TIME )
		{
			pxAACA->SetParametricTargetTime( pxAACA->GetParametricTimer() + ( DEFAULT_TWEEN_TIME / pxAACA->GetDuration() ) );
		}

		// Reset fidget timer
		m_fFidgetTimer = 0;

		// Store it for later usage.
		m_uLastFrameFidgetBlueprintHash = uFidgetBlueprintHash;
	}

	bool bIsIdle = false;

	if ( m_xOwner.IsPlayer() )
	{
		bIsIdle = m_xOwner.IsIdleForFidget();
	}
	else if ( !m_xOwner.IsADeadBody() )
	{
		AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity( m_xOwner.GetGuid() );

		if ( pxActor && pxActor->GetBrain() )
		{
			bIsIdle = pxActor->GetActorBrain()->CanFidget();
		}
	}

	// Don't play fidget animation instantly. Wait awhile and them fire the animation.
	if ( bIsIdle )
	{
		if ( !pxAACA )
		{
			m_fFidgetTimer += Asura_Timers::GetGameTimeSlice();
		}
	}
	else
	{
		ResetFidgetTimer();
	}
	
	if ( !pxAACA && m_fFidgetTimer > m_fTimeToStartNextFidget )
	{
		Asura_Hash_ID uAnimStateBPTypeHash = m_xOwner.GetAnimStateBlueprintType();

		// start to play new one.
		Asura_Blueprint* pxFidgetBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAnimStateBPTypeHash, uFidgetBlueprintHash );
		PlayRandomFidgetAnim( pxFidgetBlueprint );
	}
}

void AVP_Actor_FidgetAnimHandler::ResetFidgetTimer()
{
	// Tween out the currently playing fidget.
	AVP_Actor_AnimationHandler*				pxAnimationHandler	= m_xOwner.GetAnimationHandler();
	Asura_Animation*						pxAnimation			= pxAnimationHandler != NULL ? pxAnimationHandler->GetAnimation() : NULL;
	Asura_AnimationController_Animation*	pxAACA				= pxAnimation != NULL && m_uCurrentPlayedAnimControlerHash != ASURA_HASH_ID_UNSET ? pxAnimation->GetControllerAnimationFromHash( m_uCurrentPlayedAnimControlerHash ) : NULL;
	if( pxAACA && pxAACA->GetTimeUntilFinished() > DEFAULT_TWEEN_TIME )
	{
		pxAACA->SetParametricTargetTime( pxAACA->GetParametricTimer() + ( DEFAULT_TWEEN_TIME / pxAACA->GetDuration() ) );
	}
	m_fFidgetTimer = 0.0f;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void AVP_Actor_FidgetAnimHandler::PlayRandomFidgetAnim( Asura_Blueprint* pxFidgetBlueprint )
{
	if( pxFidgetBlueprint )
	{
		// Enumerate all anims that are in blueprint. So I can choose one of them.
		AVP_Blueprint_ParameterEnumerator_IdleAnimSelector xSelector;

		pxFidgetBlueprint->EnumerateParameters(szAVP_BLUEPRINT_ENUMERATIONNAME_IDLEANIM_NAME_ANIM, 2, xSelector);

		Asura_Hash_ID uRandomSelectedAnim = xSelector.SelectRandomAnim();

		if( uRandomSelectedAnim!=ASURA_HASH_ID_UNSET )
		{
			const AVP_Actor_AnimationHandler* pxAnimHandler = m_xOwner.GetAnimationHandler();

			if( pxAnimHandler )
			{
				u_int uAnimFlags = AAC_FLAG_ACTIVE | AAC_FLAG_DESTROY_WHEN_FINISHED;

				Asura_AnimationBlend* pxBlend = new Asura_AnimationBlend_AnimatedTween( DEFAULT_TWEEN_TIME, DEFAULT_TWEEN_TIME, false );

				m_uCurrentPlayedAnimControlerHash = pxAnimHandler->PlayAnimation( uAnimFlags, AVP_AAPC_FIDGET_ANIM_GROUP, uRandomSelectedAnim, pxBlend );
			}
		}

		// Set it to random, so we can wait some time until we start to play some random animation.
		Asura_Value xVal;
		if ( pxFidgetBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_FIDGETMINTIME, xVal ) )
		{
			m_fTimeBetweenAnimationsMin = xVal.GetFloatValue();
		}
		else
		{
			m_fTimeBetweenAnimationsMin = g_fDefaultTimeBetweenAnimationsMin;
		}

		if ( pxFidgetBlueprint->GetParameterValueByHashID( uAVP_BLUEPRINT_PARAM_FIDGETMAXTIME, xVal ) )
		{
			m_fTimeBetweenAnimationsMax = xVal.GetFloatValue();
		}
		else
		{
			m_fTimeBetweenAnimationsMax = g_fDefaultTimeBetweenAnimationsMax;
		}

		m_fTimeToStartNextFidget = Asura_Random::GetFloat( m_fTimeBetweenAnimationsMin, m_fTimeBetweenAnimationsMax );
		m_fFidgetTimer = 0;
	}
}

Asura_Hash_ID AVP_Actor_FidgetAnimHandler::GetFidgetBlueprintHash()
{
	const AVP_Actor_AnimStateData* pxStateData = m_xOwner.GetCurrentAnimStateData();
	if ( pxStateData )
	{
		return pxStateData->GetFidgetBlueprintHash();
	}

	return ASURA_HASH_ID_UNSET;
}


/// End of file. ///


