
///////////////////////////////////////////////////////////////////////////////
//
// File   : Tools_FacialAnimation_Face.cpp
// Author : Andy Tate
// Date   : 9-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/Tools_FacialAnimation_Face.h"

// Asura independent header-files...
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "FacialAnimation/Asura_FacialAnimation_System.h"

// Tools header-files...
#include "FacialAnimation/Tools_FacialAnimation_Face_MuscleGroup.h"


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

////////////////////////////////////////////////////////////////////////////////
// Version	Author	Comment
// ~~~~~~~	~~~~~~	~~~~~~~
// 1		ART		Created.
// 2		ART		Added binding to a particular face-type.
// 3		RAR		Added eye controller parameters
////////////////////////////////////////////////////////////////////////////////
const u_int	Tools_FacialAnimation_Face::s_uChunkVersion	= 3;


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


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


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

////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::CreateAsuraFace
// Author       : Andy Tate
// Date         : 22-May-2008
////////////////////////////////////////////////////////////////////////////////

Asura_FacialAnimation_Face* Tools_FacialAnimation_Face::CreateAsuraFace(void) const
{
	const Asura_Hash_ID			uHash		= m_xName.GetHash();
	Asura_FacialAnimation_Face*	pxAsuraFace	= Asura_FacialAnimation_System::FindFace(uHash);

	// Ensure that we get rid of any existing face before creating a new one...
	if(pxAsuraFace != NULL)
	{
		Asura_FacialAnimation_System::DestroyFace(uHash);
	}

	pxAsuraFace	= Asura_FacialAnimation_System::CreateFace(uHash);

	if(pxAsuraFace != NULL)
	{
		const u_int							uNumMuscleGroups	= GetNumMuscleGroups();
		Asura_FacialAnimation_MuscleGroup*	pxAsuraMuscleGroup	= pxAsuraFace->CreateMuscleGroups(uNumMuscleGroups);

		// Now copy the muscle-group data for this face...
		for(MuscleGroupConstIterator xIterator(m_xMuscleGroupContainer); !xIterator.Done(); xIterator.Next(), pxAsuraMuscleGroup++)
		{
			const Tools_FacialAnimation_Face_MuscleGroup*			pxToolsMuscleGroup	= xIterator.GetCurrent();
			const Asura_FacialAnimation_MuscleGroup::ControlType	eControlType		= pxToolsMuscleGroup->GetControlType();
			const u_int												uNumAnimations		= pxToolsMuscleGroup->GetNumAnimations();

			pxAsuraMuscleGroup->SetControlType(eControlType);
			pxAsuraMuscleGroup->SetNumAnimations(uNumAnimations);

			for(u_int u = 0; u < uNumAnimations; u++)
			{
				const Asura_String&	xAnimationName	= pxToolsMuscleGroup->GetAnimationName(u);
				const Asura_Hash_ID	uAnimationHash	= xAnimationName.GetHash();

				pxAsuraMuscleGroup->SetAnimationHash(u, uAnimationHash);
			}
		}

		pxAsuraFace->Tools_CopyEyeData(m_uNumEyeBones,m_pxEyeBones,m_xEyeValues);
	}

	return(pxAsuraFace);
}

/// End of function 'Tools_FacialAnimation_Face::CreateAsuraFace'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::DestroyAsuraFace
// Author       : Andy Tate
// Date         : 27-May-2008
////////////////////////////////////////////////////////////////////////////////

bool Tools_FacialAnimation_Face::DestroyAsuraFace(void) const
{
	const Asura_Hash_ID	uHash	= m_xName.GetHash();

	return(Asura_FacialAnimation_System::DestroyFace(uHash));
}

/// End of function 'Tools_FacialAnimation_Face::DestroyAsuraFace'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::ReloadAsuraAnimations
// Author       : Andy Tate
// Date         : 23-May-2008
////////////////////////////////////////////////////////////////////////////////

u_int Tools_FacialAnimation_Face::ReloadAsuraAnimations(void)
{
	u_int	uNumAnimationsReloaded	= 0;

	// Reload the animations used by each muscle-group in-turn...
	for(MuscleGroupIterator xIterator(m_xMuscleGroupContainer); !xIterator.Done(); xIterator.Next())
	{
		Tools_FacialAnimation_Face_MuscleGroup*	pxMuscleGroup	= xIterator.GetCurrent();

		if(pxMuscleGroup != NULL)
		{
			uNumAnimationsReloaded	+= pxMuscleGroup->ReloadAsuraAnimations();
		}
	}

	return(uNumAnimationsReloaded);
}

/// End of function 'Tools_FacialAnimation_Face::ReloadAsuraAnimations'. ///



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

////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::Tools_FacialAnimation_Face
// Author       : Andy Tate
// Date         : 9-May-2008
////////////////////////////////////////////////////////////////////////////////

Tools_FacialAnimation_Face::Tools_FacialAnimation_Face(void)
:m_uNumEyeBones(0)
,m_pxEyeBones(0)
{
	m_xEyeValues.SetDefaults();
}

/// End of function 'Tools_FacialAnimation_Face::Tools_FacialAnimation_Face'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::Tools_FacialAnimation_Face
// Author       : Andy Tate
// Date         : 9-May-2008
////////////////////////////////////////////////////////////////////////////////

Tools_FacialAnimation_Face::Tools_FacialAnimation_Face(const Asura_Hash_ID uHash,
													   Asura_Chunk_Stream& xStream)
:m_uNumEyeBones(0)
,m_pxEyeBones(0)
{
	m_xEyeValues.SetDefaults();
	ReadFromChunkStream(xStream);
	CreateAsuraFace();
}

/// End of function 'Tools_FacialAnimation_Face::Tools_FacialAnimation_Face'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::~Tools_FacialAnimation_Face
// Author       : Andy Tate
// Date         : 27-May-2008
////////////////////////////////////////////////////////////////////////////////

Tools_FacialAnimation_Face::~Tools_FacialAnimation_Face()
{
	Tools_FacialAnimation_Face_MuscleGroup*	pxMuscleGroup	= m_xMuscleGroupContainer.PopHeadObject();

	while(pxMuscleGroup != NULL)
	{
		delete(pxMuscleGroup);
		pxMuscleGroup	= m_xMuscleGroupContainer.PopHeadObject();
	}
	delete [] m_pxEyeBones;
}

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



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::CreateMuscleGroup
// Author       : Andy Tate
// Date         : 9-May-2008
////////////////////////////////////////////////////////////////////////////////

Tools_FacialAnimation_Face_MuscleGroup* Tools_FacialAnimation_Face::CreateMuscleGroup(const char* szName, 
																					  const Asura_FacialAnimation_MuscleGroup::ControlType eControlType)
{
	Tools_FacialAnimation_Face_MuscleGroup*	pxNewMuscleGroup	= new Tools_FacialAnimation_Face_MuscleGroup (szName, eControlType);

	m_xMuscleGroupContainer.PushTail(pxNewMuscleGroup);
	return(pxNewMuscleGroup);
}

/// End of function 'Tools_FacialAnimation_Face::CreateMuscleGroup'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::DestroyMuscleGroup
// Author       : Andy Tate
// Date         : 9-May-2008
////////////////////////////////////////////////////////////////////////////////

bool Tools_FacialAnimation_Face::DestroyMuscleGroup(const char* szName)
{
	Tools_FacialAnimation_Face_MuscleGroup*	pxMuscleGroup	= FindMuscleGroup(szName);

	if(pxMuscleGroup == NULL)
	{
		Asura_Assert(false, "Failed to find muscle-group to destroy.");
		return(false);
	}
	else
	{
		m_xMuscleGroupContainer.RemoveFromList(pxMuscleGroup);
		delete(pxMuscleGroup);
		return(true);
	}
}

/// End of function 'Tools_FacialAnimation_Face::DestroyMuscleGroup'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::SwapMuscleGroups
// Author       : Andy Tate
// Date         : 27-May-2008
////////////////////////////////////////////////////////////////////////////////

void Tools_FacialAnimation_Face::SwapMuscleGroups(const Tools_FacialAnimation_Face_MuscleGroup* pxMuscleGroup1,
												  const Tools_FacialAnimation_Face_MuscleGroup* pxMuscleGroup2)
{
	const u_int							uNumMuscleGroups	= GetNumMuscleGroups();
	Tools_FacialAnimation_Face_MuscleGroup**	apxMuscleGroups		= new Tools_FacialAnimation_Face_MuscleGroup* [uNumMuscleGroups];

	int									iIndex1				= -1;
	int									iIndex2				= -1;

	// Empty our list of muscle-groups AND find the indices to be swapped...
	for(u_int u = 0; u < uNumMuscleGroups; u++)
	{
		apxMuscleGroups[u]	= m_xMuscleGroupContainer.PopHeadObject();

		if(apxMuscleGroups[u] == pxMuscleGroup1)
		{
			iIndex1	= u;
		}
		else if(apxMuscleGroups[u] == pxMuscleGroup2)
		{
			iIndex2	= u;
		}
	}

	// Swap our 2 muscle-groups over...
	if(iIndex1 < 0)
	{
		Asura_Assert(false, "Failed to find first muscle-group to be swapped.");
	}
	else if(iIndex2 < 0)
	{
		Asura_Assert(false, "Failed to find second muscle-group to be swapped.");
	}
	else
	{
		Tools_FacialAnimation_Face_MuscleGroup*	pxTempMuscleGroup	= apxMuscleGroups[iIndex1];

		apxMuscleGroups[iIndex1]	= apxMuscleGroups[iIndex2];
		apxMuscleGroups[iIndex2]	= pxTempMuscleGroup;

		// Re-populate or muscle-groups...
		for(u_int u = 0; u < uNumMuscleGroups; u++)
		{
			m_xMuscleGroupContainer.PushTail(apxMuscleGroups[u]);
		}
	}

	// Finally, free-up the allocated data AND sort out the Asura-side data...
	delete[](apxMuscleGroups);
	DestroyAsuraFace();
	CreateAsuraFace();
}

/// End of function 'Tools_FacialAnimation_Face::SwapMuscleGroups'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::FindMuscleGroup
// Author       : Andy Tate
// Date         : 27-May-2008
////////////////////////////////////////////////////////////////////////////////

Tools_FacialAnimation_Face_MuscleGroup* Tools_FacialAnimation_Face::FindMuscleGroup(const char* szName)
{
	for(MuscleGroupIterator xIterator(m_xMuscleGroupContainer); !xIterator.Done(); xIterator.Next())
	{
		Tools_FacialAnimation_Face_MuscleGroup*	pxMuscleGroup	= xIterator.GetCurrent();

		if(pxMuscleGroup != NULL)
		{
			const Asura_String&	xMuscleGroupName	= pxMuscleGroup->GetName();

			if(xMuscleGroupName.Compare(szName, true) == 0)
			{
				return(pxMuscleGroup);
			}
		}
	}

	return(NULL);
}

/// End of function 'Tools_FacialAnimation_Face::FindMuscleGroup'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::FindMuscleGroup
// Author       : Andy Tate
// Date         : 27-May-2008
////////////////////////////////////////////////////////////////////////////////

const Tools_FacialAnimation_Face_MuscleGroup* Tools_FacialAnimation_Face::FindMuscleGroup(const char* szName) const
{
	for(MuscleGroupConstIterator xIterator(m_xMuscleGroupContainer); !xIterator.Done(); xIterator.Next())
	{
		const Tools_FacialAnimation_Face_MuscleGroup*	pxMuscleGroup	= xIterator.GetCurrent();

		if(pxMuscleGroup != NULL)
		{
			const Asura_String&	xMuscleGroupName	= pxMuscleGroup->GetName();

			if(xMuscleGroupName.Compare(szName, true) == 0)
			{
				return(pxMuscleGroup);
			}
		}
	}

	return(NULL);
}

/// End of function 'Tools_FacialAnimation_Face::FindMuscleGroup'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::ReadFromChunkStream
// Author       : Andy Tate
// Date         : 9-May-2008
////////////////////////////////////////////////////////////////////////////////

void Tools_FacialAnimation_Face::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	u_int	uVersion;

	// Reset any existing data...
	m_xMuscleGroupContainer.ClearList();
	m_xFaceTypeName.MakeNull();

	// First, read (and verify) the chunk-version.
	xStream >> uVersion;

	switch(uVersion)
	{
		case 1:
		{
			u_int	uNumMuscleGroups;

			xStream >> m_xName;
			xStream >> uNumMuscleGroups;

			for(u_int u = 0; u < uNumMuscleGroups; u++)
			{
				Tools_FacialAnimation_Face_MuscleGroup*	pxNewMuscleGroup;

				pxNewMuscleGroup	= new Tools_FacialAnimation_Face_MuscleGroup (xStream);
				m_xMuscleGroupContainer.PushTail(pxNewMuscleGroup);
			}
			break;
		}

		case 2:
		{
			u_int	uNumMuscleGroups;

			xStream >> m_xName;
			xStream >> m_xFaceTypeName;
			xStream >> uNumMuscleGroups;

			for(u_int u = 0; u < uNumMuscleGroups; u++)
			{
				Tools_FacialAnimation_Face_MuscleGroup*	pxNewMuscleGroup;

				pxNewMuscleGroup	= new Tools_FacialAnimation_Face_MuscleGroup (xStream);
				m_xMuscleGroupContainer.PushTail(pxNewMuscleGroup);
			}
			break;
		}

		case 3:
		{
			u_int	uNumMuscleGroups;

			xStream >> m_xName;
			xStream >> m_xFaceTypeName;
			xStream >> uNumMuscleGroups;

			for(u_int u = 0; u < uNumMuscleGroups; u++)
			{
				Tools_FacialAnimation_Face_MuscleGroup*	pxNewMuscleGroup;

				pxNewMuscleGroup	= new Tools_FacialAnimation_Face_MuscleGroup (xStream);
				m_xMuscleGroupContainer.PushTail(pxNewMuscleGroup);
			}
			
			xStream >> m_xEyeValues.m_fInLimit;
			xStream >> m_xEyeValues.m_fOutLimit;
			xStream >> m_xEyeValues.m_fUpLimit;
			xStream >> m_xEyeValues.m_fDownLimit;
			xStream >> m_xEyeValues.m_fUpSideLimit;
			xStream >> m_xEyeValues.m_fDownSideLimit;
			xStream >> m_xEyeValues.m_fLeftRightScale;
			xStream >> m_xEyeValues.m_fUpDownScale;
		
			xStream >> m_uNumEyeBones;
			m_pxEyeBones = m_uNumEyeBones ? new Tools_FacialAnimation_Face_EyeBone[m_uNumEyeBones] : 0;
			for(u_int i=0; i<m_uNumEyeBones; i++)
			{
				xStream >> m_pxEyeBones[i].m_xBoneName;
				xStream >> m_pxEyeBones[i].m_iBoneIndex;
				xStream >> m_pxEyeBones[i].m_fRotSquint;
				xStream >> m_pxEyeBones[i].m_fRotClose;
				xStream >> m_pxEyeBones[i].m_fAngMin;
				xStream >> m_pxEyeBones[i].m_fAngMax;
				xStream >> m_pxEyeBones[i].m_uFlags;
			}
			break;
		}


		default:
		{
			Asura_Assertf(false, ("Unexpected chunk-version %d", uVersion));
			break;
		}
	}		// End of switch.
}

/// End of function 'Tools_FacialAnimation_Face::ReadFromChunkStream'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::WriteToChunkStream
// Author       : Andy Tate
// Date         : 9-May-2008
////////////////////////////////////////////////////////////////////////////////

void Tools_FacialAnimation_Face::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	const u_int	uNumMuscleGroups	= m_xMuscleGroupContainer.GetNumEntries();

	// First write the chunk-version...
	xStream << s_uChunkVersion;

	xStream << m_xName;
	xStream << m_xFaceTypeName;
	xStream << uNumMuscleGroups;

	for(MuscleGroupConstIterator xIterator(m_xMuscleGroupContainer); !xIterator.Done(); xIterator.Next())
	{
		const Tools_FacialAnimation_Face_MuscleGroup*	pxMuscleGroup	= xIterator.GetCurrent();

		pxMuscleGroup->WriteToChunkStream(xStream);
	}

	xStream << m_xEyeValues.m_fInLimit;
	xStream << m_xEyeValues.m_fOutLimit;
	xStream << m_xEyeValues.m_fUpLimit;
	xStream << m_xEyeValues.m_fDownLimit;
	xStream << m_xEyeValues.m_fUpSideLimit;
	xStream << m_xEyeValues.m_fDownSideLimit;
	xStream << m_xEyeValues.m_fLeftRightScale;
	xStream << m_xEyeValues.m_fUpDownScale;

	xStream << m_uNumEyeBones;
	for(u_int i=0; i<m_uNumEyeBones; i++)
	{
		xStream << m_pxEyeBones[i].m_xBoneName;
		xStream << m_pxEyeBones[i].m_iBoneIndex;
		xStream << m_pxEyeBones[i].m_fRotSquint;
		xStream << m_pxEyeBones[i].m_fRotClose;
		xStream << m_pxEyeBones[i].m_fAngMin;
		xStream << m_pxEyeBones[i].m_fAngMax;

		Asura_WriteToStream_Bitfield_Wrapper<u_int>	xFlagsBitfield (m_pxEyeBones[i].m_uFlags);
		xFlagsBitfield.Add( m_pxEyeBones[i].m_bEye, 1);
		xFlagsBitfield.Add( m_pxEyeBones[i].m_bRight, 1);
		xStream << xFlagsBitfield;
	}

}

/// End of function 'Tools_FacialAnimation_Face::WriteToChunkStream'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::AddFilesToExportList
// Author       : Andy Tate
// Date         : 10-Sep-2008
////////////////////////////////////////////////////////////////////////////////

void Tools_FacialAnimation_Face::AddFilesToExportList(void) const
{
	//These animations no longer appear as separate files , and so need to get written out directly by the face
	//export code instead.
	#if 0
	// Ensure that the animations associated with our muscle-groups are added to the export...
	for(MuscleGroupConstIterator xIterator(m_xMuscleGroupContainer); !xIterator.Done(); xIterator.Next())
	{
		const Tools_FacialAnimation_Face_MuscleGroup*	pxMuscleGroup	= xIterator.GetCurrent();

		if(pxMuscleGroup != NULL)
		{
			const u_int	uNumAnimations	= pxMuscleGroup->GetNumAnimations();

			for(u_int u = 0; u < uNumAnimations; u++)
			{
				const Asura_String&	xAnimationName	= pxMuscleGroup->GetAnimationName(u);
				const char*			szAnimationName	= xAnimationName.GetString();

				Tools_Level_Export::AddAnimToExportList(szAnimationName);
			}
		}
	}
	#endif
}

/// End of function 'Tools_FacialAnimation_Face::AddFilesToExportList'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::AllocateEyeBones
// Author       : Richard Rice
// Date         : 17-Oct-2008
////////////////////////////////////////////////////////////////////////////////

void Tools_FacialAnimation_Face::AllocateEyeBones(u_int uNumEyeBones)
{
	delete [] m_pxEyeBones;
	m_uNumEyeBones = uNumEyeBones;
	m_pxEyeBones = uNumEyeBones ? new Tools_FacialAnimation_Face_EyeBone[uNumEyeBones] : 0;
}

/// End of function 'Tools_FacialAnimation_Face::AllocateEyeBones'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : Tools_FacialAnimation_Face::GetEyeBone
// Author       : Richard Rice
// Date         : 17-Oct-2008
////////////////////////////////////////////////////////////////////////////////

Tools_FacialAnimation_Face_EyeBone* Tools_FacialAnimation_Face::GetEyeBone(u_int uIndex)
{
	Asura_Assert(uIndex < m_uNumEyeBones, "");
	return &m_pxEyeBones[uIndex];
}
/// End of function 'Tools_FacialAnimation_Face::GetEyeBone'. ///

#endif	//defined(ASURA_USE_FACIAL_ANIMATION_SYSTEM)


/// End of file. ///
