/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2009.
     ssss    gggg x   x         Steven Goodwin. All Rights Reserved.
    
This version has been released under the GNU Public License v2.0.

Please see the license files for details.

File: sgx/src/sgx/graphics/mesh/animmesh_md2.cpp

*/

#include "sgx/core/core.hpp"
#include "sgx/graphics/graphics.hpp"
#include "sgx/filesystem/filesystem.hpp"

class sgxMD2Header { 
public:
   tUINT32 magic;		
   tUINT32 version;			
   tUINT32 skinWidth;		
   tUINT32 skinHeight;		
   tUINT32 frameSize;		
   tUINT32 numSkins;		
   tUINT32 numVertices;		
   tUINT32 numTexCoords;	
   tUINT32 numTriangles;	
   tUINT32 numGlCommands;	
   tUINT32 numFrames;		
   tUINT32 offsetSkins;		
   tUINT32 offsetTexCoords;	
   tUINT32 offsetTriangles;	
   tUINT32 offsetFrames;	
   tUINT32 offsetGlCommands;
   tUINT32 offsetEnd;		
};


// This stores the normals and vertices for the frames
class sgxMD2Triangle {
public:
   tREAL32 vertex[3];
   tREAL32 normal[3];
};

// This stores the indices into the vertex and texture coordinate arrays
class sgxMD2RawFace {
public:
   tINT16 vertexIndices[3];
   tINT16 textureIndices[3];
};


// This stores UV coordinates
class sgxMD2TexCoord{
public:
   short u, v;
};
class sgxMD2AliasTriangle{
public:
   tBYTE vertex[3];
   tBYTE lightNormalIndex;
};

// This stores the animation scale, translation and name information for a frame, plus verts
class sgxMD2BaseFrame{
public:
   tREAL32 scale[3];
   tREAL32 translate[3];
   char name[16];
   sgxMD2AliasTriangle aliasVertices[1];
};

// This stores the frames vertices after they have been transformed
class sgxMD2Frame{
public:
   char strName[16];
   sgxMD2Triangle *pVertices;
};


sgxMD2TexCoord			*m_pTexCoords;		// The texture coordinates
sgxMD2RawFace			*m_pTriangles;		// Face index information
sgxMD2Frame				*m_pFrames;			// The frames of animation (vertices)
#define MD2_MAX_FRAMESIZE	4096

	
void CSGXAnimatedMeshMD2::ParseAnimations(const tUINT32 numFrames)
{
	sgxMD2Animation animation;
	std::string strLastName = "";

	// Go through all of the frames of animation and parse each animation
	for(tUINT32 i = 0; i < numFrames; ++i)
	{
		// Assign the name of this frame of animation to a string object
		std::string strName  = m_pFrames[i].strName;
		int frameNum = 0;
		
		// Go through and extract the frame numbers and erase them from the name
		for(unsigned int j = 0; j < strName.length(); ++j)
		{
			// If the current index is a number and it's one of the last 2 characters of the name
			if( sgxIsDigit(strName[j]) && j >= strName.length() - 2)
			{
				// Use a C function to convert the character to a integer.
				// Notice we use the address to pass in the current character and on
				frameNum = sgxAtoi(&strName[j]);

				// Erase the frame number from the name so we extract the animation name
				strName.erase(j, strName.length() - j);
				break;
			}
		}

		// Check if this animation name is not the same as the last frame,
		// or if we are on the last frame of animation for this model
		if(strName != strLastName || i == numFrames - 1)
		{
			// If this animation frame is NOT the first frame
			if(strLastName != "")
			{
				//char name[256];
				animation.m_ID = sgxGetCRC32(strLastName);
				// Copy the last animation name into our new animation's name
				//sgxStrcpy((tCHAR *)&animation.strName[0], (const tCHAR *)strLastName.c_str());

				// set the last frame of this animation to i
				animation.m_iEndFrame = i;

				// add the animation to our list and reset the animation object for next time
				m_AnimationList.push_back(animation);
				sgxMemset(&animation, 0, sizeof(sgxMD2Animation));

				// Increase the number of animations for this model
//				pModel->numOfAnimations++;
			}

			// set the starting frame number to the current frame number we just found,
			// minus 1 (since 0 is the first frame) and add 'i'.
			animation.m_iStartFrame = frameNum - 1 + i;
		}

		// Store the current animation name in the strLastName string to check it latter
		strLastName = strName;
	}
}

CSGXAnimatedMeshMD2::CSGXAnimatedMeshMD2(const sgxString &filename) :
m_fTime(0)
{
	m_pTexture = CSGXTextureManager::get()->loadTexture(filename);

	sgxString md2name(filename);
	md2name += ".md2";
	CSGXFile file(md2name);

	if (!file.open()) {
		return;
	}

	sgxMD2Header header;
	file.read((tBYTE *)&header, sizeof(header));

	if(header.version != 8) 	{
		sgxTrace("Invalid MD2 file");
		file.close();
		return;
	}

	unsigned char buffer[MD2_MAX_FRAMESIZE];

	// Here we allocate all of our memory from the header's information
	m_pTexCoords = new sgxMD2TexCoord [header.numTexCoords];
	m_pTriangles = new sgxMD2RawFace [header.numTriangles];
	m_pFrames    = new sgxMD2Frame [header.numFrames];

	file.seek(header.offsetTexCoords, CSGXDeviceBlockState::eSeekFromBegin);
	file.read((tBYTE *)m_pTexCoords, sizeof(sgxMD2TexCoord) * header.numTexCoords);

	// Move the file pointer to the triangles/face data offset
	file.seek(header.offsetTriangles, CSGXDeviceBlockState::eSeekFromBegin);
	file.read((tBYTE *)m_pTriangles, sizeof(sgxMD2RawFace) * header.numTriangles);
			
	// Move the file pointer to the vertices (frames)
	file.seek(header.offsetFrames, CSGXDeviceBlockState::eSeekFromBegin);


	for (tUINT32 i=0; i < header.numFrames; ++i) {
		// Assign our alias frame to our buffer memory
		sgxMD2BaseFrame *pFrame = (sgxMD2BaseFrame *) buffer;

		// Allocate the memory for the first frame of animation's vertices
		m_pFrames[i].pVertices = new sgxMD2Triangle [header.numVertices];

		// read in the first frame of animation
		file.read((tBYTE *)pFrame, header.frameSize);
		//fread(pFrame, 1, header.frameSize, m_FilePointer);

		// Copy the name of the animation to our frames array
		sgxStrcpy((tCHAR *)&m_pFrames[i].strName[0], (const tCHAR *)&pFrame->name[0]);
			
		// Store off a vertex array pointer to cut down large lines of code
		sgxMD2Triangle *pVertices = m_pFrames[i].pVertices;

		// Go through all of the number of vertices and assign the scale and translations.
		// Store the vertices in our current frame's vertex list array, while swapping Y and Z.
		// Notice we also negate the Z axis as well to make the swap correctly.
		for (tUINT32 j=0; j < header.numVertices; ++j) {
			pVertices[j].vertex[0] = pFrame->aliasVertices[j].vertex[0] * pFrame->scale[0] + pFrame->translate[0];
			pVertices[j].vertex[2] = -1 * (pFrame->aliasVertices[j].vertex[1] * pFrame->scale[1] + pFrame->translate[1]);
			pVertices[j].vertex[1] = pFrame->aliasVertices[j].vertex[2] * pFrame->scale[2] + pFrame->translate[2];
		}
	}

	// create our animation list and store it in our model
	ParseAnimations(header.numFrames);

	// Go through every key frame and store it's vertices info in our pObject list.
	for (tUINT32 i=0; i < header.numFrames; ++i) {
		// create a local object to store the first frame of animation's data
		sgxMD2Object currentFrame;

		// Go through all of the vertices and assign them over to our structure
		for (tUINT32 j=0; j < header.numVertices; ++j) {
			currentFrame.m_VertexData.push_back(sgxVector3(m_pFrames[i].pVertices[j].vertex[0],m_pFrames[i].pVertices[j].vertex[1],m_pFrames[i].pVertices[j].vertex[2]));
		}

		// We can now free the old vertices stored in this frame of animation
		// since we have them stored in the pObject list of our model
		delete m_pFrames[i].pVertices;
	
		if(i == 0) {
		
			for(tUINT32 j=0; j < header.numTexCoords; ++j) {
				currentFrame.m_UVData.push_back(sgxUV(0,0,m_pTexCoords[j].u / float(header.skinWidth), 1 - m_pTexCoords[j].v / float(header.skinHeight)));
			}

			for(tUINT32 j=0; j < header.numTriangles; ++j) {
				sgxMD2Face face;

				face.m_iVertIndex[0] = m_pTriangles[j].vertexIndices[0];
				face.m_iVertIndex[1] = m_pTriangles[j].vertexIndices[1];
				face.m_iVertIndex[2] = m_pTriangles[j].vertexIndices[2];

				face.m_iCoordIndex[0] = m_pTriangles[j].textureIndices[0];
				face.m_iCoordIndex[1] = m_pTriangles[j].textureIndices[1];
				face.m_iCoordIndex[2] = m_pTriangles[j].textureIndices[2];

				currentFrame.m_FaceData.push_back(face);
			}
		}

		m_ObjectList.push_back(currentFrame);
	}

	delete m_pFrames;

	startAnimation(1);
}

CSGXAnimatedMeshMD2::~CSGXAnimatedMeshMD2() {
}

void
CSGXAnimatedMeshMD2::startAnimation(const tUINT32 animIndex) {
	CAnimatedMesh::startAnimation(animIndex);
	m_iCurrentFrame = m_AnimationList[animIndex].m_iStartFrame;
}

void
CSGXAnimatedMeshMD2::update(const tREAL32 telaps)
{
	if(!m_bPlaying || m_ObjectList.size() <= 0) {
		return;
	}
// TODO: m_AnimationData & m_pCurrentAnimation
	sgxMD2Animation *pAnim = &(m_AnimationList[m_iCurrentAnim]);

	m_fTime += (telaps / m_fFrameRate);

	if (m_fTime > 1.0f) {
		m_fTime = 0;
		if (++m_iCurrentFrame >= pAnim->m_iEndFrame) {
			if (m_bTransition) {
				startAnimation(m_iTransitionAnim);
				m_bTransition = FALSE;
			} else {
				m_iCurrentFrame =  pAnim->m_iStartFrame;
			}
		}
	}
//
	m_NextFrame = m_iCurrentFrame+1;
	if (m_NextFrame >= pAnim->m_iEndFrame) {
		m_NextFrame =  pAnim->m_iStartFrame;
	}
}


void
CSGXAnimatedMeshMD2::draw(const sgxMatrix43 &transform) const
{
	const sgxMD2Object &pFrame =		 m_ObjectList[m_iCurrentFrame];
	const sgxMD2Object &pNextFrame =  m_ObjectList[m_NextFrame];
	const sgxMD2Object &pFirstFrame = m_ObjectList[0];

	sgxGfxPolygon poly;
	poly.pTexture = m_pTexture;
	poly.vtx.push_back(sgxPolyVtx());
	poly.vtx.push_back(sgxPolyVtx());
	poly.vtx.push_back(sgxPolyVtx());

	sgxVector<sgxMD2Face>::const_iterator	it = pFirstFrame.m_FaceData.begin();
	sgxVector<sgxMD2Face>::const_iterator	fit = pFirstFrame.m_FaceData.begin();

	for(;it!=pFirstFrame.m_FaceData.end();++it,++fit) {
		for(tUINT32 vtxIndex = 0; vtxIndex < 3; ++vtxIndex) {
			tUINT32 m_iVertIndex = (*fit).m_iVertIndex[vtxIndex];
			tUINT32 texIndex  = (*fit).m_iCoordIndex[vtxIndex];

			poly.vtx[vtxIndex].u = pFirstFrame.m_UVData[texIndex].u2;
			poly.vtx[vtxIndex].v = pFirstFrame.m_UVData[texIndex].v2;
			poly.vtx[vtxIndex].vect = sgxLerp(pFrame.m_VertexData[m_iVertIndex], pNextFrame.m_VertexData[m_iVertIndex], m_fTime);
		}
		CSGXGraphicsEngine::get()->drawPolygon(poly, transform);
	}

}


