#ifndef _MESH_H_
#define _MESH_H_

#include <vector>
#include <iostream>

#include "headers\math_headers.h"
#include "headers\opengl_headers.h"

class Mesh
{
public:
	
	struct Face{
		unsigned int id1,id2,id3;
		Face() {}
		Face(int a, int b, int c) : id1(a), id2(b), id3(c){}
		void IDMinusMinus() {id1--; id2--; id3--;}
	};

	struct Tet{
		unsigned int id1,id2,id3,id4;
		Tet() {}
		Tet(int a, int b, int c, int d) : id1(a), id2(b), id3(c), id4(d){}
		void IDMinusMinus() {id1--; id2--; id3--; id4--;}
	};

	Mesh() {}
	virtual ~Mesh();

	// pure virtual functions
	virtual void ReadFromFile(char* filename, float scale = 1.0f) = 0;
	virtual void WriteToFile(char* filename) = 0;
	virtual void GetMeshInfo(char* info) = 0;
	virtual void WriteToSimulation(VectorX& v) = 0;
	virtual void WriteToSimulation(EigenMatrix& v) = 0;
	virtual void ReadFromSimulation(const VectorX& v) = 0;
	virtual unsigned int GetNumebrOfElements() = 0;
	virtual std::vector<unsigned int> GetElementId(unsigned int i) = 0;

	// inline functions
	inline bool Info() {return m_load_success;}
	inline std::vector<glm::vec3>& SimulationColors(bool disconnetec_mesh = false) {return (disconnetec_mesh)?m_disconnected_simulation_colors:m_simulation_colors;}
	inline std::vector<float>& UserData() {return m_user_data;}
	
	// for data exchange with EigenLibrary
	void WriteToEigenVector3D(VectorX& v);
	void ReadFromEigenVector3D(const VectorX& v);
	void WriteToEigenVector2D(VectorX& v);
	void ReadFromEigenVector2D(const VectorX& v);
	void ReadDisconnectedMeshFromEigenVector2D(const VectorX& v);

	// for visualizing: functions
	// update the normal per frame for visualization.
	void SetUniformColor(glm::vec3 color = glm::vec3(0.3f, 0.8f, 1.0f));
	void SetUniformLineColor(glm::vec3 color = glm::vec3(0.0f, 0.0f, 0.0f));
	void SetUniformSimulationColor(glm::vec3 color = glm::vec3(0.7f, 0.7f, 0.7f));
	void ComputeNormal();
	void Draw(const VBO& vbos, int show_texture = 0);
	void DrawSimulationColor(const VBO& vbos, bool draw_disconnected_mesh = false);
	void DrawWireFrame(const VBO& vbos, bool draw_disconnected_mesh = false, int line_width = 1);
	void DrawUserData(const VBO& vbos, bool draw_disconnected_mesh = false);

	// for visualizing: data
	std::vector<glm::vec3> m_vertices;
	std::vector<glm::vec3> m_disconnected_vertices;
	std::vector<glm::vec3> m_normals;
	std::vector<glm::vec3> m_colors;
	std::vector<glm::vec3> m_line_colors;
	std::vector<glm::vec3> m_simulation_colors;
	std::vector<glm::vec3> m_disconnected_simulation_colors;
	std::vector<glm::vec2> m_texcoords;
	std::vector<unsigned int> m_triangle_list;

	std::vector<float> m_user_data;

	bool m_load_success;
};

class ObjMesh : public Mesh
{
public:
	ObjMesh() {}
	virtual ~ObjMesh() {m_faces.clear();}

	virtual void ReadFromFile(char* filename, float scale = 1.0f);
	virtual void WriteToFile(char* filename);
	virtual void GetMeshInfo(char* info);
	virtual void WriteToSimulation(VectorX& v);
	virtual void WriteToSimulation(EigenMatrix& v);
	virtual void ReadFromSimulation(const VectorX& v);
	virtual unsigned int GetNumebrOfElements() {return m_faces.size();}
	virtual std::vector<unsigned int> GetElementId(unsigned int i);

	std::vector<Face> m_faces;
};

class MeshMesh : public Mesh
{
public:
	MeshMesh() {}
	virtual ~MeshMesh() {m_faces.clear(); m_tets.clear();}

	virtual void ReadFromFile(char* filename, float scale = 1.0f);
	virtual void WriteToFile(char* filename);
	virtual void GetMeshInfo(char* info);
	virtual void WriteToSimulation(VectorX& v);
	virtual void WriteToSimulation(EigenMatrix& v);
	virtual void ReadFromSimulation(const VectorX& v);
	virtual unsigned int GetNumebrOfElements() {return m_tets.size();}
	virtual std::vector<unsigned int> GetElementId(unsigned int i);

	std::vector<Face> m_faces;
	std::vector<Tet> m_tets;
};

#endif