// Author : Chris Tolley
// Date   : 1/14/2003
// ms3d.h contains the class definition for a class which loads and displays
// an ms3d file.  ms3d files are created with MilkShape3d

#include <cstdio>
#include <iostream>
#include <fstream>

using namespace std;

#include <windows.h>
#include <gl/gl.h>
#include "ms3d.h"
#include "logger.h"

// The relative file path to the model textures.
const string RELATIVE_PATH = "models/";

// Constructor
ms3d::ms3d(const char* ms3dFile)
{
	// Open a file to the ms3d file
	FILE* f = fopen( ms3dFile, "rb");

	// Check to see if the file was opened
	if( f == NULL )
	{
		logger::event( "ms3d::constructor", "File did not open", 0 );
		exit( -1 );
	}

	// Create a buffer of bytes and read in the ms3d header
	// An unsigned char is one byte long
	unsigned char buffer[10];
	int version[1];

	// Read in the header info
	fread( buffer, 1, 10, f );

	fread( version, 4, 1, f );
	
	// Do a version check to see if the file is of a valid version
	if( *version != 3 && *version != 4 )
	{
		logger::event( "ms3d::Constructor", "Incorrect file version.", 0 );
		fclose( f );
		return;
	}

	// Read in the number of vertices
	unsigned short int numVerts[1];
	fread( numVerts, 2, 1, f );

	
	// The vertex section of the ms3d file
	// Read in each vertex
	for( int n = 0; n < *numVerts; ++n )
	{
		// Create a temporary vertex
		ms3dVertex temp;

		// Read in the flag data
		fread( &temp.flags_, 1, 1, f );

		// Store the x,y.z coordinates in it
		fread( temp.vert_, 4, 3, f );

		// Store the bone data
		fread( &temp.bone_, 1, 1, f );

		// Read in the unused byte
		fread( &temp.unused_, 1, 1, f );

		// Add temp to points_;
		points_.push_back( temp );
	}

	// Triangle section of the ms3d file
	// Read in the number of triangles.
	unsigned short int numTriangles[1];
	fread( numTriangles, 2, 1, f );

	for( n = 0; n < *numTriangles; ++n )
	{
		// Create a temporary triangle to store the data.
		ms3dTriangle temp;

		// Read in the editor flag
		fread( &temp.flag_, 2, 1, f );
		
		// Read in the indexes to the vertices that make up the triangle
		fread( temp.VertIndexs_, 2, 3, f );

		// Read in the vertex normals used for lighting
		fread( temp.normal_[0], 4, 3, f );
		fread( temp.normal_[1], 4, 3, f );
		fread( temp.normal_[2], 4, 3, f );

		// Read in the texture coordinates for this triangle
		fread( temp.texCoords_[0], 4, 3, f );
		fread( temp.texCoords_[1], 4, 3, f );
		
		// Read in the unused byte
		fread( &temp.smoothing_, 1, 1, f );
		
		// unused byte
		fread( &temp.group_, 1, 1, f );

		// Add the new triangle to the vector of triangles.
		triangles_.push_back( temp );
	}

	// The mesh section of the ms3d file
	// Read in the number of meshs
	unsigned short int numMeshs[1];
	fread( numMeshs, 2, 1, f );

	// Read in each mesh data group into a ms3dMesh struct
	for( n = 0; n < *numMeshs; ++n )
	{
		// Create a temporary ms3dMesh to store the mesh data
		ms3dMesh temp;

		// Read in the 1byte flag
		fread( &temp.flag_, 1, 1, f );

		// Read in the 32 byte mesh name
		fread( &temp.name_, 1, 32, f );

		// Read in the number of triangles in this mesh
		fread( &temp.numTris_, 2, 1, f );

		// Read in each triangle index
		for( int n = 0; n < temp.numTris_; ++n )
		{
			unsigned short int tempIndex;
			fread( &tempIndex, 2, 1, f );
			temp.triIndexs_.push_back( tempIndex );
		}

		// Read in the material index
		fread( &temp.material_, 1, 1, f );

		// Add the temp mesh to the meshData vector
		meshData_.push_back( temp );
	}// End for n

	// Read in the material data from the ms3d file
	// Read in the number of materials
	unsigned short int numMaterials;
	fread( &numMaterials, 2, 1, f );

	// Read in each material and store it in a ms3dMaterial struct
	for( n = 0; n < numMaterials; ++n )
	{
		// Create a temporary ms3dMaterial to hold the data
		ms3dMaterial temp;

		// Read in the name of the material
		fread( temp.name_, 1, 32, f );

		// Read in the ambient value
		fread( temp.ambient_, 4, 4, f );

		// Read in the diffuse value
		fread( temp.diffuse_, 4, 4, f );

		// Read in the specular value
		fread( temp.specular_, 4, 4, f );

		// Read in the emissive value
		fread( temp.emissive_, 4, 4, f );

		// Read in the shininess value
		fread( &temp.shininess_, 4, 1, f );

		// Read in the transparency value
		fread( &temp.transparency_, 4, 1, f );

		// Read in the unused mode byte
		fread( &temp.mode_, 1, 1, f );

		// Read in the filename for the texture image
		char filename[128];
		fread( filename, 1, 128, f  );

		// Get rid of the entire file path and only keep the model name
		string file( filename );
		cout << file << endl;

		int n = file.size();
		while( file[n] != '\\' )
		{
			--n;
		}// End while n
		++n;

		string ActualFile;
		for( n; n < file.size(); ++n )
		{
			ActualFile += file[n];
		}// End for n

		// Read in the alpha map
		fread( temp.alpha_, 1, 128, f );

		// Load the actual texture image
		if( filename != NULL )
			temp.texture_.load( ( RELATIVE_PATH + ActualFile ).c_str() );

		// Add the temporary ms3dMaterial to the material data
		materialData_.push_back( temp );
	}// End for n

	// Read in 12 bytes that are only used by MilkShape
	char unused[12];
	fread( unused, 1, 12, f );

	// Read in the model joints
	// Read in the number of Joints
	unsigned short int numJoints;
	fread( &numJoints, 2, 1, f );

	// Read in the joints
	for( n = 0; n < numJoints; ++n )
	{
		// Create a temp joint
		ms3dJoint temp;

		// Read in the editor flag
		fread( &temp.flag_, 1, 1, f );

		// Read in the name of the joint
		fread( temp.name_, 1, 32, f );

		// Read in the name of the parent joint
		fread( temp.parent_, 1, 32, f );

		// Read in the starting rotation of the joint
		fread( temp.rotation_, 4, 3, f );

		// Read in the starting position of the joint
		fread( temp.position_, 4, 3, f );

		// Read in the number of rotation frames
		fread( &temp.numRotationFrames_, 2, 1, f );

		// Read in the number of translation frames
		fread( &temp.numTranslationFrames_, 2, 1, f );

		// Read in each rotation key frame
		for( int r = 0; r < temp.numRotationFrames_; ++r )
		{
			// Create a temp key frame
			ms3dKeyFrame tempkf;

			// Read in the time value
			fread( &tempkf.time_, 4, 1, f );

			// Read in the x,y,z values
			fread( tempkf.values_, 4, 3, f );

			// Add the temp key frame to the vector of rotation frames
			temp.rotationKeyFrames_.push_back( tempkf );
		}// End for r

		// Read in each translation key frame
		for( int t = 0; t < temp.numTranslationFrames_; ++t )
		{			
			// Create a temp key frame
			ms3dKeyFrame tempkf;

			// Read in the time value
			fread( &tempkf.time_, 4, 1, f );

			// Read in the x,y,z values
			fread( tempkf.values_, 4, 3, f );

			// Add the temp key frame to the vector of translation frames
			temp.translationKeyFrames_.push_back( tempkf );
		}// End for t

		// Add the temporary joint the joint vector
		joints_.push_back( temp );
	}// End for n

	// Close the file
	fclose( f );
}// End ms3d::Constructor

/*
// Does the math inorder to prepare the model for animation
void ms3d::setup()
{
	// For each joint and set up it's matrices
	vector< ms3dJoint >::iterator joint = joints_.begin();
	for( joint; joint != joints_.end(); ++joint )
	{
		// Set all the joint's matrices to the identity
		joint->absolute_.Identity();
		joint->final_.Identity();
		joint->local_.Identity();

		// Set up the joint's local matrix
		joint->local_.SetRotation( joint->rotation_ );
		joint->local_.SetTranslation( joint->position_ );

		// Set the joint's absolute matrix to combine the transformations of 
		// this joint and the joint's parent
		if( joint->parentIndex_ != -1 )
		{
			joint->absolute_ = joints_[joint->parentIndex_].absolute_ * 
								joint->local_;
		}// End if
		else
		{// If the joint has no parent, set the absolute matrix to the local
			joint->absolute_ = joint->local_;
		}// End else

		joint->final_ = joint->absolute_;
	}// End for joint

	// Now we move through each vertex on the model and move them to their
	// initial position
	vector< ms3dVertex >::iterator vert = points_.begin();
	for( vert; vert != points_.end(); ++vert )
	{
		// If there is a bone then we transform the vertex
		if( vert->bone_ != -1 )
		{
			// Get the m
			Matrix* mat = &joints_[ vert->bone_ ].final_;

			// Inverse translate and rotate the final matrix
			mat->InverseTranslateVec( vert->vert_ );
			mat->InverseRotateVec( vert->vert_ );
		}// End if
	}// End for vert

	// Now move through each normal and transform them
	vector< ms3dTriangle >::iterator tri = triangles_.begin();
	for( tri; tri != triangles_.end(); ++tri )
	{
		// Loop through each vertex that is part of this triangle
		for( int n = 0; n < 3; ++n )
		{
			// Get the current vertex of this triangle
			ms3dVertex* v = &points_[ tri->VertIndexs_[n] ];

			// If the vertex is attached to a bone
			if( v->bone_ != -1 )
			{
				// Get the joint
				ms3dJoint* j = &joints_[ v->bone_ ];

				// Transform the normal
				j->final_.InverseRotateVec( tri->normal_[n] );
			}// End if
		}// End for n
	}// End for tri
}// End ms3d::setup
*/


// Draws the model with texture in it's natural position
void ms3d::DrawTexturedMesh()
{
	// Enable texturing
	glEnable( GL_TEXTURE_2D );
	
	// Create an iterator to the mesh data
	vector<ms3dMesh>::iterator it = meshData_.begin();
	for( it; it != meshData_.end(); ++it )
	{
		ms3dMaterial* currMat;
		// Set up the material properties of each mesh
		if( it->material_ >= 0 )
		{
			currMat = &materialData_[ it->material_ ];

			// Set the alpha  for transparency
			currMat->diffuse_[3] = currMat->transparency_;

			glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, currMat->ambient_ );
			glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, currMat->diffuse_ );
			glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, currMat->specular_ );
			glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, currMat->emissive_ );
			glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, currMat->shininess_ );
			glEnable( GL_BLEND );
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

			// Tell OpenGL to use this Mesh's texture when it renders textures.
			currMat->texture_.select();
		}// End if
		else
			glDisable( GL_BLEND );

		// Tell OpenGl to draw triangles
		glBegin( GL_TRIANGLES );
	
		// Loop thru each triangle in each mesh and draw it
		for( int n = 0; n < it->numTris_; ++n )
		{
			// Create a pointer to the current triangle
			ms3dTriangle* current = &triangles_[ ( it->triIndexs_.at(n) ) ];

			// Send the normal to OpenGL for lighting
			glNormal3fv( current->normal_[0] );
			// Send the texture coordinates
			glTexCoord2f( current->texCoords_[0][0], current->texCoords_[1][0] );
			// Send the vertex position
			glVertex3fv( points_[ current->VertIndexs_[0] ].vert_ );

			// Send the next set in the same way
			glNormal3fv( current->normal_[1] );
			glTexCoord2f( current->texCoords_[0][1], current->texCoords_[1][1] );
			glVertex3fv( points_[ current->VertIndexs_[1] ].vert_ );

			// Send the last set in the same way
			glNormal3fv( current->normal_[2] );
			glTexCoord2f( current->texCoords_[0][2], current->texCoords_[1][2] );
			glVertex3fv( points_[ current->VertIndexs_[2] ].vert_ );
		}// End for n

		// End the drawing of triangles
		glEnd();

		currMat->texture_.deselect();
	}// End for it

	// Disable texturing
	glDisable( GL_TEXTURE_2D );
}// End ms3d::DrawTexturedMest



