//////////////////////////////////////////////////////////////////////////////
/// \file CooneyMesh.h
///
/// Declares:
/// Mesh - Geometric data structure organized for rendering with OpenGL.
///
/// Stephen Timothy Cooney, 2009
//////////////////////////////////////////////////////////////////////////////
#ifndef	COONEYMESH_H
#define COONEYMESH_H

#include <vector>
#include "CooneyMath.h"

namespace Cooney
{
	// prototypes
	struct MeshVertex;
	struct MeshVertFace;
	struct MeshIndexFace;
	struct MeshVertQuad;
	struct MeshIndexQuad;
	class Mesh;
	
	/// Describes a geometric mesh with OpenGL capabilities.
	/// Data is kept in seperate streams and can be pushed-popped
	/// from OpenGL without affecting the other data.
	///
	/// Supports:
	/// Triangles (Called faces here)
	/// Quads
	/// -
	/// Positions
	/// Normals
	/// Tangents (necessary for normal maps)
	/// Colors
	/// Texture Coordinates (1 right now but easily extendable)
	class Mesh
	{
		std::vector<MeshIndexFace> faces; // Tris
		std::vector<MeshIndexQuad> quads; // Quads
		
		std::vector<Cooney::Math::Vec3> positions;
		std::vector<Cooney::Math::Vec3> normals;
		std::vector<Cooney::Math::Vec3> tangents;
		std::vector<Cooney::Math::Vec3> colors;
		std::vector<Cooney::Math::Vec2> texCoords;
		
		// OpenGL mapping
#define VBO_POSITION 0
#define VBO_NORMAL 1
#define VBO_TANGENT 2
#define VBO_COLOR 3
#define VBO_TEXCOORD 4
#define VBO_FACEINDEX 5
#define VBO_QUADINDEX 6
#define VBO_NUM 7
		bool initiatedvbo[VBO_NUM];
		unsigned int vertexbufferobject[VBO_NUM];
		
#define VBO_STATIC 0 // if this, will load the vertex data ONCE!!! if you change the data, it won't update it unless you tell it to! PushGLData(); default...
#define VBO_STREAM 1 // if this, will load the vertex data every FRAME! (streaming, hint hint) use for animated meshes
		unsigned int vboStyle[VBO_NUM];
		
	public:
		
		Mesh();
		~Mesh();

		// VERTEX
		
		/// Add a vertex into the buffer.
		/// Returns an int that directly maps to the vertex's
		/// index. If an equivalent vertex already exists, the
		/// vertex will not be added, but that equal vert's
		/// index will be returned. Returns -1 on failure.
		int				AddVertex(const MeshVertex &vertex);
		/// Overwrites a vertex at a specific index.
		/// Returns true on success, false on failure.
		bool			SetVertex(const MeshVertex &vertex, const unsigned int vertIndex);
		/// Does a vertex compare(== see class) to determine if
		/// the vertex exists in the mesh. Returns it's index
		/// if it exists. Returns -1 if it is not found.
		int				FindVertex(const MeshVertex &vertex);
		/// Returns a copy of the vertex at a specific index.
		/// Will return a default/empty vertex if one is not
		/// found so it's best to know you're using the right
		/// index.
		MeshVertex		GetVertex(const unsigned int vertIndex);
		/// Gets the vertex at a specific index and fills an out vertex
		/// with it's data. Returns true on success, false on failure.
		bool			GetVertex(const unsigned int vertIndex, MeshVertex& outVert);
		
		// TRIANGLES

		/// Add a triangle face to the mesh.
		/// First adds the face's vertexes and then pushes
		/// an index face onto the list.
		/// Returns the new face's index. Returns -1 on failure.
		int				AddFace(const MeshVertFace &face);
		/// Add a triangle face based on indexes.
		/// Checks for bad indexes, otherwise adds the index
		/// face to the list. It is assumed that the indexes
		/// map towards indended vertices in the mesh.
		/// Returns the faces index, otherwise returns -1.
		int				AddFace(const MeshIndexFace &face);
		/// Adds a triangle A-B-C(-A) from three vertexes.
		/// Adds the vertexes to the vertex stack and adds
		/// their index face to a list for rendering.
		int				AddFace(const MeshVertex &vertA, const MeshVertex &vertB, const MeshVertex &vertC);
		/// Adds a triangle built directly from indexes that map to
		/// a triangle A-B-C(-A).
		/// Returns the new index triangle's own index
		/// or -1 on failure.
		int				AddFace(const unsigned int indexA, const unsigned int indexB, const unsigned int indexC);
		/// Overwrites a triangle face at a specific index.
		/// Returns true on success, false on failure.
		bool			SetFace(const MeshVertFace &face, const unsigned int indexFace);
		/// Overwrites a triangle index face at a
		/// specific index.
		/// Returns true on success, false on failure.
		bool			SetFace(const MeshIndexFace &face, const unsigned int indexFace);
		/// Overwrites a face's vertices at a specific index.
		/// Returns true on success, returns -1 on failure.
		bool			SetFace(const MeshVertex &vertA, const MeshVertex &vertB, const MeshVertex &vertC, const unsigned int indexFace);
		/// Overwrites an index face at a specific index.
		/// Returns true on success, returns -1 on failure.
		bool			SetFace(const unsigned int indexA, const unsigned int indexB, const unsigned int indexC, const unsigned int indexFace);
		/// Returns a face consisting the actual vertexes at
		/// a specific face index. If the index is bad, returns
		/// an empty MeshVertFace.
		MeshVertFace	GetVertFace(const unsigned int index);
		/// Returns a face consisting of the vertex indexes
		/// at a face index. If the index is bad, returns
		/// a default MeshIndexFace.
		MeshIndexFace	GetIndexFace(const unsigned int index);
		
		/// QUADS

		/// Add a quad face to the mesh.
		/// First adds the quads's vertexes and then pushes
		/// an index face onto the list.
		/// Returns the new quad's index. Returns -1 on failure.
		int				AddQuad(const MeshVertQuad &quad);
		/// Add a quad face based on indexes.
		/// Checks for bad indexes, otherwise adds the index
		/// quad to the list. It is assumed that the indexes
		/// map towards indended vertices in the mesh.
		/// Returns the qaud's index, otherwise returns -1.
		int				AddQuad(const MeshIndexQuad &quad);
		/// Adds a quad A-B-C-D(-A) from four vertexes.
		/// Adds the vertexes to the vertex stack and adds
		/// their index quad to a list for rendering.
		int				AddQuad(const MeshVertex &vertA, const MeshVertex &vertB, const MeshVertex &vertC, const MeshVertex &vertD);
		/// Adds a quad built directly from indexes that map to
		/// a quad A-B-C-D(-A).
		/// Returns the new index quad's own index or
		/// -1 on failure.
		int				AddQuad(const unsigned int indexA, const unsigned int indexB, const unsigned int indexC, const unsigned int indexD);
		/// Overwrites a quad face at a specific index.
		/// Returns true on success, false on failure.
		bool			SetQuad(const MeshVertQuad &quad, const unsigned int indexQuad);
		/// Overwrites a quad index face at a
		/// specific index.
		/// Returns true on success, false on failure.
		bool			SetQuad(const MeshIndexQuad &quad, const unsigned int indexQuad);
		/// Overwrites a quad's vertices at a specific index.
		/// Returns true on success, returns -1 on failure.
		bool			SetQuad(const MeshVertex &vertA, const MeshVertex &vertB, const MeshVertex &vertC, const MeshVertex &vertD, const unsigned int indexQuad);
		/// Overwrites a quad face at a specific index.
		/// Returns true on success, returns -1 on failure.
		bool			SetQuad(const unsigned int indexA, const unsigned int indexB, const unsigned int indexC, const unsigned int indexD, const unsigned int indexQuad);
		/// Returns a quad consisting the actual vertexes at
		/// a specific quad index. If the index is bad, returns
		/// an empty MeshVertQuad.
		MeshVertQuad	GetVertQuad(const unsigned int index);
		/// Returns a quad consisting of the vertex indexes
		/// at a quad index. If the index is bad, returns
		/// a default MeshIndexQuad.
		MeshIndexQuad	GetIndexQuad(const unsigned int index);
		
		// Utility functions for autogenerating
		// normals and tangents.

		/// Automatically generates the normals based on the
		/// face's winding and size. The first pass sums each
		/// vertex's faces cross product normals. Last (and only
		/// last) these summed normals are normalized which
		/// allows larger faces to have more 'weight' on the
		/// vertex's lighting normal.
		void CalculateNormals();

		/// Automatically generate tangents based on the UV
		/// coordinates of the faces connected to a vertex.
		void CalculateTangents();
		
		/// Empty the mesh.
		void Clear();
		/// Resize the number of verts in the mesh.
		void Resize(unsigned int numVertexes, unsigned int numFaces, unsigned int numQuads);
		/// Reserve memory for a potential amount of vertexes in the mesh.
		void Reserve(unsigned int numVertexes, unsigned int numFaces, unsigned int numQuads);
		
		/// Makes a deep copy of an input mesh.
		/// This method is a little more explicit than using
		/// a copy constructor.
		void Copy(Cooney::Mesh &copy);
		
		/// DATA ACCESSORS
		
		/// Return the number of triangle faces in the mesh.
		inline unsigned int			NumFaces() const { return (unsigned int)faces.size(); }
		/// Return a pointer to the beginning of the index
		/// faces array.
		inline MeshIndexFace *		Faces()			{ return &*faces.begin(); }
		
		/// Return the number of quad faces in the mesh.
		inline unsigned int			NumQuads() const { return (unsigned int)quads.size(); }
		/// Returns a pointer to the beginning of the index
		/// quads array.
		inline MeshIndexQuad *		Quads()			{ return &*quads.begin(); }
		
		/// Returns the number of vertexes in the mesh.
		/// This number is equivalent for positions, normals,
		/// tangents, colors and texcoords.
		inline const unsigned int	NumVertexes() const	{ return (unsigned int)positions.size(); }
		/// Returns a pointer to the beginning of the vertex positions array.
		inline Cooney::Math::Vec3 *	VertPositions()	{ return &*positions.begin(); }
		/// Returns a pointer to the beginning of the normals array.
		inline Cooney::Math::Vec3 *	VertNormals()	{ return &*normals.begin(); }
		/// Returns a pointer to the beginning of the tangents array.
		inline Cooney::Math::Vec3 *	VertTangents()	{ return &*tangents.begin(); }
		/// Returns a pointer to the beginning of the colors array.
		inline Cooney::Math::Vec3 *	VertColors()	{ return &*colors.begin(); }
		/// Returns a pointer to the beginning of the texture coordinates array.
		inline Cooney::Math::Vec2 *	VertTexCoords()	{ return &*texCoords.begin(); }

		// OPENGL

		/// For each of the vertex data channels, set whether or not that
		/// data should be expected to be streamed or static. Static data
		/// is processed faster, however streamed data has a better
		/// connection to the buffer between the host and GPU (This is what
		/// OpenGL says, implementation may vary).
		void SetGLStreaming(bool bPositions=false, bool bNormals=false, bool bTangents=false, bool bColors=false, bool bTexCoords=false, bool bFaceIndexes=false, bool bQuadIndexes=false);
		
		/// Push the vertex data onto OpenGL's registers and draw.
		/// If the data is not already on the card, this call will
		/// automatically upload the data first. This may also
		/// automatically push the data onto the card if a certain
		/// buffer was set to streaming with SetGLStreaming().
		void PushGL(bool bPositions=true, bool bNormals=false, bool bTangents=false, bool bColors=false, bool bTexCoords=false);
		/// Upload/Push new data to the OpenGL enabled device.
		void PushGLData(bool bPositions=true, bool bNormals=false, bool bTangents=false, bool bColors=false, bool bTexCoords=false, bool bFaceIndexes=false, bool bQuadIndexes=false);
		/// Remove the OpenGL data from device and delete their
		/// buffer references.
		void PopGLData(bool bPositions=true, bool bNormals=true, bool bTangents=true, bool bColors=true, bool bTexCoords=true, bool bFaceIndexes=true, bool bQuadIndexes=true); // kills all connection to OPENGL
		
		// FILE UTILITIES

		/// Load into this mesh from a prorietary format (defined here).
		bool Load(const char *filePath);
		/// Save out to a file in a proprietary format (defined here).
		bool Save(const char *filePath);
		
		/// Used in parallel with Load, this abstracts the
		/// loading procedure to a stream so that it can be
		/// used with different forms of data input.
		void StreamInMesh(std::istream& inStream);
		/// Used in parallel with Save, this abstracts the
		/// saving procedure to a stream so that it can be
		/// used with different forms of data output.
		void StreamOutMesh(std::ostream& outStream);
	};

	/// Describes a geometric point on a mesh.
	struct MeshVertex
	{
		Cooney::Math::Vec3 position;
		Cooney::Math::Vec3 normal;
		Cooney::Math::Vec3 tangent;
		Cooney::Math::Vec3 color;
		Cooney::Math::Vec2 texCoord;
		
		bool operator ==(const MeshVertex &other)
		{
			if( position == other.position )
				// TODO: FatSim: Create a more dynamic compare,
				// in this application we only need this
				// sort but for other applications, vertex
				// individuality may have different meanings.
				//&&
				//normal == other.normal &&
				//tangent == other.tangent &&
				//color == other.color &&
				//texCoord == other.texCoord )
			{
				return true;
			}
			
			return false;
		}
		
		bool operator !=(const MeshVertex &other)
		{
			if( position == other.position )
				// TODO: FatSim: Create a more dynamic compare,
				// in this application we only need this
				// sort but for other applications, vertex
				// individuality may have different meanings.
				//&&
				//normal == other.normal &&
				//tangent == other.tangent &&
				//color == other.color &&
				//texCoord == other.texCoord )
			{
				return false;
			}
			
			return true;
		}
	};
	
	/// A triangle face is not much more here than
	/// a collection of three vertices.
	struct MeshVertFace
	{
		MeshVertex vertA, vertB, vertC;
	};
	
	/// A index triangle face describes mappings to
	/// indexes in mesh that contains many, some shared,
	/// vertexes.
	struct MeshIndexFace
	{
		unsigned int indexA, indexB, indexC;
		
		MeshIndexFace(){indexA=indexB=indexC=0;}
	};
	
	/// A quad face is not much more here than a
	/// collection of four vertexes.
	struct MeshVertQuad
	{
		MeshVertex vertA, vertB, vertC, vertD;
	};
	
	/// A index quad face describes mappings to
	/// vertexes in a mesh that contains many,
	/// some shared, vertexes.
	struct MeshIndexQuad
	{
		unsigned int indexA, indexB, indexC, indexD;
		
		MeshIndexQuad(){indexA=indexB=indexC=indexD=0;}
	};
};

#endif //#ifndef COONEYMESH_H