#include "Animation.h"

cAnimationSet::cAnimationSet()
{
	m_Name = NULL;
	m_Length = 0;
	m_Next = NULL;
	m_NumAnimations = 0;
	m_Animations = NULL;
}

cAnimationSet::~cAnimationSet()
{
	delete[] m_Name;    m_Name = NULL;
	delete m_Next;       m_Next = NULL;
	delete m_Animations; m_Animations = NULL;
}

cAnimation::cAnimation()
{
	m_Name = NULL;
	m_Bone = NULL;
	m_Next = NULL;

	m_NumTranslationKeys = 0;
	m_NumScaleKeys = 0;
	m_NumRotationKeys = 0;
	m_NumMatrixKeys = 0;

	m_TranslationKeys = NULL;
	m_ScaleKeys = NULL;
	m_RotationKeys = NULL;
	m_MatrixKeys = NULL;
}

cAnimation::~cAnimation()
{
	delete[] m_Name; m_Name = NULL;
	delete m_Next;    m_Next = NULL;
	delete[] m_TranslationKeys;
	delete[] m_ScaleKeys;
	delete[] m_RotationKeys;
	delete[] m_MatrixKeys;
}

cAnimationCollection::cAnimationCollection()
{
	m_NumAnimationSets = 0;
	m_AnimationSets = NULL;
}

cAnimationCollection::~cAnimationCollection()
{
	Free();
}

BOOL cAnimationCollection::ParseObject(ID3DXFileData *pDataObj, ID3DXFileData *pParentDataObj, DWORD Depth, void **Data, BOOL Reference)
{
	const GUID *Type = GetObjectGUID(pDataObj);
	DWORD i;

	// Check if template is AnimationSet type
	if (*Type == TID_D3DRMAnimationSet) {

		// Create and link in a cAnimationSet object
		cAnimationSet *AnimSet = new cAnimationSet();
		AnimSet->m_Next = m_AnimationSets;
		m_AnimationSets = AnimSet;

		// Increase # of animation sets
		m_NumAnimationSets++;

		// Set animation set name
		AnimSet->m_Name = GetObjectName(pDataObj);
	}

	// Check if template is Animation type
	if (*Type == TID_D3DRMAnimation && m_AnimationSets) {

		// Add a cAnimation class to top-level cAnimationSet
		cAnimation *Anim = new cAnimation();
		Anim->m_Next = m_AnimationSets->m_Animations;
		m_AnimationSets->m_Animations = Anim;

		// Increase # of animations
		m_AnimationSets->m_NumAnimations++;
	}

	// Check if a frame reference inside animation template
	if (*Type == TID_D3DRMFrame && Reference == TRUE &&          \
		m_AnimationSets &&               \
		m_AnimationSets->m_Animations) {

		// Make sure parent object is an Animation template
		if (pParentDataObj && *GetObjectGUID(pParentDataObj) == \
			TID_D3DRMAnimation) {

			// Get name of frame and store it as animation
			m_AnimationSets->m_Animations->m_Name = \
				GetObjectName(pDataObj);
		}

		// Don't process child of reference frames
		return TRUE;
	}

	// Check if template is AnimationKey type
	if (*Type == TID_D3DRMAnimationKey && m_AnimationSets && m_AnimationSets->m_Animations) {

		// Get a pointer to top-level animation object
		cAnimation *Anim = m_AnimationSets->m_Animations;

		// Get a data pointer
		DWORD *DataPtr = (DWORD*)GetObjectData(pDataObj, NULL);

		// Get key type
		DWORD Type = *DataPtr++;

		// Get # of keys to follow
		DWORD NumKeys = *DataPtr++;

		// Branch based on key type
		switch (Type) {
		case 0: // Rotation
			delete[] Anim->m_RotationKeys;
			Anim->m_NumRotationKeys = NumKeys;
			Anim->m_RotationKeys = new cAnimationQuaternionKey[NumKeys];
			for (i = 0; i<NumKeys; i++) {
				// Get time
				Anim->m_RotationKeys[i].m_Time = *DataPtr++;
				if (Anim->m_RotationKeys[i].m_Time > m_AnimationSets->m_Length)
					m_AnimationSets->m_Length = Anim->m_RotationKeys[i].m_Time;

				// Skip # keys to follow (should be 4)
				DataPtr++;

				// Get rotational values
				float *fPtr = (float*)DataPtr;
				Anim->m_RotationKeys[i].m_quatKey.w = *fPtr++;
				Anim->m_RotationKeys[i].m_quatKey.x = *fPtr++;
				Anim->m_RotationKeys[i].m_quatKey.y = *fPtr++;
				Anim->m_RotationKeys[i].m_quatKey.z = *fPtr++;
				DataPtr += 4;
			}
			break;

		case 1: // Scaling
			delete[] Anim->m_ScaleKeys;
			Anim->m_NumScaleKeys = NumKeys;
			Anim->m_ScaleKeys = new cAnimationVectorKey[NumKeys];
			for (i = 0; i<NumKeys; i++) {
				// Get time
				Anim->m_ScaleKeys[i].m_Time = *DataPtr++;
				if (Anim->m_ScaleKeys[i].m_Time > m_AnimationSets->m_Length)
					m_AnimationSets->m_Length = Anim->m_ScaleKeys[i].m_Time;

				// Skip # keys to follow (should be 3)
				DataPtr++;

				// Get scale values
				D3DXVECTOR3 *vecPtr = (D3DXVECTOR3*)DataPtr;
				Anim->m_ScaleKeys[i].m_vecKey = *vecPtr;
				DataPtr += 3;
			}
			break;

		case 2: // Translation
			delete[] Anim->m_TranslationKeys;
			Anim->m_NumTranslationKeys = NumKeys;
			Anim->m_TranslationKeys = new cAnimationVectorKey[NumKeys];
			for (i = 0; i<NumKeys; i++) {
				// Get time
				Anim->m_TranslationKeys[i].m_Time = *DataPtr++;
				if (Anim->m_TranslationKeys[i].m_Time > m_AnimationSets->m_Length)
					m_AnimationSets->m_Length = Anim->m_TranslationKeys[i].m_Time;

				// Skip # keys to follow (should be 3)
				DataPtr++;

				// Get translation values
				D3DXVECTOR3 *vecPtr = (D3DXVECTOR3*)DataPtr;
				Anim->m_TranslationKeys[i].m_vecKey = *vecPtr;
				DataPtr += 3;
			}
			break;

		case 4: // Transformation matrix
			delete[] Anim->m_MatrixKeys;
			Anim->m_NumMatrixKeys = NumKeys;
			Anim->m_MatrixKeys = new cAnimationMatrixKey[NumKeys];
			for (i = 0; i<NumKeys; i++) {
				// Get time
				Anim->m_MatrixKeys[i].m_Time = *DataPtr++;

				if (Anim->m_MatrixKeys[i].m_Time > m_AnimationSets->m_Length)
					m_AnimationSets->m_Length = \
					Anim->m_MatrixKeys[i].m_Time;

				// Skip # keys to follow (should be 16)
				DataPtr++;

				// Get matrix values
				D3DXMATRIX *mPtr = (D3DXMATRIX *)DataPtr;
				Anim->m_MatrixKeys[i].m_matKey = *mPtr;
				DataPtr += 16;
			}
			break;
		}
	}

	return ParseChildObjects(pDataObj, Depth, Data, Reference);
}

BOOL cAnimationCollection::Load(char *Filename)
{
	// Free a prior loaded collection
	Free();

	// Parse the file
	return Parse(Filename);
}

void cAnimationCollection::Free()
{
	m_NumAnimationSets = 0;
	delete m_AnimationSets; m_AnimationSets = NULL;
}

void cAnimationCollection::Map(D3DXFRAME_EX *RootFrame)
{
	// Go through each animation set
	cAnimationSet *AnimSet = m_AnimationSets;
	while (AnimSet != NULL) {

		// Go through each animation object
		cAnimation *Anim = AnimSet->m_Animations;
		while (Anim != NULL) {

			// Go through all frames and look for match
			Anim->m_Bone = RootFrame->Find(Anim->m_Name);

			// Go to next animation object
			Anim = Anim->m_Next;
		}

		// Go to next animation set object
		AnimSet = AnimSet->m_Next;
	}
}

void cAnimationCollection::Update(char *AnimationSetName, DWORD Time, BOOL Loop)
{
	cAnimationSet *AnimSet = m_AnimationSets;

	// Look for matching animation set name if used
	if (AnimationSetName) {

		// Find matching animation set name
		while (AnimSet != NULL) {

			// Break when match found
			if (!_stricmp(AnimSet->m_Name, AnimationSetName))
				break;

			// Go to next animation set object
			AnimSet = AnimSet->m_Next;
		}
	}

	// Return no set found
	if (AnimSet == NULL)
		return;

	// Bounds time to animation length
	if (Time > AnimSet->m_Length)
		Time = (Loop == TRUE) ? Time % (AnimSet->m_Length + 1) : AnimSet->m_Length;

	// Go through each animation
	cAnimation *Anim = AnimSet->m_Animations;
	while (Anim) {

		// Only process if it's attached to a bone
		if (Anim->m_Bone) {

			// Reset transformation
			D3DXMatrixIdentity(&Anim->m_Bone->TransformationMatrix);

			// Apply various matrices to transformation

			// Scaling
			if (Anim->m_NumScaleKeys && Anim->m_ScaleKeys) {

				// Loop for matching scale key
				DWORD Key1 = 0, Key2 = 0;
				for (DWORD i = 0; i<Anim->m_NumScaleKeys; i++) {
					if (Time >= Anim->m_ScaleKeys[i].m_Time)
						Key1 = i;
				}

				// Get 2nd key number
				Key2 = (Key1 >= (Anim->m_NumScaleKeys - 1)) ? Key1 : Key1 + 1;

				// Get difference in keys' times
				DWORD TimeDiff = Anim->m_ScaleKeys[Key2].m_Time -
					Anim->m_ScaleKeys[Key1].m_Time;
				if (!TimeDiff)
					TimeDiff = 1;

				// Calculate a scalar value to use
				float Scalar = (float)(Time - Anim->m_ScaleKeys[Key1].m_Time) / (float)TimeDiff;

				// Calculate interpolated scale values
				D3DXVECTOR3 vecScale = Anim->m_ScaleKeys[Key2].m_vecKey -
					Anim->m_ScaleKeys[Key1].m_vecKey;
				vecScale *= Scalar;
				vecScale += Anim->m_ScaleKeys[Key1].m_vecKey;

				// Create scale matrix and combine with transformation
				D3DXMATRIX matScale;
				D3DXMatrixScaling(&matScale, vecScale.x, vecScale.y, vecScale.z);
				Anim->m_Bone->TransformationMatrix *= matScale;
			}

			// Rotation
			if (Anim->m_NumRotationKeys && Anim->m_RotationKeys) {

				// Loop for matching rotation key
				DWORD Key1 = 0, Key2 = 0;
				for (DWORD i = 0; i<Anim->m_NumRotationKeys; i++) {
					if (Time >= Anim->m_RotationKeys[i].m_Time)
						Key1 = i;
				}

				// Get 2nd key number
				Key2 = (Key1 >= (Anim->m_NumRotationKeys - 1)) ? Key1 : Key1 + 1;

				// Get difference in keys' times
				DWORD TimeDiff = Anim->m_RotationKeys[Key2].m_Time -
					Anim->m_RotationKeys[Key1].m_Time;
				if (!TimeDiff)
					TimeDiff = 1;

				// Calculate a scalar value to use
				float Scalar = (float)(Time - Anim->m_RotationKeys[Key1].m_Time) / (float)TimeDiff;

				// slerp rotation values
				D3DXQUATERNION quatRotation;
				D3DXQuaternionSlerp(&quatRotation,
					&Anim->m_RotationKeys[Key1].m_quatKey,
					&Anim->m_RotationKeys[Key2].m_quatKey,
					Scalar);

				// Create rotation matrix and combine with transformation
				D3DXMATRIX matRotation;
				D3DXMatrixRotationQuaternion(&matRotation, &quatRotation);
				Anim->m_Bone->TransformationMatrix *= matRotation;
			}

			// Translation
			if (Anim->m_NumTranslationKeys && Anim->m_TranslationKeys) {

				// Loop for matching translation key
				DWORD Key1 = 0, Key2 = 0;
				for (DWORD i = 0; i<Anim->m_NumTranslationKeys; i++) {
					if (Time >= Anim->m_TranslationKeys[i].m_Time)
						Key1 = i;
				}

				// Get 2nd key number
				Key2 = (Key1 >= (Anim->m_NumTranslationKeys - 1)) ? Key1 : Key1 + 1;

				// Get difference in keys' times
				DWORD TimeDiff = Anim->m_TranslationKeys[Key2].m_Time -
					Anim->m_TranslationKeys[Key1].m_Time;
				if (!TimeDiff)
					TimeDiff = 1;

				// Calculate a scalar value to use
				float Scalar = (float)(Time - Anim->m_TranslationKeys[Key1].m_Time) / (float)TimeDiff;

				// Calculate interpolated vector values
				D3DXVECTOR3 vecPos = Anim->m_TranslationKeys[Key2].m_vecKey -
					Anim->m_TranslationKeys[Key1].m_vecKey;
				vecPos *= Scalar;
				vecPos += Anim->m_TranslationKeys[Key1].m_vecKey;

				// Create translation matrix and combine with transformation
				D3DXMATRIX matTranslation;
				D3DXMatrixTranslation(&matTranslation, vecPos.x, vecPos.y, vecPos.z);
				Anim->m_Bone->TransformationMatrix *= matTranslation;
			}

			// Matrix
			if (Anim->m_NumMatrixKeys && Anim->m_MatrixKeys) {
				// Loop for matching matrix key
				DWORD Key1 = 0, Key2 = 0;
				for (DWORD i = 0; i<Anim->m_NumMatrixKeys; i++) {
					if (Time >= Anim->m_MatrixKeys[i].m_Time)
						Key1 = i;
				}

				// Get 2nd key number
				Key2 = (Key1 >= (Anim->m_NumMatrixKeys - 1)) ? Key1 : Key1 + 1;

				// Get difference in keys' times
				DWORD TimeDiff = Anim->m_MatrixKeys[Key2].m_Time -
					Anim->m_MatrixKeys[Key1].m_Time;
				if (!TimeDiff)
					TimeDiff = 1;

				// Calculate a scalar value to use
				float Scalar = (float)(Time - Anim->m_MatrixKeys[Key1].m_Time) / (float)TimeDiff;

				// Calculate interpolated matrix
				D3DXMATRIX matDiff = Anim->m_MatrixKeys[Key2].m_matKey -
					Anim->m_MatrixKeys[Key1].m_matKey;
				matDiff *= Scalar;
				matDiff += Anim->m_MatrixKeys[Key1].m_matKey;

				// Combine with transformation
				Anim->m_Bone->TransformationMatrix *= matDiff;
			}
		}

		// Go to next animation
		Anim = Anim->m_Next;
	}
}
