// Copyright (C) 2002-2007 Nikolaus Gebhardt / Thomas Alten
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#ifndef __I_ANIMATED_MESH_MD3_H_INCLUDED__
#define __I_ANIMATED_MESH_MD3_H_INCLUDED__

#include "IAnimatedMesh.h"
#include "IQ3Shader.h"
#include "quaternion.h"

namespace irr
{
namespace scene
{

	enum eMD3Models
	{
		EMD3_HEAD = 0,
		EMD3_UPPER,
		EMD3_LOWER,
		EMD3_WEAPON,
		EMD3_NUMMODELS
	};


	// Animation list
	enum EMD3_ANIMATION_TYPE
	{
		// Animations for both lower and upper parts of the player
		EMD3_BOTH_DEATH_1 = 0,
		EMD3_BOTH_DEAD_1,
		EMD3_BOTH_DEATH_2,
		EMD3_BOTH_DEAD_2,
		EMD3_BOTH_DEATH_3,
		EMD3_BOTH_DEAD_3,

		// Animations for the upper part
		EMD3_TORSO_GESTURE,
		EMD3_TORSO_ATTACK_1,
		EMD3_TORSO_ATTACK_2,
		EMD3_TORSO_DROP,
		EMD3_TORSO_RAISE,
		EMD3_TORSO_STAND_1,
		EMD3_TORSO_STAND_2,

		// Animations for the lower part
		EMD3_LEGS_WALK_CROUCH,
		EMD3_LEGS_WALK,
		EMD3_LEGS_RUN,
		EMD3_LEGS_BACK,
		EMD3_LEGS_SWIM,
		EMD3_LEGS_JUMP_1,
		EMD3_LEGS_LAND_1,
		EMD3_LEGS_JUMP_2,
		EMD3_LEGS_LAND_2,
		EMD3_LEGS_IDLE,
		EMD3_LEGS_IDLE_CROUCH,
		EMD3_LEGS_TURN,

		//! Not an animation, but amount of animation types.
		EMD3_ANIMATION_COUNT
	};

	struct SMD3AnimationInfo
	{
		s32 first;     // First frame
		s32 num;       // Last frame
		s32 looping;   // Looping frames
		s32 fps;       // Frames per second
	};


// byte-align structures
#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__) 
#	pragma pack( push, packing )
#	pragma pack( 1 )
#	define PACK_STRUCT
#elif defined( __GNUC__ )
#	define PACK_STRUCT	__attribute__((packed))
#else
#	error compiler not supported
#endif

	// this holds the header info of the MD3 file
	struct SMD3Header
	{
		c8	headerID[4];	//id of file, always "IDP3" 
		s32	Version;		//this is a version number, always 15 
		s8	fileName[68];	//sometimes left Blank... 65 chars, 32bit aligned == 68 chars 
		s32	numFrames;		//number of KeyFrames
		s32	numTags;		//number of 'tags' per frame
		s32	numMeshes;		//number of meshes/skins 
		s32	numMaxSkins;	//maximum number of unique skins used in md3 file 
		s32	headerSize;		//always equal to the length of this header 
		s32	tagStart;		//starting position of tag-structures 
		s32	tagEnd;			//ending position of tag-structures/starting position of mesh-structures 
		s32	fileSize;
	};

	struct SMD3MeshHeader
	{
		c8 meshID[4];			//id, must be IDP3 
		c8 meshName[68];		//name of mesh 65 chars, 32 bit aligned == 68 chars

		s32 numFrames;			//number of meshframes in mesh 
		s32 numShader;			//number of skins in mesh 
		s32 numVertices;		//number of vertices 
		s32 numTriangles;		//number of Triangles 

		s32 offset_triangles;	//starting position of Triangle data, relative to start of Mesh_Header 
		s32 offset_shaders;		//size of header 
		s32 offset_st;			//starting position of texvector data, relative to start of Mesh_Header 
		s32 vertexStart;		//starting position of vertex data,relative to start of Mesh_Header 
		s32 offset_end;
	};


	//! Compressed Vertex Data
	struct SMD3Vertex
	{
		s16 position[3];
		u8 normal[2];
	};

	//! Texure Coordinate
	struct SMD3TexCoord
	{
		f32 u;
		f32 v;
	};

	//! Triangle Index
	struct SMD3Face
	{
		s32 Index[3];
	};


// Default alignment
#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__) 
#	pragma pack( pop, packing )
#endif

#undef PACK_STRUCT

	//! Holding Frame Data for a Mesh
	struct SMD3MeshBuffer : public IReferenceCounted
	{
		SMD3MeshHeader MeshHeader;

		core::array < core::stringc > Shader;
		core::array < s32 > Indices;
		core::array < SMD3Vertex > Vertices;
		core::array < SMD3TexCoord > Tex;
	};

	//! hold a tag info for connecting meshes
	//! basically its an alternate way to describe a transformation
	struct SMD3QuaterionTag
	{
		SMD3QuaterionTag() {}

		SMD3QuaterionTag( const core::stringc& name )
			: Name ( name ) {}

		// construct from a matrix
		SMD3QuaterionTag ( const core::stringc& name, const core::matrix4 &m )
		{
			Name = name;
			position = m.getTranslation ();
			rotation = m;
		}

		// set to matrix
		void setto ( core::matrix4 &m )
		{
			rotation.getMatrix ( m );
			m.setTranslation ( position );
		}

		// construct from a position and euler angles in degrees
		SMD3QuaterionTag ( const core::vector3df&pos, const core::vector3df &angle )
		{
			position = pos;
			rotation.set ( angle.X * core::DEGTORAD, angle.Y * core::DEGTORAD, angle.Z * core::DEGTORAD );
		}

		core::stringc Name;
		core::vector3df position;
		core::quaternion rotation;

		bool operator == ( const SMD3QuaterionTag &other ) const
		{
			return Name == other.Name;
		}
	};

	// holds a assoziative list of named quaternions
	struct SMD3QuaterionTagList : public virtual IReferenceCounted
	{
		SMD3QuaterionTag* get ( const core::stringc& name )
		{
			SMD3QuaterionTag search ( name );
			s32 index = Container.linear_search ( search );
			if ( index >= 0 )
				return &Container[index];
			return 0;
		}

		u32 size () const
		{
			return Container.size();
		}

		SMD3QuaterionTag& operator[] (u32 index )
		{
			return Container[index];
		}


		core::array < SMD3QuaterionTag > Container;
	};


	//! Holding Frames Buffers and Tag Infos
	struct SMD3Mesh: public IReferenceCounted
	{
		~SMD3Mesh()
		{
			for (u32 i=0; i<Buffer.size(); ++i)
				Buffer[i]->drop();
		};

		SMD3Header MD3Header;
		core::stringc Name;
		core::array < SMD3MeshBuffer * > Buffer;
		SMD3QuaterionTagList TagList;
	};


	//! Interface for using some special functions of MD3 meshes
	class IAnimatedMeshMD3 : public IAnimatedMesh
	{
	public:

		//! tune how many frames you want to render inbetween
		virtual void setInterpolationShift ( u32 shift, u32 loopMode ) = 0;

		virtual SMD3QuaterionTagList *getTagList(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) = 0;

		virtual SMD3Mesh * getOriginalMesh () = 0;
	};

} // end namespace scene
} // end namespace irr

#endif

