
#ifndef _MESH_H_
#define _MESH_H_
#define _CRT_SECURE_NO_DEPRECATE //i won't use fopen_s; so, don't warn me
#define HAVE_SINGLEPRECISION_MATH //Eigen package does #include <complex> which conflicts with Inventor/C/basic.h definitions; so prevent them with this in files using include Inventor

#include <iostream>
#include <vector>
#include <set>
#include <queue>

using namespace std;

#define EXP							2.718281828f
//#define PI							3.141592653f
#define INF							9000000000000000000000000000000.0f //largest float is 3.4x10^38


struct Triangle
{
	//tris[idx] is this tri
	int idx;

	//idx to verts forming this tri
	int v1i, v2i, v3i;

	double distortion;
	double area;

	int tribehind;
	float thickness;

	float shortestegde_to_circumrad;

	Triangle(int i, int i1, int i2, int i3) : idx(i), v1i(i1), v2i(i2), v3i(i3) { distortion = 0; tribehind = NULL; 
	shortestegde_to_circumrad = 0;
	};
};


struct Vertex
{
	//verts[idx] is this vert
	int idx; //,
	//	closestVertIdx; //closest mesh vertex to this vert during the current ICP iteration

	//current and previous iter's coordinates and deformation direction
	float* coords;

	//idx to vert neighbors of this vert (use set for efficient duplicate entry prevention)
	typedef set< int, std::less< int > > vertNeighborsIS; vertNeighborsIS vertNeighborsSet;

	//idx to triangle (tris) and tetrahedron (tets) neighbs
	vector< int > triNeighbors, tetNeighbors;

	//idx to edges incident to this vertex
	vector< int > edgeList, interiorEdgeList; //interior/invisible edges incident to this vert

	float* regulator;

	//int tribehind;
	//float thickness;

	//double quality;



	Vertex(int i, float* c) : idx(i), coords(c) { 

		float* regulator = new float[3];
		regulator[0] = 0.0f;
		regulator[1] = 0.0f;
		regulator[2] = 0.0f;

		this->regulator = regulator;
	};

	bool addVertNeighbor(int vertInd)
	{
		//add vertInd as a vert neighb of this vert if it won't cause a duplication

		pair< vertNeighborsIS::const_iterator, bool > pa;
		//O(log N) search for duplicates by the use of red-black tree (balanced binary search tree)
		pa = vertNeighborsSet.insert(vertInd);
		//pa.second is false if item already exists, and true if insertion succeeds
		return pa.second;
	}

	void removeVertNeighbor(int toBeRemoved)
	{
		//size_type erase (const key_type& x): Deletes all the elements matching x. Returns the 
		//number of elements erased. Since a set supports unique keys, erase will always return 1 or 0

		vertNeighborsSet.erase(toBeRemoved);
	}

	void removeTriNeighbor(int t)
	{
		//this fnctn could've been used by contract/split/flip as well but i wrote it later; so, only createSphere() uses it

		unsigned int removeIdx;
		for (removeIdx = 0; removeIdx < triNeighbors.size(); removeIdx++)
			if (triNeighbors[removeIdx] == t)
				break;

		if (removeIdx == triNeighbors.size()) { cout << "tri" << t << " not in vert" << idx << ".triNeighbs!\n"; exit(0); }

		triNeighbors.erase(triNeighbors.begin() + removeIdx);
	}
};

struct Edge
{
	//edges[idx] is this edge
	int idx;

	//idx to endpnt verts of this edge
	int v1i, v2i;

	//distance between v1i & v2i
	float length;

	bool interior; //true if this is an interior edge, false if it is on the boundary (i.e. useful for geodesic computations)

	Edge(int i, int i1, int i2, float l, bool in) : idx(i), v1i(i1), v2i(i2), length(l), interior(in) {};
};

class Mesh
{
public:
	//triangular mesh stuff
	vector< Triangle* > tris;
	vector< Vertex* > verts;
	vector< Edge* > edges;
	float minEdgeLen, maxEdgeLen, edgeLenTotal, avgEdgeLen;

	Mesh() {};
	
	void readID(int id, string foldername, string format);
	void loadOff(char* fName);
	void loadObj(char* fName);
	void loadObj_face(char* fName);


	void WriteOff(string filename);

	void LaplacianSmoothing();

	void inVertexNormal(Vertex* myvertex, float(&vnormal)[3]);
	void fairing_vert_set(const vector<int>& vert_set);


	void triNormal(Triangle* trifloat, float(&normal)[3]);
	void triCenter(Triangle* tri, float(&mycenter)[3]);

	void AlignParts(const vector<vector<int>>&, int noofparts,string format);

	vector<int> FindEdgeVertices(const vector<int>& vertlist1,const vector<int>& vertlist2);
	void alignTwoParts(const vector<int>& vertlist1,const  vector<int>& vertlist2, const vector<int>& movingpart);
	void alignRotation(const vector<int>& vertlist1, const  vector<int>& vertlist2, const vector<int>& movingpart);


	void alignHelper(const vector<vector<int>>& partvertlist, int  stillpart, int movingpart);
	void fairing();
	void fairing_N(int n);
	
	void linearinterpolation(Mesh* mesh1, Mesh* mesh2, float amount);
	void dijkstraShortestPaths(int sourceVert, vector< int >* savePathToTargets = NULL);
	void computeEvenlySpacedSamples(int N);

private:
	int addVertex(float* v);
	int addVertex(float x, float y, float z);
	void addVertexND(float* c);
	int addTriangle(int v1i, int v2i, int v3i);
	int addTriangleTetmesh(int v1i, int v2i, int v3i, bool tetPartInMeshFile);
	void addEdge(int v1i, int v2i, bool interiorEdge = false);
	inline float calcTriArea(int t);
	int* getTrianglesSharedBy(Edge* bigEdge, int v1i = -1, int v2i = -1);
	void addTetrahedron(int v1i, int v2i, int v3i, int v4i);
	int getEdgeSharedBy(int vi, int vj);



};

void bezierNmesh(vector<Mesh*> meshlist, float amount, Mesh* meshinter, bool overwrite);
void linearint(Mesh* meshlin1, Mesh* mesh1, Mesh*  mesh2, float  amount);
unsigned long long int factorial(int k);
unsigned long long int combination(int k, int i);
float dot(float* v1, float* v2);
void mycross(float* v1, float* v2, float(&returnArray)[3]);
float lenght(Vertex* v1);
void normalize(float* f);

#endif