#ifndef TRIMESH_H
#define TRIMESH_H
/*
Szymon Rusinkiewicz
Princeton University

TriMesh.h
Class for triangle meshes.
*/

#include "Vec.h"
#include "Color.h"
#include <vector>
using std::vector;

struct TrimeshPart;

struct PointSample {
  size_t id;			// id
  size_t faceid;  // id of face it belongs to
  point  sample;  // point
  point normal;		// normal
  vector<PointSample*>  nearestNeighbors;		// nearest neighbors
  bool toBeErased;			// sample too close to another sample?
	TrimeshPart *part;
};

// part of a mesh
struct TrimeshPart {
	std::vector<size_t> faceids;
	std::string label;
	vector<PointSample*>  pointsamples;
	size_t			mesh_part_id;
	size_t			old_mesh_part_id;
	size_t			label_id;

	TrimeshPart() 
	{ label = ""; mesh_part_id = 0; }
	TrimeshPart(std::string _label)
	{ label = _label; mesh_part_id = 0; }
};


class TriMesh {
protected:
	static bool read_helper(const char *filename, TriMesh *mesh);

public:
	size_t id,old_id;
	char filename[4096];				// filename
	float normalizationFactor;			// normalization factor (based on geod. distances)

	struct Face {
		int v[3];
    vector<PointSample*> pointsamples;		// point samples it contains
 		bool visited;							// visited (during a BFS search)
		vec facenormal;
		float faceArea;
		point faceCenter;
		TrimeshPart *part;				// part that the face belongs to

		Face() {}
		Face(const int &v0, const int &v1, const int &v2)
			{ v[0] = v0; v[1] = v1; v[2] = v2; part = NULL; }
		Face(const int *v_)
			{ v[0] = v_[0]; v[1] = v_[1]; v[2] = v_[2]; part = NULL; }
		Face(const int &v0, const int &v1, const int &v2, TrimeshPart* _part)
			{ v[0] = v0; v[1] = v1; v[2] = v2; part = _part; }
		Face(const int *v_,  TrimeshPart* _part)
			{ v[0] = v_[0]; v[1] = v_[1]; v[2] = v_[2]; part = _part; }

		int &operator[] (int i) { return v[i]; }
		const int &operator[] (int i) const { return v[i]; }
		operator const int * () const { return &(v[0]); }
		operator const int * () { return &(v[0]); }
		operator int * () { return &(v[0]); }
		int indexof(int v_) const
		{
			return (v[0] == v_) ? 0 :
			       (v[1] == v_) ? 1 :
			       (v[2] == v_) ? 2 : -1;
		}
	};


	struct BBox {
		point min, max;
		point center() const { return 0.5f * (min+max); }
		vec size() const { return max - min; }
		bool valid;
		BBox() : valid(false)
			{}
	};

	struct BSphere {
		point center;
		float r;
		bool valid;
		BSphere() : valid(false)
			{}
	};

	// Enums
	enum tstrip_rep { TSTRIP_LENGTH, TSTRIP_TERM };
	enum { GRID_INVALID = -1 };

	// The basics: vertices and faces
	vector<point> vertices;
	vector<Face> faces;


	// Triangle strips
	vector<int> tstrips;

	// Grid, if present
	vector<int> grid;
	int grid_width, grid_height;

	// Other per-vertex properties
	vector<Color> colors;
	vector<float> confidences;
	vector<unsigned> flags;
	unsigned flag_curr;
	
	// Computed per-vertex properties
	vector<vec> normals;
	vector<vec> pdir1, pdir2;
	vector<float> curv1, curv2;
	vector< Vec<4,float> > dcurv;
	vector<vec> cornerareas;
	vector<float> pointareas;

	// Bounding structures
	BBox bbox;
	BSphere bsphere;

	// Connectivity structures:
	//  For each vertex, all neighboring vertices
	vector< vector<int> > neighbors;
	//  For each vertex, all neighboring faces
	vector< vector<int> > adjacentfaces;
	//  For each face, the three faces attached to its edges
	//  (for example, across_edge[3][2] is the number of the face
	//   that's touching the edge opposite vertex 2 of face 3)
	vector<Face> across_edge;

	// MY PROPERTIES MINE
	// parts
	vector<TrimeshPart*> parts;
	vector<int> number_of_parts_per_label;
	float totalFaceArea;
	bool trimesh_part_data_updated;

	// Compute all this stuff...
	void need_tstrips();
	void convert_strips(tstrip_rep rep);
	void unpack_tstrips();
	void triangulate_grid();
	void need_faces()
	{
		if (!trimesh_part_data_updated)
			updateTrimeshPartData();
		if (!faces.empty())
			return;
		if (!tstrips.empty())
			unpack_tstrips();
		else if (!grid.empty())
			triangulate_grid();
	}
	void need_normals();
	void need_pointareas();
	void need_curvatures();
	void need_dcurv();
	void need_bbox();
	void need_bsphere();
	void need_neighbors();
	void need_adjacentfaces();
	void need_across_edge();

	// Input and output
	static TriMesh *read(const char *filename, size_t id = 0);
	void write(const char *filename, bool use_old_writer = false);

	// Statistics
	float feature_size();



	// Useful queries
	inline void updateTrimeshPartData()
	{
		std::cout << "Updating mesh part data...";

		trimesh_part_data_updated = true; 

		for (size_t i = 0; i < parts.size(); i++)
		{
			parts[i]->pointsamples.clear();
			parts[i]->faceids.clear();
		}

		for (size_t i = 0; i < faces.size(); i++)
			if ( faces[i].part != NULL)
				faces[i].part->faceids.push_back(i);
			//else 
			//	std::cerr << "face " << i << " does not belong to any part!" << std::endl;

		for (size_t i = 0; i < parts.size(); i++)
		{
			for (size_t j = 0; j < parts[i]->faceids.size(); j++)
			{
				for (size_t k = 0; k < faces[ parts[i]->faceids[j] ].pointsamples.size(); k++)
				{
					faces[ parts[i]->faceids[j] ].pointsamples[k]->part = parts[i];
					parts[i]->pointsamples.push_back(  faces[ parts[i]->faceids[j] ].pointsamples[k] );
				}
			}
		}
		std::cout << "Done." << std::endl;
	}

	bool is_bdy(int v)
	{
		if (neighbors.empty()) need_neighbors();
		if (adjacentfaces.empty()) need_adjacentfaces();
		return neighbors[v].size() != adjacentfaces[v].size();
	}
	vec trinorm(int f)
	{
		if (faces.empty()) need_faces();
		return ::trinorm(vertices[faces[f][0]], vertices[faces[f][1]],
			vertices[faces[f][2]]);
	}

	// Debugging printout, controllable by a "verbose"ness parameter
	static int verbose;
	static void set_verbose(int);
	static int dprintf(const char *format, ...);

	// Constructor
	TriMesh() : grid_width(-1), grid_height(-1), flag_curr(0)
		{}

	TriMesh(size_t _id) : id(_id), grid_width(-1), grid_height(-1), flag_curr(0) 
		{}
};



#endif
