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

#ifndef __C_ANIMATED_MESH_HALFLIFE_H_INCLUDED__
#define __C_ANIMATED_MESH_HALFLIFE_H_INCLUDED__

#include "IAnimatedMesh.h"
#include "ISceneManager.h"
#include "irrArray.h"
#include "irrString.h"
#include "IMeshLoader.h"
#include "SMesh.h"
#include "IReadFile.h"

namespace irr
{
namespace scene
{


#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

	// STUDIO MODELS, Copyright (c) 1998, Valve LLC. All rights reserved.
	#define MAXSTUDIOTRIANGLES	20000	// TODO: tune this
	#define MAXSTUDIOVERTS		2048	// TODO: tune this
	#define MAXSTUDIOSEQUENCES	256		// total animation sequences
	#define MAXSTUDIOSKINS		100		// total textures
	#define MAXSTUDIOSRCBONES	512		// bones allowed at source movement
	#define MAXSTUDIOBONES		128		// total bones actually used
	#define MAXSTUDIOMODELS		32		// sub-models per model
	#define MAXSTUDIOBODYPARTS	32
	#define MAXSTUDIOGROUPS		4
	#define MAXSTUDIOANIMATIONS	512		// per sequence
	#define MAXSTUDIOMESHES		256
	#define MAXSTUDIOEVENTS		1024
	#define MAXSTUDIOPIVOTS		256
	#define MAXSTUDIOCONTROLLERS 8

	typedef f32 vec3_hl[3];	// x,y,z
	typedef f32 vec4_hl[4];	// x,y,z,w

	struct SHalflifeHeader
	{
		c8 id[4];
		s32 version;

		c8 name[64];
		s32 length;

		vec3_hl eyeposition;	// ideal eye position
		vec3_hl min;			// ideal movement hull size
		vec3_hl max;

		vec3_hl bbmin;			// clipping bounding box
		vec3_hl bbmax;

		s32	flags;

		u32	numbones;			// bones
		u32	boneindex;

		u32	numbonecontrollers;		// bone controllers
		u32	bonecontrollerindex;

		u32	numhitboxes;			// complex bounding boxes
		u32	hitboxindex;

		u32	numseq;				// animation sequences
		u32	seqindex;

		u32	numseqgroups;		// demand loaded sequences
		u32	seqgroupindex;

		u32	numtextures;		// raw textures
		u32	textureindex;
		u32	texturedataindex;

		u32	numskinref;			// replaceable textures
		u32	numskinfamilies;
		u32	skinindex;

		u32	numbodyparts;
		u32	bodypartindex;

		u32	numattachments;		// queryable attachable points
		u32	attachmentindex;

		s32	soundtable;
		s32	soundindex;
		s32	soundgroups;
		s32	soundgroupindex;

		s32 numtransitions;		// animation node to animation node transition graph
		s32	transitionindex;
	} PACK_STRUCT;

	// header for demand loaded sequence group data
	typedef struct
	{
		s32 id;
		s32 version;

		c8 name[64];
		s32 length;
	} PACK_STRUCT studioseqhdr_t;

	// bones
	struct SHalflifeBone
	{
		c8 name[32];	// bone name for symbolic links
		s32 parent;		// parent bone
		s32 flags;		// ??
		s32 bonecontroller[6];	// bone controller index, -1 == none
		f32 value[6];	// default DoF values
		f32 scale[6];   // scale for delta DoF values
	} PACK_STRUCT;


	// bone controllers
	struct SHalflifeBoneController
	{
		s32 bone;	// -1 == 0
		s32 type;	// X, Y, Z, XR, YR, ZR, M
		f32 start;
		f32 end;
		s32 rest;	// byte index value at rest
		s32 index;	// 0-3 user set controller, 4 mouth
	} PACK_STRUCT;

	// intersection boxes
	struct SHalflifeBBox
	{
		s32 bone;
		s32 group;			// intersection group
		vec3_hl bbmin;		// bounding box
		vec3_hl bbmax;
	} PACK_STRUCT;

#ifndef ZONE_H
	// NOTE: this was a void*, but that crashes on 64bit. 
	// I have found no mdl format desc, so not sure what it's meant to be, but s32 at least works. 
	typedef s32 cache_user_t;	
#endif

	// demand loaded sequence groups
	struct SHalflifeSequenceGroup
	{
		c8 label[32];	// textual name
		c8 name[64];	// file name
		cache_user_t cache;		// cache index pointer
		s32 data;		// hack for group 0
	} PACK_STRUCT;

	// sequence descriptions
	struct SHalflifeSequence
	{
		c8 label[32];	// sequence label

		f32 fps;		// frames per second
		s32 flags;		// looping/non-looping flags

		s32 activity;
		s32 actweight;

		s32 numevents;
		s32 eventindex;

		s32 numframes;	// number of frames per sequence

		u32 numpivots;	// number of foot pivots
		u32 pivotindex;

		s32 motiontype;
		s32 motionbone;
		vec3_hl linearmovement;
		s32 automoveposindex;
		s32 automoveangleindex;

		vec3_hl bbmin;		// per sequence bounding box
		vec3_hl bbmax;

		s32 numblends;
		s32 animindex;		// SHalflifeAnimOffset pointer relative to start of sequence group data
		// [blend][bone][X, Y, Z, XR, YR, ZR]

		s32 blendtype[2];	// X, Y, Z, XR, YR, ZR
		f32 blendstart[2];	// starting value
		f32 blendend[2];	// ending value
		s32 blendparent;

		s32 seqgroup;		// sequence group for demand loading

		s32 entrynode;		// transition node at entry
		s32 exitnode;		// transition node at exit
		s32 nodeflags;		// transition rules

		s32 nextseq;		// auto advancing sequences
	} PACK_STRUCT;

	// events
	typedef struct
	{
		s32 frame;
		s32 event;
		s32 type;
		c8 options[64];
	} PACK_STRUCT mstudioevent_t;


	// pivots
	typedef struct
	{
		vec3_hl org;	// pivot point
		s32 start;
		s32 end;
	} PACK_STRUCT mstudiopivot_t;

	// attachment
	struct SHalflifeAttachment
	{
		c8 name[32];
		s32 type;
		s32 bone;
		vec3_hl org;	// attachment point
		vec3_hl vectors[3];
	} PACK_STRUCT;

	struct SHalflifeAnimOffset
	{
		u16	offset[6];
	} PACK_STRUCT;

	// animation frames
	union SHalflifeAnimationFrame
	{
		struct {
			u8	valid;
			u8	total;
		} PACK_STRUCT num;
		s16		value;
	} PACK_STRUCT;


	// body part index
	struct SHalflifeBody
	{
		c8 name[64];
		u32 nummodels;
		u32 base;
		u32 modelindex; // index into models array
	} PACK_STRUCT;


	// skin info
	struct SHalflifeTexture
	{
		c8 name[64];
		s32 flags;
		s32 width;
		s32 height;
		s32 index;
	} PACK_STRUCT;


	// skin families
	// short	index[skinfamilies][skinref]

	// studio models
	struct SHalflifeModel
	{
		c8 name[64];
		s32 type;

		f32	boundingradius;

		u32	nummesh;
		u32	meshindex;

		u32	numverts;		// number of unique vertices
		u32	vertinfoindex;	// vertex bone info
		u32	vertindex;		// vertex vec3_hl
		u32	numnorms;		// number of unique surface normals
		u32	norminfoindex;	// normal bone info
		u32	normindex;		// normal vec3_hl

		u32	numgroups;		// deformation groups
		u32	groupindex;
	} PACK_STRUCT;


	// meshes
	typedef struct
	{
		u32	numtris;
		u32	triindex;
		u32	skinref;
		u32	numnorms;		// per mesh normals
		u32	normindex;		// normal vec3_hl
	} PACK_STRUCT SHalflifeMesh;

	// lighting options
	#define STUDIO_NF_FLATSHADE		0x0001
	#define STUDIO_NF_CHROME		0x0002
	#define STUDIO_NF_FULLBRIGHT	0x0004

	// motion flags
	#define STUDIO_X		0x0001
	#define STUDIO_Y		0x0002
	#define STUDIO_Z		0x0004
	#define STUDIO_XR		0x0008
	#define STUDIO_YR		0x0010
	#define STUDIO_ZR		0x0020
	#define STUDIO_LX		0x0040
	#define STUDIO_LY		0x0080
	#define STUDIO_LZ		0x0100
	#define STUDIO_AX		0x0200
	#define STUDIO_AY		0x0400
	#define STUDIO_AZ		0x0800
	#define STUDIO_AXR		0x1000
	#define STUDIO_AYR		0x2000
	#define STUDIO_AZR		0x4000
	#define STUDIO_TYPES	0x7FFF
	#define STUDIO_RLOOP	0x8000	// controller that wraps shortest distance

	// sequence flags
	#define STUDIO_LOOPING	0x0001

	// bone flags
	#define STUDIO_HAS_NORMALS	0x0001
	#define STUDIO_HAS_VERTICES 0x0002
	#define STUDIO_HAS_BBOX		0x0004
	#define STUDIO_HAS_CHROME	0x0008	// if any of the textures have chrome on them

	#define RAD_TO_STUDIO		(32768.0/M_PI)
	#define STUDIO_TO_RAD		(M_PI/32768.0)

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

	/*!
		Textureatlas
		Combine Source Images with arbitrary size and bithdepth to an Image with 2^n size
		borders from the source images are copied around for allowing filtering ( bilinear, mipmap )
	*/
	struct STextureAtlas
	{
		STextureAtlas ()
		{
			release();
		}

		virtual ~STextureAtlas ()
		{
			release ();
		}

		void release ();
		void addSource ( const c8 * name, video::IImage * image );
		void create ( u32 pixelborder, video::E_TEXTURE_CLAMP texmode );
		void getScale ( core::vector2df &scale );
		void getTranslation ( const c8 * name, core::vector2di &pos );

		struct TextureAtlasEntry
		{
			io::path name;
			u32 width;
			u32 height;

			core::vector2di pos;

			video::IImage * image;

			bool operator < ( const TextureAtlasEntry & other )
			{
				return height > other.height;
			}
		};


		core::array < TextureAtlasEntry > atlas;
		video::IImage * Master;
	};


	//! Possible types of Animation Type
	enum E_ANIMATION_TYPE
	{
		//! No Animation
		EAMT_STILL,
		//! From Start to End, then Stop ( Limited Line )
		EAMT_WAYPOINT,
		//! Linear Cycling Animation	 ( Sawtooth )
		EAMT_LOOPING,
		//! Linear bobbing				 ( Triangle )
		EAMT_PINGPONG
	};

	//! Names for Animation Type
	const c8* const MeshAnimationTypeNames[] =
	{
		"still",
		"waypoint",
		"looping",
		"pingpong",
		0
	};


	//! Data for holding named Animation Info
	struct KeyFrameInterpolation
	{
		core::stringc Name;		// Name of the current Animation/Bone
		E_ANIMATION_TYPE AnimationType;	// Type of Animation ( looping, usw..)

		f32 CurrentFrame;		// Current Frame
		s32 NextFrame;			// Frame which will be used next. For blending

		s32 StartFrame;			// Absolute Frame where the current animation start
		s32 Frames;				// Relative Frames how much Frames this animation have
		s32 LoopingFrames;		// How much of Frames sould be looped
		s32 EndFrame;			// Absolute Frame where the current animation ends End = start + frames - 1

		f32 FramesPerSecond;	// Speed in Frames/Seconds the animation is played
		f32 RelativeSpeed;		// Factor Original fps is modified

		u32 BeginTime;			// Animation started at this thime
		u32 EndTime;			// Animation end at this time
		u32 LastTime;			// Last Keyframe was done at this time

		KeyFrameInterpolation ( const c8 * name = "", s32 start = 0, s32 frames = 0, s32 loopingframes = 0,
								f32 fps = 0.f, f32 relativefps = 1.f  )
			: Name ( name ), AnimationType ( loopingframes ? EAMT_LOOPING : EAMT_WAYPOINT),
			CurrentFrame ( (f32) start ), NextFrame ( start ), StartFrame ( start ),
			Frames ( frames ), LoopingFrames ( loopingframes ), EndFrame ( start + frames - 1 ),
			FramesPerSecond ( fps ), RelativeSpeed ( relativefps ),
			BeginTime ( 0 ), EndTime ( 0 ), LastTime ( 0 )
		{
		}

		// linear search
		bool operator == ( const KeyFrameInterpolation & other ) const
		{
			return Name.equals_ignore_case ( other.Name );
		}

	};


	//! a List holding named Animations
	typedef core::array < KeyFrameInterpolation > IAnimationList;

	//! a List holding named Skins
	typedef core::array < core::stringc > ISkinList;


	// Current Model per Body
	struct SubModel
	{
		core::stringc name;
		u32 startBuffer;
		u32 endBuffer;
		u32 state;
	};

	struct BodyPart
	{
		core::stringc name;
		u32 defaultModel;
		core::array < SubModel > model;
	};
	//! a List holding named Models and SubModels
	typedef core::array < BodyPart > IBodyList;


	class CAnimatedMeshHalfLife : public IAnimatedMesh
	{
	public:

		//! constructor
		CAnimatedMeshHalfLife();

		//! destructor
		virtual ~CAnimatedMeshHalfLife();

		//! loads a Halflife mdl file
		virtual bool loadModelFile( io::IReadFile* file, ISceneManager * smgr );

		//IAnimatedMesh
		virtual u32 getFrameCount() const;
		virtual IMesh* getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop);
		virtual const core::aabbox3d<f32>& getBoundingBox() const;
		virtual E_ANIMATED_MESH_TYPE getMeshType() const;
		virtual void renderModel ( u32 param, video::IVideoDriver * driver, const core::matrix4 &absoluteTransformation);

		//! returns amount of mesh buffers.
		virtual u32 getMeshBufferCount() const;
		//! returns pointer to a mesh buffer
		virtual IMeshBuffer* getMeshBuffer(u32 nr) const;
		//! Returns pointer to a mesh buffer which fits a material
		virtual IMeshBuffer* getMeshBuffer( const video::SMaterial &material) const;

		virtual void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue);

		//! set the hardware mapping hint, for driver
		virtual void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer=EBT_VERTEX_AND_INDEX);

		//! flags the meshbuffer as changed, reloads hardware buffers
		virtual void setDirty(E_BUFFER_TYPE buffer=EBT_VERTEX_AND_INDEX);

		//! set user axis aligned bounding box
		virtual void setBoundingBox(const core::aabbox3df& box);

		//! Get the Animation List
		virtual IAnimationList* getAnimList () { return &AnimList; }

		//! Return the named Body List of this Animated Mesh
		virtual IBodyList *getBodyList() { return &BodyList; }

	private:

		// KeyFrame Animation List
		IAnimationList AnimList;
		// Sum of all sequences
		u32 FrameCount;

		// Named meshes of the Body
		IBodyList BodyList;

		//! return a Mesh per frame
		SMesh MeshIPol;

		ISceneManager *SceneManager;

		SHalflifeHeader *Header;
		SHalflifeHeader *TextureHeader;
		bool OwnTexModel;						// do we have a modelT.mdl ?
		SHalflifeHeader *AnimationHeader[32];	// sequences named model01.mdl, model02.mdl

		void initData ();
		SHalflifeHeader * loadModel( io::IReadFile* file, const io::path &filename );
		bool postLoadModel( const io::path &filename );

		u32 SequenceIndex;	// sequence index
		f32 CurrentFrame;	// Current Frame

		#define MOUTH_CONTROLLER	4
		u8 BoneController[4 + 1 ]; // bone controllers + mouth position
		u8 Blending[2]; // animation blending

		f32 SetController( s32 controllerIndex, f32 value );

		u32 SkinGroupSelection; // skin group selection
		u32 SetSkin( u32 value );

		void initModel ();
		void dumpModelInfo ( u32 level);

		void ExtractBbox( s32 sequence, core::aabbox3df &box );

		void setUpBones ();
		SHalflifeAnimOffset * getAnim( SHalflifeSequence *seq );
		void slerpBones( vec4_hl q1[], vec3_hl pos1[], vec4_hl q2[], vec3_hl pos2[], f32 s );
		void calcRotations ( vec3_hl *pos, vec4_hl *q, SHalflifeSequence *seq, SHalflifeAnimOffset *anim, f32 f );

		vec4_hl BoneAdj;
		void calcBoneAdj();
		void calcBoneQuaternion(const s32 frame, const SHalflifeBone *bone, SHalflifeAnimOffset *anim, const u32 j, f32& angle1, f32& angle2) const;
		void calcBonePosition(const s32 frame, f32 s, const SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *pos ) const;

		void buildVertices ();

		io::path TextureBaseName;

#define HL_TEXTURE_ATLAS

#ifdef HL_TEXTURE_ATLAS
		STextureAtlas TextureAtlas;
		video::ITexture *TextureMaster;
#endif

	};


	//! Meshloader capable of loading HalfLife Model files
	class CHalflifeMDLMeshFileLoader : public IMeshLoader
	{
	public:

		//! Constructor
		CHalflifeMDLMeshFileLoader( scene::ISceneManager* smgr );

		//! returns true if the file maybe is able to be loaded by this class
		/** based on the file extension (e.g. ".bsp") */
		virtual bool isALoadableFileExtension(const io::path& filename) const;

		//! creates/loads an animated mesh from the file.
		/** \return Pointer to the created mesh. Returns 0 if loading failed.
		If you no longer need the mesh, you should call IAnimatedMesh::drop().
		See IReferenceCounted::drop() for more information.
		*/
		virtual IAnimatedMesh* createMesh(io::IReadFile* file);

	private:
		scene::ISceneManager* SceneManager;
	};


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

#endif

