#include "Model.h"
#include <fstream>
#include <sstream>
#include <string>

void Model::init()
{
	//initializes the Model
	mesh = new Mesh(name, groundTruth, useShortcutEdges, maxLevel, nInitialSamples, rFactor);

	string pathString(path);
	if (pathString.substr(pathString.length()-3).compare("obj") == 0)
	{
		loadObj3(path);
	}
	else if (pathString.substr(pathString.length()-3).compare("off") == 0 || pathString.substr(pathString.length()-3).compare("OFF") == 0)
	{
		loadOff(path);
	}
	else
	{
		cout << "unknown mesh format" << endl;
		system("pause");
		exit(-1);
	}

	cout << "Initial mesh has " << (int) mesh->tris.size() << " tris, " << (int) mesh->verts.size() << " verts, "
		 << (int) mesh->edges.size() << " edges\nInitialization done\n\n";

	cout << "Genus = 1 - ((" << (int) mesh->tris.size() << " + " << (int) mesh->verts.size() << " - " << (int) mesh->edges.size() << ") / 2)";
	cout << " = 1 - (" << (int) mesh->tris.size() + (int) mesh->verts.size() - (int) mesh->edges.size() << " / 2)";
	cout << " = " << 1 - ((int) mesh->tris.size() + (int) mesh->verts.size() - (int) mesh->edges.size()) / 2.0 << endl << endl;

	mesh->updateNormals();

	color = new float[3];
	color[0] = color[1] = color[2] = 0.75f;

	mesh->writeMeshPropertiesToFile(path);
	/*if (!mesh->writeMeshPropertiesToFile(path))
	{
		cout << "inappropriate input mesh: " << path << endl;
		system("pause");
		exit(-1);
	}*/
}

Mesh* Model::getMesh()
{
	return mesh;
}

void Model::loadObj(const char* meshFile)
{
	cout << "Mesh initializing (to model in " << meshFile << " file)...\n";

	FILE* fPtr = fopen(meshFile, "r");
	if (fPtr == 0)
		cout << "cannot read " << meshFile << endl;

	char type;	//type of line: vertex line (v) or face line (f)
	float a, b, c;	//for face lines casting these to int will suffice
	bool first = true;
	float minX, minY, minZ, maxX, maxY, maxZ; //bbox to be used in center-of-bbox calculation
	mesh->minEdgeLen = INF;	mesh->maxEdgeLen = -INF; mesh->edgeLenTotal = 0.0f;
	while (fscanf(fPtr, "%c %f %f %f\n", &type, &a, &b, &c) != EOF) //go till the end of file
		if (type == 'v')
		{
			//for a robust duplicateExists check in Mesh.addVertex(), scale up coords just in case
			a *= SCALE_FACTOR;	b *= SCALE_FACTOR;	c *= SCALE_FACTOR;

			//none of verts are removed/duplicated initially 'cos i read .obj
			float* coords = new float[3];
			coords[0] = a;
			coords[1] = b;
			coords[2] = c;
			mesh->addVertexND(coords); //ND: no duplicate check

			//bbox computation
			if ((int) mesh->verts.size() == 1)
			{
				minX = maxX = a;	minY = maxY = b;	minZ = maxZ = c;
			}
			else
			{
				if (a < minX)	minX = a; else if (a > maxX)	maxX = a;
				if (b < minY)	minY = b; else if (b > maxY)	maxY = b;
				if (c < minZ)	minZ = c; else if (c > maxZ)	maxZ = c;
			}
		}
		else //if (type == 'f')
			mesh->addTriangle((int) a - 1, (int) b - 1, (int) c - 1, true); //-1: obj indices start at 1

	float cobX = (minX+maxX) / 2.0f, cobY = (minY+maxY) / 2.0f, cobZ = (minZ+maxZ) / 2.0f;
	cout << "bbox.center: (" << cobX << ", " << cobY << ", " << cobZ << "); "
		 << "bbox.(w,h,d): (" << maxX-minX << ", " << maxY-minY << ", " << maxZ-minZ << ")\n"
		 << "min <= avg <= maxEdgeLen: " << mesh->minEdgeLen << " <= " << mesh->edgeLenTotal/((int) mesh->edges.size()) << " <= " << mesh->maxEdgeLen << "\n";
	fclose(fPtr);
	cout << "Initialization done\n\n";
}
void Model::loadObj2(char* meshFile)
{
	//only difference from loadObj() is this one expects no preceding 'v' and 'f' in lines, and a separator line ("-9 -9 -9") b/w vertices and faces

	cout << "Mesh initializing (to model in " << meshFile << " file)...\n";

	FILE* fPtr = fopen(meshFile, "r");
	if (fPtr == 0)
		cout << "cannot read " << meshFile << endl;

	char type;	//type of line: vertex line (v) or face line (f)
	float a, b, c;	//for face lines casting these to int will suffice
	bool first = true;
	float minX, minY, minZ, maxX, maxY, maxZ; //bbox to be used in center-of-bbox calculation
	mesh->minEdgeLen = INF;	mesh->maxEdgeLen = -INF; mesh->edgeLenTotal = 0.0f;
type = 'v';
	while (fscanf(fPtr, "%f %f %f\n", &a, &b, &c) != EOF) //missing 'v' and 'f' in each line of .obj2 files [dog-wolf case]
		if (type == 'v')
		{
if (a == -9 && b == -9 && c == -9) { type = 'f'; continue; } //my special .obj2 file starts triangles right after -9 -9 -9 line

			//for a robust duplicateExists check in Mesh.addVertex(), scale up coords just in case
			a *= SCALE_FACTOR;	b *= SCALE_FACTOR;	c *= SCALE_FACTOR;

			//none of verts are removed/duplicated initially 'cos i read .obj
			float* coords = new float[3];
			coords[0] = a, coords[1] = b, coords[2] = c;
			mesh->addVertexND(coords); //ND: no duplicate check

			//bbox computation
			if ((int) mesh->verts.size() == 1)
			{
				minX = maxX = a;	minY = maxY = b;	minZ = maxZ = c;
			}
			else
			{
				if (a < minX)	minX = a; else if (a > maxX)	maxX = a;
				if (b < minY)	minY = b; else if (b > maxY)	maxY = b;
				if (c < minZ)	minZ = c; else if (c > maxZ)	maxZ = c;
			}
		}
		else //if (type == 'f')
			mesh->addTriangle((int) a - 1, (int) b - 1, (int) c - 1, true); //-1: obj indices start at 1

	float cobX = (minX+maxX) / 2.0f, cobY = (minY+maxY) / 2.0f, cobZ = (minZ+maxZ) / 2.0f;
	cout << "bbox.center: (" << cobX << ", " << cobY << ", " << cobZ << "); "
		 << "bbox.(w,h,d): (" << maxX-minX << ", " << maxY-minY << ", " << maxZ-minZ << ")\n"
		 << "min <= avg <= maxEdgeLen: " << mesh->minEdgeLen << " <= " << mesh->edgeLenTotal/((int) mesh->edges.size()) << " <= " << mesh->maxEdgeLen << "\n";
	fclose(fPtr);
	cout << "Initialization done\n\n";
}

void Model::loadObj3(const char* meshFile)
{
	cout << "Mesh initializing (to model in " << meshFile << " file)...\n";

	ifstream infile(meshFile);
	string line;

	if (!infile.is_open())
	{
		cout << "Cannot open " << meshFile << endl;
		system("pause");
		exit(-1);
	}

	float minX, minY, minZ, maxX, maxY, maxZ; //bbox to be used in center-of-bbox calculation
	mesh->minEdgeLen = INF;	mesh->maxEdgeLen = -INF; mesh->edgeLenTotal = 0.0f;
	while (getline(infile, line))
	{
		istringstream iss(line);
		if (!iss.str().empty() && iss.str().at(0) == 'v')
		{
			char temp;
			float a, b, c;

			iss >> temp >> a >> b >> c;

			a *= SCALE_FACTOR;	b *= SCALE_FACTOR;	c *= SCALE_FACTOR;

			//none of verts are removed/duplicated initially 'cos i read .obj
			float* coords = new float[3];
			coords[0] = a;
			coords[1] = b;
			coords[2] = c;
			mesh->addVertexND(coords); //ND: no duplicate check

			//bbox computation
			if ((int) mesh->verts.size() == 1)
			{
				minX = maxX = a;	minY = maxY = b;	minZ = maxZ = c;
			}
			else
			{
				if (a < minX)	minX = a; else if (a > maxX)	maxX = a;
				if (b < minY)	minY = b; else if (b > maxY)	maxY = b;
				if (c < minZ)	minZ = c; else if (c > maxZ)	maxZ = c;
			}
		}
		else if (!iss.str().empty() && iss.str().at(0) == 'f')
		{
			char temp;
			int a, b, c;

			iss >> temp >> a >> b >> c;

			mesh->addTriangle(a-1, b-1, c-1, true); //-1: obj indices start at 1
		}
	}
	infile.close();

	float cobX = (minX+maxX) / 2.0f, cobY = (minY+maxY) / 2.0f, cobZ = (minZ+maxZ) / 2.0f;
	cout << "bbox.center: (" << cobX << ", " << cobY << ", " << cobZ << "); "
		 << "bbox.(w,h,d): (" << maxX-minX << ", " << maxY-minY << ", " << maxZ-minZ << ")\n"
		 << "min <= avg <= maxEdgeLen: " << mesh->minEdgeLen << " <= " << mesh->edgeLenTotal/((int) mesh->edges.size()) << " <= " << mesh->maxEdgeLen << "\n";
	cout << "Initialization done\n\n";
}

void Model::loadOff(const char* meshFile)
{
	cout << "Mesh initializing (to model in " << meshFile << " file)...\n";

	ifstream infile(meshFile);
	string line;

	if (!infile.is_open())
	{
		cout << "Cannot open " << meshFile << endl;
		system("pause");
		exit(-1);
	}

	getline(infile, line);
	assert(line.compare("OFF") == 0);
	
	int numOfVertices, numOfTriangles;
	getline(infile, line);
	istringstream iss(line);
	iss >> numOfVertices >> numOfTriangles;

	float minX, minY, minZ, maxX, maxY, maxZ; //bbox to be used in center-of-bbox calculation
	mesh->minEdgeLen = INF;	mesh->maxEdgeLen = -INF; mesh->edgeLenTotal = 0.0f;

	for (int i = 0; i < numOfVertices; i++)
	{
		getline(infile, line);
		istringstream iss(line);
		float a, b, c;
		iss >> a >> b >> c;
		a *= SCALE_FACTOR;	b *= SCALE_FACTOR;	c *= SCALE_FACTOR;

		//none of verts are removed/duplicated initially 'cos i read .obj
		float* coords = new float[3];
		coords[0] = a;
		coords[1] = b;
		coords[2] = c;
		mesh->addVertexND(coords); //ND: no duplicate check

		//bbox computation
		if ((int) mesh->verts.size() == 1)
		{
			minX = maxX = a;	minY = maxY = b;	minZ = maxZ = c;
		}
		else
		{
			if (a < minX)	minX = a; else if (a > maxX)	maxX = a;
			if (b < minY)	minY = b; else if (b > maxY)	maxY = b;
			if (c < minZ)	minZ = c; else if (c > maxZ)	maxZ = c;
		}
	}

	for (int i = 0; i < numOfTriangles; i++)
	{
		getline(infile, line);
		istringstream iss(line);
		int temp;
		int a, b, c;
		iss >> temp >> a >> b >> c;
		mesh->addTriangle(a, b, c, true); //-1: obj indices start at 0
	}

	infile.close();

	float cobX = (minX+maxX) / 2.0f, cobY = (minY+maxY) / 2.0f, cobZ = (minZ+maxZ) / 2.0f;
	cout << "bbox.center: (" << cobX << ", " << cobY << ", " << cobZ << "); "
		 << "bbox.(w,h,d): (" << maxX-minX << ", " << maxY-minY << ", " << maxZ-minZ << ")\n"
		 << "min <= avg <= maxEdgeLen: " << mesh->minEdgeLen << " <= " << mesh->edgeLenTotal/((int) mesh->edges.size()) << " <= " << mesh->maxEdgeLen << "\n";
	cout << "Initialization done\n\n";
}