// Copyright (C) 2007-2009 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
	{
		//! First frame
		s32 first;
		//! Last frame
		s32 num;
		//! Looping frames
		s32 looping;
		//! Frames per second
		s32 fps;
	};


// 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. artefact md2
		s32	frameStart;		//starting position of frame-structur
		s32	tagStart;		//starting position of tag-structures
		s32	tagEnd;			//ending position of tag-structures/starting position of mesh-structures
		s32	fileSize;
	};

	//! this holds the header info of an MD3 mesh section
	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];
	};

	//! Texture 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::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 SMD3QuaternionTag
	{
		virtual ~SMD3QuaternionTag()
		{
			position.X = 0.f;
		}

		// construct copy constructor
		SMD3QuaternionTag( const SMD3QuaternionTag & copyMe )
		{
			*this = copyMe;
		}

		// construct for searching
		SMD3QuaternionTag( const core::stringc& name )
			: Name ( name ) {}

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

		// construct from a position and euler angles in degrees
		SMD3QuaternionTag ( const core::vector3df &pos, const core::vector3df &angle )
			: position(pos), rotation(angle * core::DEGTORAD) {}

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

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

		SMD3QuaternionTag & operator=( const SMD3QuaternionTag & copyMe )
		{
			Name = copyMe.Name;
			position = copyMe.position;
			rotation = copyMe.rotation;
			return *this;
		}

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

	//! holds a associative list of named quaternions
	struct SMD3QuaternionTagList
	{
		SMD3QuaternionTagList ()
		{
			Container.setAllocStrategy ( core::ALLOC_STRATEGY_SAFE );
		}

		// construct copy constructor
		SMD3QuaternionTagList( const SMD3QuaternionTagList & copyMe )
		{
			*this = copyMe;
		}

		virtual ~SMD3QuaternionTagList () {}

		SMD3QuaternionTag* get ( const core::stringc& name )
		{
			SMD3QuaternionTag search ( name );
			s32 index = Container.linear_search ( search );
			if ( index >= 0 )
				return &Container[index];
			return 0;
		}

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

		void set_used ( u32 new_size)
		{
			s32 diff = (s32) new_size - (s32) Container.allocated_size ();
			if ( diff > 0 )
			{
				SMD3QuaternionTag e ( "" );
				for ( s32 i = 0; i < diff; ++i )
					Container.push_back ( e );
			}
		}

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

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

		void push_back ( const SMD3QuaternionTag& other )
		{
			Container.push_back ( other );
		}

		SMD3QuaternionTagList& operator = (const SMD3QuaternionTagList & copyMe)
		{
			Container = copyMe.Container;
			return *this;
		}

	private:
		core::array < SMD3QuaternionTag > Container;
	};


	//! Holding Frames Buffers and Tag Infos
	struct SMD3Mesh: public IReferenceCounted
	{
		SMD3Mesh ()
		{
			MD3Header.numFrames = 0;
		}

		virtual ~SMD3Mesh()
		{
			for (u32 i=0; i<Buffer.size(); ++i)
				Buffer[i]->drop();
		}

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


	//! 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;

		//! get the tag list of the mesh.
		virtual SMD3QuaternionTagList *getTagList(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) = 0;

		//! get the original md3 mesh.
		virtual SMD3Mesh * getOriginalMesh () = 0;
	};

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

#endif

