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

#ifndef __C_Q3_LEVEL_MESH_H_INCLUDED__
#define __C_Q3_LEVEL_MESH_H_INCLUDED__

#include "IQ3LevelMesh.h"
#include "IReadFile.h"
#include "IFileSystem.h"
#include "SMesh.h"
#include "SMeshBufferLightMap.h"
#include "IVideoDriver.h"
#include "irrString.h"
#include "ISceneManager.h"
#include "os.h"

namespace irr
{
namespace scene
{
	class CQ3LevelMesh : public IQ3LevelMesh
	{
	public:

		//! constructor
		CQ3LevelMesh(io::IFileSystem* fs, video::IVideoDriver* driver,  scene::ISceneManager* smgr);

		//! destructor
		virtual ~CQ3LevelMesh();

		//! loads a level from a .bsp-File. Also tries to load all needed textures. Returns true if successful.
		bool loadFile(io::IReadFile* file);

		//! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh.
		virtual u32 getFrameCount() const;

		//! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level.
		virtual IMesh* getMesh(s32 frameInMs, s32 detailLevel=255, s32 startFrameLoop=-1, s32 endFrameLoop=-1);

		virtual void releaseMesh ( s32 index );

		//! Returns an axis aligned bounding box of the mesh.
		//! \return A bounding box of this mesh is returned.
		virtual const core::aabbox3d<f32>& getBoundingBox() const;

		virtual void setBoundingBox( const core::aabbox3df& box);


		//! Returns the type of the animated mesh.
		virtual E_ANIMATED_MESH_TYPE getMeshType() const;

		//! loads the shader definition
		virtual const quake3::SShader * getShader ( const c8 * filename, s32 fileNameIsValid );

		//! returns a already loaded Shader
		virtual const quake3::SShader * getShader ( u32 index  ) const;


		//! get's an interface to the entities
		virtual const quake3::tQ3EntityList & getEntityList ();



		//Link to held meshes? ...


		//! returns amount of mesh buffers.
		virtual u32 getMeshBufferCount() const
		{
			return 0;
		}

		//! returns pointer to a mesh buffer
		virtual IMeshBuffer* getMeshBuffer(u32 nr) const
		{
			return 0;
		}

		//! Returns pointer to a mesh buffer which fits a material
 		/** \param material: material to search for
		\return Returns the pointer to the mesh buffer or
		NULL if there is no such mesh buffer. */
		virtual IMeshBuffer* getMeshBuffer( const video::SMaterial &material) const
		{
			return 0;
		}

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




	private:

		//! constructs a mesh from the quake 3 level file.
		void constructMesh();

		//! loads the textures
		void loadTextures();

		void constructMesh2();

		void loadTextures2();

		struct STexShader
		{
			video::ITexture* Texture;
			s32 ShaderID;
		};

		core::array< STexShader > Tex;
		core::array<video::ITexture*> Lightmap;

		enum eLumps
		{
			kEntities = 0,     // Stores player/object positions, etc...
			kTextures,         // Stores texture information
			kPlanes,           // Stores the splitting planes
			kNodes,            // Stores the BSP nodes
			kLeafs,            // Stores the leafs of the nodes
			kLeafFaces,        // Stores the leaf's indices into the faces
			kLeafBrushes,      // Stores the leaf's indices into the brushes
			kModels,           // Stores the info of world models
			kBrushes,          // Stores the brushes info (for collision)
			kBrushSides,       // Stores the brush surfaces info
			kVertices,         // Stores the level vertices
			kMeshVerts,        // Stores the model vertices offsets
			kShaders,          // Stores the shader files (blending, anims..)
			kFaces,            // Stores the faces for the level
			kLightmaps,        // Stores the lightmaps for the level
			kLightVolumes,     // Stores extra world lighting information
			kVisData,          // Stores PVS and cluster info (visibility)
			kMaxLumps          // A constant to store the number of lumps
		};

		struct tBSPLump
		{
			s32 offset;
			s32 length;
		};

		struct tBSPHeader
		{
			s32 strID;     // This should always be 'IBSP'
			s32 version;       // This should be 0x2e for Quake 3 files
		};

		struct tBSPVertex
		{
			f32 vPosition[3];      // (x, y, z) position.
			f32 vTextureCoord[2];  // (u, v) texture coordinate
			f32 vLightmapCoord[2]; // (u, v) lightmap coordinate
			f32 vNormal[3];        // (x, y, z) normal vector
			u8 color[4];           // RGBA color for the vertex
		};

		struct tBSPFace
		{
			s32 textureID;        // The index into the texture array
			s32 effect;           // The index for the effects (or -1 = n/a)
			s32 type;             // 1=polygon, 2=patch, 3=mesh, 4=billboard
			s32 vertexIndex;      // The index into this face's first vertex
			s32 numOfVerts;       // The number of vertices for this face
			s32 meshVertIndex;    // The index into the first meshvertex
			s32 numMeshVerts;     // The number of mesh vertices
			s32 lightmapID;       // The texture index for the lightmap
			s32 lMapCorner[2];    // The face's lightmap corner in the image
			s32 lMapSize[2];      // The size of the lightmap section
			f32 lMapPos[3];     // The 3D origin of lightmap.
			f32 lMapBitsets[2][3]; // The 3D space for s and t unit vectors.
			f32 vNormal[3];     // The face normal.
			s32 size[2];          // The bezier patch dimensions.
		};

		struct tBSPTexture
		{
			c8 strName[64];   // The name of the texture w/o the extension
			u32 flags;          // The surface flags (unknown)
			u32 contents;       // The content flags (unknown)
		};

		struct tBSPLightmap
		{
			u8 imageBits[128][128][3];   // The RGB data in a 128x128 image
		};

		struct tBSPNode
		{
			s32 plane;      // The index into the planes array
			s32 front;      // The child index for the front node
			s32 back;       // The child index for the back node
			s32 mins[3];    // The bounding box min position.
			s32 maxs[3];    // The bounding box max position.
		};

		struct tBSPLeaf
		{
			s32 cluster;           // The visibility cluster
			s32 area;              // The area portal
			s32 mins[3];           // The bounding box min position
			s32 maxs[3];           // The bounding box max position
			s32 leafface;          // The first index into the face array
			s32 numOfLeafFaces;    // The number of faces for this leaf
			s32 leafBrush;         // The first index for into the brushes
			s32 numOfLeafBrushes;  // The number of brushes for this leaf
		};

		struct tBSPPlane
		{
			f32 vNormal[3];     // Plane normal.
			f32 d;              // The plane distance from origin
		};

		struct tBSPVisData
		{
			s32 numOfClusters;   // The number of clusters
			s32 bytesPerCluster; // Bytes (8 bits) in the cluster's bitset
			c8 *pBitsets;      // Array of bytes holding the cluster vis.
		};

		struct tBSPBrush
		{
			s32 brushSide;           // The starting brush side for the brush
			s32 numOfBrushSides;     // Number of brush sides for the brush
			s32 textureID;           // The texture index for the brush
		};

		struct tBSPBrushSide
		{
			s32 plane;              // The plane index
			s32 textureID;          // The texture index
		};

		struct tBSPModel
		{
			f32 min[3];           // The min position for the bounding box
			f32 max[3];           // The max position for the bounding box.
			s32 faceIndex;          // The first face index in the model
			s32 numOfFaces;         // The number of faces in the model
			s32 brushIndex;         // The first brush index in the model
			s32 numOfBrushes;       // The number brushes for the model
		};

		struct tBSPShader
		{
			c8 strName[64];     // The name of the shader file
			s32 brushIndex;       // The brush index for this shader
			s32 unknown;          // This is 99% of the time 5
		};

		struct tBSPLights
		{
			u8 ambient[3];     // This is the ambient color in RGB
			u8 directional[3]; // This is the directional color in RGB
			u8 direction[2];   // The direction of the light: [phi,theta]
		};

		void loadTextures	(tBSPLump* l, io::IReadFile* file);		// Load the textures
		void loadLightmaps  (tBSPLump* l, io::IReadFile* file);      // Load the lightmaps
		void loadVerts      (tBSPLump* l, io::IReadFile* file);		// Load the vertices
		void loadFaces      (tBSPLump* l, io::IReadFile* file);		// Load the faces
		void loadPlanes     (tBSPLump* l, io::IReadFile* file);		// Load the Planes of the BSP
		void loadNodes      (tBSPLump* l, io::IReadFile* file);		// load the Nodes of the BSP
		void loadLeafs      (tBSPLump* l, io::IReadFile* file);		// load the Leafs of the BSP
		void loadLeafFaces  (tBSPLump* l, io::IReadFile* file);		// load the Faces of the Leafs of the BSP
		void loadVisData    (tBSPLump* l, io::IReadFile* file);		// load the visibility data of the clusters
		void loadEntities   (tBSPLump* l, io::IReadFile* file);		// load the entities
		void loadModels     (tBSPLump* l, io::IReadFile* file);		// load the models
		void loadMeshVerts  (tBSPLump* l, io::IReadFile* file);		// load the mesh vertices
		void loadBrushes    (tBSPLump* l, io::IReadFile* file);		// load the brushes of the BSP
		void loadBrushSides (tBSPLump* l, io::IReadFile* file);		// load the brushsides of the BSP
		void loadLeafBrushes(tBSPLump* l, io::IReadFile* file);		// load the brushes of the leaf
		void loadShaders	(tBSPLump* l, io::IReadFile* file);		// load the shaders

		// second parameter i is the zero based index of the current face.
		void createCurvedSurface(SMeshBufferLightMap* meshBuffer, s32 i);

		//bi-quadratic bezier patches
		void createCurvedSurface2 (	SMeshBufferLightMap* meshBuffer,
									s32 faceIndex,
									s32 patchTesselation,
									s32 storevertexcolor
								);

		void createCurvedSurface3 (	SMeshBufferLightMap* meshBuffer,
									s32 faceIndex,
									s32 patchTesselation,
									s32 storevertexcolor
								);

		f32 Blend( const f64 s[3], const f64 t[3], const tBSPVertex *v[9], int offset);

		struct S3DVertex2TCoords_64
		{
			core::vector3d<f64> Pos;
			core::vector3d<f64> Normal;
			video::SColorf Color;
			core::vector2d<f64> TCoords;
			core::vector2d<f64> TCoords2;

			void copyto ( video::S3DVertex2TCoords &dest ) const;

			S3DVertex2TCoords_64() {}
			S3DVertex2TCoords_64(const core::vector3d<f64>& pos, const core::vector3d<f64>& normal, const video::SColorf& color,
				const core::vector2d<f64>& tcoords, const core::vector2d<f64>& tcoords2)
				: Pos(pos), Normal(normal), Color(color), TCoords(tcoords), TCoords2(tcoords2) {}

			S3DVertex2TCoords_64 getInterpolated_quadratic(const S3DVertex2TCoords_64& v2, const S3DVertex2TCoords_64& v3, const f64 d) const
			{
				return S3DVertex2TCoords_64 (
						Pos.getInterpolated_quadratic ( v2.Pos, v3.Pos, d  ),
						Normal.getInterpolated_quadratic ( v2.Normal, v3.Normal, d ),
						Color.getInterpolated_quadratic ( v2.Color, v3.Color, (f32) d ),
						TCoords.getInterpolated_quadratic ( v2.TCoords, v3.TCoords, d ),
						TCoords2.getInterpolated_quadratic ( v2.TCoords2, v3.TCoords2, d )
					);
			}
		};

		inline void copy ( video::S3DVertex2TCoords * dest, const tBSPVertex * source, s32 vertexcolor ) const;
		void copy ( S3DVertex2TCoords_64 * dest, const tBSPVertex * source, s32 vertexcolor ) const;


		struct SBezier
		{
			SMeshBufferLightMap *Patch;
			S3DVertex2TCoords_64 control[9];

			void tesselate(s32 level);

		private:
			s32	Level;

			core::array<S3DVertex2TCoords_64> column[3];

		};
		SBezier Bezier;

		s32 PatchTesselation;

		tBSPLump Lumps[kMaxLumps];

		tBSPTexture* Textures;
		s32 NumTextures;

		tBSPLightmap* LightMaps;
		s32 NumLightMaps;

		tBSPVertex* Vertices;
		s32 NumVertices;

		tBSPFace* Faces;
		s32 NumFaces;

		tBSPPlane* Planes;
		s32 NumPlanes;

		tBSPNode* Nodes;
		s32 NumNodes;

		tBSPLeaf* Leafs;
		s32 NumLeafs;

		s32 *LeafFaces;
		s32 NumLeafFaces;

		s32 *MeshVerts;           // The vertex offsets for a mesh
		s32 NumMeshVerts;

		tBSPBrush* Brushes;
		s32 NumBrushes;

		scene::SMesh* Mesh[quake3::E_Q3_MESH_SIZE];
		video::IVideoDriver* Driver;
		core::stringc LevelName;
		io::IFileSystem* FileSystem; // needs because there are no file extenstions stored in .bsp files.

		// Additional content
		scene::ISceneManager* SceneManager;
		enum eToken
		{
			Q3_TOKEN_UNRESOLVED	= 0,
			Q3_TOKEN_EOF		= 1,
			Q3_TOKEN_START_LIST,
			Q3_TOKEN_END_LIST,
			Q3_TOKEN_ENTITY,
			Q3_TOKEN_TOKEN,
			Q3_TOKEN_EOL,
			Q3_TOKEN_COMMENT,
			Q3_TOKEN_MATH_DIVIDE,
			Q3_TOKEN_MATH_ADD,
			Q3_TOKEN_MATH_MULTIPY
		};
		struct SQ3Parser
		{
			const c8 *source;
			u32 sourcesize;
			u32 index;
			core::stringc token;
			u32 tokenresult;
		};
		SQ3Parser Parser;


		typedef void ( CQ3LevelMesh::*tParserCallback ) ( quake3::SVarGroupList *& groupList );
		void parser_parse ( const void * data, u32 size, tParserCallback callback );
		void parser_nextToken ();

		void dumpVarGroup ( const quake3::SVarGroup * group, s32 stack ) const;

		void scriptcallback_entity ( quake3::SVarGroupList *& grouplist );
		quake3::tQ3EntityList Entity;

		void scriptcallback_shader ( quake3::SVarGroupList *& grouplist );
		core::array < quake3::SShader > Shader;
		quake3::tStringList ShaderFile;
		void InitShader ();
		void ReleaseShader ();
		void ReleaseEntity ();


		s32 setShaderMaterial ( video::SMaterial & material, const tBSPFace * face ) const;

		struct SToBuffer
		{
			s32 takeVertexColor;
			u32 index;
		};

		void cleanMeshes ();
		void calcBoundingBoxes ();

	};

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

#endif

