
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_FacialAnimation_AnimationHandler.cpp
// Author : Andy Tate
// Date   : 22-May-2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

#if defined(ASURA_USE_FACIAL_ANIMATION_SYSTEM)

// This file's header...
#include "FacialAnimation/Asura_FacialAnimation_AnimationHandler.h"

// Asura independent header-files...
#include "Animation/Asura_Animation.h"
#include "Debugging/Asura_Logfile.h"
#include "FacialAnimation/Asura_FacialAnimation_BoneDataModifier.h"
#include "FacialAnimation/Asura_FacialAnimation_Face.h"
#include "FacialAnimation/Asura_FacialAnimation_System.h"
#if defined(ASURA_USE_AUDIO_SYSTEM)
#include "Sound/Asura_Audio_System.h"
#include "Sound/Asura_Audio_Voice.h"
#else
#include "Sound/Asura_Sound.h"
#endif	//defined(ASURA_USE_AUDIO_SYSTEM)
#include "Sound/Asura_Dialogue.h"

#if defined(ASURA_TOOLS)
// Tools header-files...
#include "FacialAnimation/Tools_FacialAnimation_System.h"
#endif	//defined(ASURA_TOOLS)


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

#if 1
#define LOG_OUTPUT(s)
#else
#define LOG_OUTPUT(s)	Asura_OutputToDebugger_Retail(s)
#endif


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


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::Asura_FacialAnimation_AnimationHandler
// Author       : Andy Tate
// Date         : 22-May-2008
////////////////////////////////////////////////////////////////////////////////

Asura_FacialAnimation_AnimationHandler::Asura_FacialAnimation_AnimationHandler(const Asura_Hash_ID uFaceHash,
																			   const char* szAnimationGroupName,
																			   const u_int uAnimationGroupPriority,
																			   const Asura_Hash_ID uParentAnimationGroupHash)
: m_uFaceHash(uFaceHash)
, m_pxBoneDataModifier(NULL)
, m_szAnimationGroupName(szAnimationGroupName)
, m_uAnimationGroupHash(Asura_GetHashID(szAnimationGroupName))
, m_uAnimationGroupPriority(uAnimationGroupPriority)
, m_uParentAnimationGroupHash(uParentAnimationGroupHash)
, m_uNumMuscleGroupAnimations(0)
, m_pfMuscleGroupAnimationWeights(NULL)
, m_pfMuscleGroupAnimationBalances(NULL)
, m_pfAnimationEyeValues(NULL)
, m_bActive(false)
, m_fExternalEyeLeftRight(0.0f)
, m_fExternalEyeUpDown(0.0f)
, m_fExternalEyeFocus(0.0f)
, m_fExternalEyeClose(0.0f)
, m_fExternalEyeSquint(0.0f)
, m_fExternalEyeBalance(0.0f)
{
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::Asura_FacialAnimation_AnimationHandler'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::~Asura_FacialAnimation_AnimationHandler
// Author       : Andy Tate
// Date         : 22-May-2008
////////////////////////////////////////////////////////////////////////////////

Asura_FacialAnimation_AnimationHandler::~Asura_FacialAnimation_AnimationHandler()
{
	delete m_pxBoneDataModifier;
	delete[](m_pfMuscleGroupAnimationWeights);
	delete[](m_pfMuscleGroupAnimationBalances);
	delete[](m_pfAnimationEyeValues);
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::~Asura_FacialAnimation_AnimationHandler'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::OnActivation
// Author       : Andy Tate
// Date         : 29-Aug-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_AnimationHandler::OnActivation(void)
{
	ZeroWeightsAndBalances();
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::OnActivation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::OnDeactivation
// Author       : Andy Tate
// Date         : 29-Aug-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_AnimationHandler::OnDeactivation(void)
{
#if defined(ASURA_TOOLS)
	// Need to ensure that we reset the data displayed in the overlays...
	ZeroWeightsAndBalances();
#endif	//defined(ASURA_TOOLS)
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::OnDeactivation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::PlayFaceAnimation
// Author       : Andy Tate
// Date         : 26-Aug-2008
////////////////////////////////////////////////////////////////////////////////

bool Asura_FacialAnimation_AnimationHandler::PlayFaceAnimation(const Asura_Hash_ID uFaceAnimationHash, bool bLooped /*= false*/)
{
	// Do we actually have a face-animation?
	if(uFaceAnimationHash == ASURA_HASH_ID_UNSET)
	{
		return(false);
	}
	else
	{
		Asura_FacialAnimation_FaceAnimationInstance*	pxFaceAnimationInstance	= m_xFaceAnimationInstanceContainer.GetDataByHash(uFaceAnimationHash);

		if(pxFaceAnimationInstance == NULL)
		{
			const Asura_FacialAnimation_FaceAnimation*	pxFaceAnimation	= Asura_FacialAnimation_System::FindFaceAnimation(uFaceAnimationHash);

			if(pxFaceAnimation == NULL)
			{
				Asura_Assert(false, "Failed to find face-animation to be played.");
				return(false);
			}
			else
			{
				pxFaceAnimationInstance	= m_xFaceAnimationInstanceContainer.AddNode(uFaceAnimationHash);

				if(pxFaceAnimationInstance == NULL)
				{
					return(false);
				}
				else
				{
					pxFaceAnimationInstance->Initialise(uFaceAnimationHash, bLooped);
					return(true);
				}
			}
		}
		else
		{
			return(false);
		}
	}
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::PlayFaceAnimation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::PlayFaceAnimation
// Author       : Andy Tate
// Date         : 26-Aug-2008
////////////////////////////////////////////////////////////////////////////////

bool Asura_FacialAnimation_AnimationHandler::PlayFaceAnimation(const Asura_Dialogue_Line& xDialogueLine,
															   Asura_Handle& iSoundHandle)
{
#if defined(ASURA_TOOLS)
	const char*									szSoundFilename			= xDialogueLine.GetSoundFilename();
	const Asura_String							xFaceAnimationName		= Tools_FacialAnimation_System::ConvertSoundFilenameToFaceAnimationName(szSoundFilename);
	const Tools_FacialAnimation_FaceAnimation*	pxToolsFaceAnimation	= Tools_FacialAnimation_System::FindOrLoadAnimation(xFaceAnimationName);

	if(pxToolsFaceAnimation == NULL)
	{
		Asura_Assertf(false, ("Failed to find face-animation to be played ('%s').", xFaceAnimationName.GetString()));
		return(false);
	}
	else
	{
		const Asura_Hash_ID	uFaceAnimationHash	= xFaceAnimationName.GetHash();
#else
	{
		const Asura_Hash_ID	uFaceAnimationHash	= xDialogueLine.GetFaceAnimationHash();
#endif	//defined(ASURA_TOOLS)

		// Do we actually have a face-animation for this line of dialogue?
		if(uFaceAnimationHash == ASURA_HASH_ID_UNSET)
		{
			return(false);
		}
		else
		{
			Asura_FacialAnimation_FaceAnimationInstance*	pxFaceAnimationInstance	= m_xFaceAnimationInstanceContainer.GetDataByHash(uFaceAnimationHash);

			if(pxFaceAnimationInstance == NULL)
			{
				pxFaceAnimationInstance	= m_xFaceAnimationInstanceContainer.AddNode(uFaceAnimationHash);

				if(pxFaceAnimationInstance == NULL)
				{
					return(false);
				}
				else
				{
					const Asura_Hash_ID	uSoundHash	= xDialogueLine.GetSoundHash();

					pxFaceAnimationInstance->Initialise(uFaceAnimationHash, iSoundHandle, uSoundHash);
					return(true);
				}
			}
			else
			{
				return(false);
			}
		}
	}
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::PlayFaceAnimation'. ///

void Asura_FacialAnimation_AnimationHandler::SetExternalEyeData( const float fLeftRight, const float fUpDown, const float fFocus, const float fClose, const float fSquint, const float fBalance )
{
	m_fExternalEyeLeftRight	= fLeftRight;
	m_fExternalEyeUpDown	= fUpDown;
	m_fExternalEyeFocus		= fFocus;
	m_fExternalEyeClose		= fClose;
	m_fExternalEyeSquint	= fSquint;
	m_fExternalEyeBalance	= fBalance;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::ZeroWeightsAndBalances
// Author       : Andy Tate
// Date         : 23-May-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_AnimationHandler::ZeroWeightsAndBalances(void)
{
	if(m_pfMuscleGroupAnimationWeights != NULL)
	{
		for(u_int u = 0; u < m_uNumMuscleGroupAnimations; u++)
		{
			m_pfMuscleGroupAnimationWeights[u]	= 0.0f;
		}
	}

	if(m_pfMuscleGroupAnimationBalances != NULL)
	{
		for(u_int u = 0; u < m_uNumMuscleGroupAnimations; u++)
		{
			m_pfMuscleGroupAnimationBalances[u]	= 0.0f;
		}
	}
	if(m_pfAnimationEyeValues)
	{
		for(u_int u=0; u<ASURA_FACIALANIMATION_EYE_NUMPARAM; u++)
		{
			m_pfAnimationEyeValues[u] = 0.0f;
		}
		m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_TARGETMIX] = 1.0f;
		m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_SQUINT] = 1.0f;
	}
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::ZeroWeightsAndBalances'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::SetActive
// Author       : Andy Tate
// Date         : 29-Aug-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_AnimationHandler::SetActive(const bool bActive)
{
	if(!m_bActive && bActive)
	{
		OnActivation();
	}
	else if(m_bActive && !bActive)
	{
		OnDeactivation();
	}

	m_bActive	= bActive;
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::SetActive'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::CreateAnimationGroup
// Author       : Andy Tate
// Date         : 22-May-2008
////////////////////////////////////////////////////////////////////////////////

Asura_AnimationController_Group* Asura_FacialAnimation_AnimationHandler::CreateAnimationGroup(Asura_Animation& xAnimation)
{
	const Asura_FacialAnimation_Face*	pxFace	= Asura_FacialAnimation_System::FindFace(m_uFaceHash);

	if(pxFace == NULL)
	{
		Asura_Assert(false, "Failed to find the face to use for facial-animation.");
		return(NULL);
	}
	else
	{
		Asura_AnimationController_Group*	pxNewGroup	= pxFace->CreateAnimationGroup(m_szAnimationGroupName, m_uAnimationGroupPriority, m_uParentAnimationGroupHash, xAnimation);

		if(pxNewGroup == NULL)
		{
			return(NULL);
		}
		else
		{
			m_uNumMuscleGroupAnimations			= pxFace->CalculateTotalNumMuscleGroupAnimations();
			m_pfMuscleGroupAnimationWeights		= new float [m_uNumMuscleGroupAnimations];
			m_pfMuscleGroupAnimationBalances	= new float [m_uNumMuscleGroupAnimations];
			if(pxFace->GetNumEyeBones())
			{
				m_pfAnimationEyeValues = new float[ASURA_FACIALANIMATION_EYE_NUMPARAM];
			}
			m_bActive							= true;

			ZeroWeightsAndBalances();
			return(pxNewGroup);
		}
	}
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::CreateAnimationGroup'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::DestroyAnimationGroup
// Author       : Andy Tate
// Date         : 22-May-2008
////////////////////////////////////////////////////////////////////////////////

bool Asura_FacialAnimation_AnimationHandler::DestroyAnimationGroup(Asura_Animation& xAnimation)
{
	m_bActive	= false;
	return(xAnimation.DestroyController(m_uAnimationGroupHash));
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::DestroyAnimationGroup'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::UpdateAnimation
// Author       : Andy Tate
// Date         : 22-May-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_AnimationHandler::UpdateAnimation(const float fTimeStep,
															 Asura_Animation& xAnimation)
{
	const Asura_FacialAnimation_Face*	pxFace	= Asura_FacialAnimation_System::FindFace(m_uFaceHash);

	if(pxFace == NULL)
	{
		Asura_Assert(false, "Failed to find the face to use for facial-animation.");
	}
	else
	{
		Asura_AnimationController_Group*	pxFacialAnimationGroup	= xAnimation.GetControllerGroupFromHash(m_uAnimationGroupHash);


		// Do we need to create our animation-group?
		if(pxFacialAnimationGroup == NULL)
		{
			pxFacialAnimationGroup	= CreateAnimationGroup(xAnimation);
		}

		// Do we have data to update?
		if(pxFacialAnimationGroup == NULL)
		{
			Asura_Assert(false, "Failed to find animation-group for this facial-animation.");
		}
		else
		{
			const bool	bIsGroupActive	= pxFacialAnimationGroup->IsActive();

			if(m_bActive)
			{
				const u_int	uNumFaceAnimationInstances	= m_xFaceAnimationInstanceContainer.GetNumberOfNodes();

				// Ensure that our controller-group is active...
				if(!bIsGroupActive)
				{
					pxFacialAnimationGroup->Activate();
				}

				if(uNumFaceAnimationInstances > 0)
				{
					// If we've got animations playing we need to ensure that we start off with blank weights/balances...
					ZeroWeightsAndBalances();

					// Update the muscle-group weights based on any face-animation currently playing...
					for(FaceAnimationInstanceIterator xIterator(m_xFaceAnimationInstanceContainer); !xIterator.Done(); xIterator.Next())
					{
						Asura_FacialAnimation_FaceAnimationInstance*	pxInstance	= xIterator.GetCurrent();

						if(pxInstance->Update(fTimeStep, m_pfMuscleGroupAnimationWeights, m_pfMuscleGroupAnimationBalances, m_pfAnimationEyeValues))
						{
							LOG_OUTPUT(("Time = %.3f:", pxInstance->GetTimePlayed()));

							for(u_int u = 0; u < m_uNumMuscleGroupAnimations; u++)
							{
								LOG_OUTPUT(("\t(%.3f/%.3f)", m_pfMuscleGroupAnimationWeights[u], m_pfMuscleGroupAnimationBalances[u]));
							}

							LOG_OUTPUT(("\n"));
						}
						else
						{
							const Asura_Hash_ID	uHash	= xIterator.GetCurrentHash();

							m_xFaceAnimationInstanceContainer.DeleteNodeByHash(uHash);
						}
					}
				}

				// Update the "Asura_Animation" itself...
				pxFace->UpdateAnimation(m_pfMuscleGroupAnimationWeights, m_pfMuscleGroupAnimationBalances, xAnimation, *pxFacialAnimationGroup);
			}
			else
			{
				// Ensure that our controller-group is inactive...
				if(bIsGroupActive)
				{
					pxFacialAnimationGroup->Deactivate();
				}
			}
		}

		if ( !m_pxBoneDataModifier )
		{
			m_pxBoneDataModifier = new Asura_FacialAnimation_BoneDataModifier( &xAnimation, m_uFaceHash );
		}

		//Blend the eye values from the animations with the external ones.
		if(m_pfAnimationEyeValues)
		{
			float fTargetMix = m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_TARGETMIX];
			
			//The first 3 are mixed using the target mix
			float fEyeLeftRight = Asura_Maths::Lerp(m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_LEFTRIGHT], m_fExternalEyeLeftRight, fTargetMix);	
			float fEyeUpDown = Asura_Maths::Lerp(m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_UPDOWN], m_fExternalEyeUpDown, fTargetMix);	
			float fEyeFocus = Asura_Maths::Lerp(m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_FOCUS], m_fExternalEyeFocus, fTargetMix);
			
			//For close and balance take the value with the highest magnitude
			float fEyeClose;
			if(Asura_Maths::Abs(m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_CLOSE]) > Asura_Maths::Abs(m_fExternalEyeClose))
			{
				fEyeClose = m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_CLOSE];
			}
			else
			{
				fEyeClose = m_fExternalEyeClose;
			}

			float fEyeBalance;
			if(Asura_Maths::Abs(m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_BALANCE]) > Asura_Maths::Abs(m_fExternalEyeBalance))
			{
				fEyeBalance = m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_BALANCE];
			}
			else
			{
				fEyeBalance = m_fExternalEyeBalance;
			}

			//And for squint take the minimum , since its a bit odd like that
			float fEyeSquint = Asura_Maths::Min(m_pfAnimationEyeValues[ASURA_FACIALANIMATION_EYE_SQUINT], m_fExternalEyeSquint);

			m_pxBoneDataModifier->SetAnimatedParameters( 	1.0f, 
															fEyeLeftRight,
															fEyeUpDown,
															fEyeFocus,
															fEyeClose,
															fEyeSquint,
															fEyeBalance);
		}

	}
}

/// End of function 'Asura_FacialAnimation_AnimationHandler::UpdateAnimation'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::Tools_GetEyeValuePointer
// Author       : Richard Rice
// Date         : 30-Oct-2008
////////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_TOOLS
float* Asura_FacialAnimation_AnimationHandler::Tools_GetEyeValuePointer(u_int uIndex)
{
	if(!m_pfAnimationEyeValues)
	{
		Asura_Assert(0,"Animation handler doesn't have eye values");
		return 0;
	}
	return &m_pfAnimationEyeValues[uIndex];

}
#endif //ASURA_TOOLS

/// End of function 'Asura_FacialAnimation_AnimationHandler::Tools_GetEyeValuePointer'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::Tools_GetEyeValue
// Author       : Richard Rice
// Date         : 30-Oct-2008
////////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_TOOLS
float Asura_FacialAnimation_AnimationHandler::Tools_GetEyeValue(u_int uIndex) const
{
	if(!m_pfAnimationEyeValues)
	{
		Asura_Assert(0,"Animation handler doesn't have eye values");
		return 0;
	}
	return m_pfAnimationEyeValues[uIndex];
}
#endif //ASURA_TOOLS

/// End of function 'Asura_FacialAnimation_AnimationHandler::Tools_GetEyeValue'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_AnimationHandler::SetManualTimerUpdate
// Author       : Richard Rice
// Date         : 15-Dec-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_AnimationHandler::SetManualTimerUpdate(Asura_Hash_ID uFaceAnimationHash, bool bSet, float fTimer)
{
	Asura_FacialAnimation_FaceAnimationInstance* pxFaceAnimationInstance	= m_xFaceAnimationInstanceContainer.GetDataByHash(uFaceAnimationHash);
	if(pxFaceAnimationInstance)
	{
		pxFaceAnimationInstance->SetManualTimerUpdate(bSet, fTimer);
	}

}
/// End of function 'Asura_FacialAnimation_AnimationHandler::SetManualTimerUpdate'. ///



////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimationInstance::Initialise
// Author       : Andy Tate
// Date         : 5-Sep-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_FaceAnimationInstance::Initialise(const Asura_Hash_ID uFaceAnimationHash, bool bLooped /*= false*/)
{
	m_uFaceAnimationHash	= uFaceAnimationHash;
	m_fTimePlayed			= 0.0f;
	m_piSoundHandle			= NULL;
	m_uSoundHash			= ASURA_HASH_ID_UNSET;
	m_bLoopedAnim			= bLooped;
}

/// End of function 'Asura_FacialAnimation_FaceAnimationInstance::Initialise'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimationInstance::Initialise
// Author       : Andy Tate
// Date         : 27-Aug-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_FaceAnimationInstance::Initialise(const Asura_Hash_ID uFaceAnimationHash,
															 Asura_Handle& iSoundHandle,
															 const Asura_Hash_ID uSoundHash)
{
	m_uFaceAnimationHash	= uFaceAnimationHash;
	m_fTimePlayed			= 0.0f;
	m_piSoundHandle			= &iSoundHandle;
	m_uSoundHash			= uSoundHash;
	m_bLoopedAnim			= false;
}

/// End of function 'Asura_FacialAnimation_FaceAnimationInstance::Initialise'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimationInstance::Update
// Author       : Andy Tate
// Date         : 27-Aug-2008
////////////////////////////////////////////////////////////////////////////////

bool Asura_FacialAnimation_FaceAnimationInstance::Update(const float fTimeStep,
														 float* pfWeights,
														 float* pfBalances,
														 float* pfEyeValues)
{
	const Asura_FacialAnimation_FaceAnimation*	pxFaceAnimation	= Asura_FacialAnimation_System::FindFaceAnimation(m_uFaceAnimationHash);

	if(pxFaceAnimation == NULL)
	{
		Asura_Assert(false, "Cannot find the face-animation to be updated.");
		return(false);
	}
	else
	{
		// Only update our internal timer if we don't have a sound OR it is actually playing.
		// NOTE: This is needed to ensure that sounds streamed sync with the animation.
		bool bUpdate = ( m_piSoundHandle == NULL );
		
		#ifdef ASURA_USE_AUDIO_SYSTEM

		if( m_piSoundHandle )
		{
			const Asura_Audio_Voice* pxVoice = Asura_Audio_System::GetVoice( *m_piSoundHandle );

			if( pxVoice && !pxVoice->IsPreparing() )
			{
				bUpdate = true;
			}
		}
		
		#else

		if(m_piSoundHandle)
		{
			if( !Asura_Sound_System::SoundIsPreparing( *m_piSoundHandle ) )
			{
				bUpdate = true;
			}
		}

		#endif
		
		if( bUpdate && !m_bManualTimerUpdate)
		{
			m_fTimePlayed += fTimeStep;

			if(m_bLoopedAnim)
			{
				const float fAnimDuration = pxFaceAnimation->GetDuration();
				if ( m_fTimePlayed > fAnimDuration )
				{
					//Deal with looping
					if(fAnimDuration)
					{
						//loop the timer
						m_fTimePlayed = Asura_Maths::FloatMod(m_fTimePlayed, fAnimDuration);
						//and ensure it is now in range to be safe
						m_fTimePlayed = Asura_Maths::ClampToRange(m_fTimePlayed, 0.0f, fAnimDuration);
					}
					else
					{
						m_fTimePlayed = 0.0f;//zero length animation , so the timer has to stay at zero
					}
				}
			}
		}

		bool bRet = pxFaceAnimation->Update( m_fTimePlayed, pfWeights, pfBalances, pfEyeValues );

		if ( !bRet )
		{
			//This check may now be redundant , but I guess it may be useful in case a manual timer update was done
			if ( m_fTimePlayed >= pxFaceAnimation->GetDuration() && m_bLoopedAnim )
			{
				m_fTimePlayed = 0.0f;
				bRet = true;
			}
		}

		// Update the blend weights accordingly...
		return bRet;
	}
}

/// End of function 'Asura_FacialAnimation_FaceAnimationInstance::Update'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_FacialAnimation_FaceAnimationInstance::SetManualTimerUpdate
// Author       : Richard Rice
// Date         : 15-Dec-2008
////////////////////////////////////////////////////////////////////////////////

void Asura_FacialAnimation_FaceAnimationInstance::SetManualTimerUpdate(bool bSet, float fTimer)
{
	m_bManualTimerUpdate = bSet;
	if(bSet) m_fTimePlayed = fTimer;
}

/// End of function 'Asura_FacialAnimation_FaceAnimationInstance::SetManualTimerUpdate'. ///

#endif	//defined(ASURA_USE_FACIAL_ANIMATION_SYSTEM)


/// End of file. ///
