// MeshFrame.cpp: implementation of the CMeshFrame class.
//
//////////////////////////////////////////////////////////////////////

#include "MeshFrame.h"

#include "Mesh.h"

#include "NodeKey.h"

#include "KeyNode.h"
#include "KeyFrame.h"

#include "Kernel.h"

#include "..\Common\FileStream.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CMeshFrame::CMeshFrame()
{
	m_pszClassName = "CMeshFrame";

	///////////////////////////////////////////////////////////////////////////
	m_pMeshs = NULL;

	m_pKeyNodes = NULL;
	m_pTransform = NULL;

	m_pKeyFrame = NULL;
}

CMeshFrame::~CMeshFrame()
{
	Destroy();
}

void CMeshFrame::Destroy()
{
	if (m_pMeshs)
	{
		delete [] m_pMeshs;
		m_pMeshs = NULL;
	}

	if (m_pKeyNodes)
	{
		delete [] m_pKeyNodes;
		m_pKeyNodes = NULL;
	}
	if (m_pTransform)
	{
		delete [] m_pTransform;
		m_pTransform = NULL;
	}

	CUnknown::Destroy();
}

bool CMeshFrame::Load(LPSTR lpszFileName)
{
	if (!CUnknown::Create(lpszFileName))
		return false;

	///////////////////////////////////////////////////////////////////////////	
	CFileStream stream;
	if (!stream.Open(lpszFileName, CFileStream::READONLY))
		return false;

	if (!ReadHeader(&stream))
		return false;

	if (!CKernel::ReadMaterial(&stream))
		return false;


	// Read KeyNode
	///////////////////////////////////////////////////////////////////////////////////////////////
	stream.Read(&m_dwNumKeyNodes, sizeof(DWORD), 1);

	m_pKeyNodes = new CKeyNode [m_dwNumKeyNodes];
	if (!m_pKeyNodes)
		return false;

	CKeyNode* pKeyNode = m_pKeyNodes;
	for (int i = 0; i < (int) m_dwNumKeyNodes; i ++)
	{
		pKeyNode->Read(&stream);
		pKeyNode ++;
	}

	// Transform
	m_pTransform = new MATRIX [m_dwNumKeyNodes];
	if (!m_pTransform)
		return false;

	// Read Mesh
	///////////////////////////////////////////////////////////////////////////////////////////////
	stream.Read(&m_dwNumMeshs, sizeof(DWORD), 1);

	m_pMeshs = new CMesh [m_dwNumMeshs];
	if (!m_pMeshs)
		return false;

	CMesh* pMesh = m_pMeshs;
	for (i=0; i<(int) m_dwNumMeshs; i++)
	{
		pMesh->SetTransform(m_pTransform);

		pMesh->Read(&stream);
		pMesh ++;
	}

	return true;
}

void CMeshFrame::Render()
{
	CMesh* pMesh = m_pMeshs;
	for (int i=0; i<(int) m_dwNumMeshs; i++)
	{
		pMesh->Render();
		pMesh ++;
	}
}

bool CMeshFrame::ReadHeader(CFileStream *lpStream)
{
	// Read file header
	char szHeader[255];
	
	int nLength = lpStream->ReadString(szHeader);
	if (strncmp(szHeader, "RIFF", 4))
		return false;

	DWORD dwVersion;
	lpStream->Read(&dwVersion, sizeof(DWORD), 1);
	if (dwVersion != 100)
		return false;

	return true;
}

void CMeshFrame::Update()
{
	if (m_pKeyFrame)
	{
		int nCurrentFrame = (int) m_pKeyFrame->GetCurrentFrame();
		float s1 = m_pKeyFrame->GetCurrentFrame() - nCurrentFrame;
		if (s1 < 0)
			s1 = 0;
		else if (s1 > 1.0f)
			s1 = 1.0f;

		float s2 = 1.0f - s1;

		CNodeKey* pNodeKey = m_pKeyFrame->GetNodeKeys();
		for (int i=0; i<(int) m_pKeyFrame->GetNumNodeKeys(); i++)
		{
			LPKINEMATICS pCurrent = (LPKINEMATICS) pNodeKey->GetKinematices() + nCurrentFrame;
			LPKINEMATICS pNext = pCurrent;

			if (m_pKeyFrame->GetCurrentFrame() < (float) m_pKeyFrame->GetNumFrames() - 1)
				pNext ++;

			// Calc Quaternion
			QUATERNION q;
			QuaternionSlerp(&pCurrent->quaternion, &pNext->quaternion, s1, &q);

			MATRIX m;
			QuaternionMatrix(&q, &m);

			// calc Translate
			m._03 = pCurrent->translate.x * s2 + pNext->translate.x * s1;
			m._13 = pCurrent->translate.y * s2 + pNext->translate.y * s1;
			m._23 = pCurrent->translate.z * s2 + pNext->translate.z * s1;

	        int nParent = pNodeKey->GetParent();
			if (nParent == -1)
				MatrixCopy(&m, &m_pTransform[i]);
			else
				MatrixMultiply(&m_pTransform[nParent], &m, &m_pTransform[i]);

			pNodeKey ++;
		}
	}
	else
	{
		CKeyNode* pKeyNode = m_pKeyNodes;
		for (int i = 0; i < (int) m_dwNumKeyNodes; i ++)
		{
			KINEMATICS kinematics = pKeyNode->GetKinematices();

			// Calc Quaternion
			MATRIX m;
			QuaternionMatrix(&kinematics.quaternion, &m);

			// calc Translate
			m._03 = kinematics.translate.x;
			m._13 = kinematics.translate.y;
			m._23 = kinematics.translate.z;

			int nParent = pKeyNode->GetParent();
			if (nParent == -1)
				MatrixCopy(&m, &m_pTransform[i]);
			else
				MatrixMultiply(&m_pTransform[nParent], &m, &m_pTransform[i]);

			pKeyNode ++;
		}
	}

	CMesh* pMesh = m_pMeshs;
	for (int i=0; i<(int) m_dwNumMeshs; i++)
	{
		pMesh->Update();
		pMesh ++;
	}
}
