#include "Mesh.h"
#include "BaseGL.h"

Mesh::Mesh() 
{
	meshVertexArray = NULL;
	meshNormalArray = NULL;
	meshTexcoordsArray = NULL;
	triangleVertices = NULL;
	triangleNormals  = NULL;
	triangleTextures = NULL;
	nofMeshVertices = -1;
	nofMeshNormals = -1;
	tetIndexArray = NULL;
	baryCoordArray = NULL;
	diffmat[0] = 0.45f;diffmat[1] = 0.25f;diffmat[2] = 0.0f;diffmat[3] = 1;
}


/*****************************************
	Ladet ein Wavefront Obj file.

	Implementiert: v, vn, vt, f <=> vertex coords, normal coords, texture coords, faces (polygons)
	Nicht implementiert: Gruppen, Material

	Zu verbessern:
	- alle arrays fuer faces werden erstellt (vertex, texture & normal coords)
	  auch wenn koordinaten komplett fehlen.
******************************************/
bool Mesh::LoadMesh(char* filename) {
	printf("Loading Mesh from file: %s\nFirst pass: get amount of data to store...\n", filename);
	const int STRING_LEN(256);					// assume line has at most 256 characters
	unsigned int numvertices = 0;		/* number of vertices in model */
	unsigned int numnormals = 0;		/* number of normals in model */
	unsigned int numtexcoords = 0;		/* number of texcoords in model */
	unsigned int numtriangles = 0;		/* number of triangles in model */
	unsigned int v, n, t;
	char buf[STRING_LEN];

	// Open file for reading
	FILE *file = fopen(filename, "r");
	if (!file) {
		printf("Couldn't open mesh file: %s \n", filename);
		return false;
	}


	// FIRST PASS: get number of elements
	while(fscanf(file, "%s", buf) != EOF) {		// go through whole file
		switch(buf[0]) {
			case '#':				/* comment */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'v':				/* v, vn, vt */
				switch(buf[1]) {
					case '\0':			/* vertex */
						/* eat up rest of line */
						fgets(buf, sizeof(buf), file);
						numvertices++;
						break;
					case 'n':				/* normal */
						/* eat up rest of line */
						fgets(buf, sizeof(buf), file);
						numnormals++;
						break;
					case 't':				/* texcoord */
						/* eat up rest of line */
						fgets(buf, sizeof(buf), file);
						numtexcoords++;
						break;
					default:
						printf("LoadMesh(): Unknown token \"%s\".\n", buf);
						//exit(1);
						fclose(file);
						return false;
						break;
				}
				break;
			case 'm':				/* Material --- NOT IMPLEMENTED YET */
				fgets(buf, sizeof(buf), file);
				break;
			case 'u':
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'g':				/* group --- NOT IMPLEMENTED YET */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'f':				/* face */
				v = n = t = 0;
				fscanf(file, "%s", buf);
				/* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
				if (strstr(buf, "//")) {
					/* v//n */
					sscanf(buf, "%d//%d", &v, &n);
					fscanf(file, "%d//%d", &v, &n);
					fscanf(file, "%d//%d", &v, &n);
					numtriangles++;
					while(fscanf(file, "%d//%d", &v, &n) > 0) {
						numtriangles++;
					}
				} else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
					/* v/t/n */
					fscanf(file, "%d/%d/%d", &v, &t, &n);
					fscanf(file, "%d/%d/%d", &v, &t, &n);
					numtriangles++;
					while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
						numtriangles++;
					}
				} else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
					/* v/t */
					fscanf(file, "%d/%d", &v, &t);
					fscanf(file, "%d/%d", &v, &t);
					numtriangles++;
					while(fscanf(file, "%d/%d", &v, &t) > 0) {
						numtriangles++;
					}
				} else {
					/* v */
					fscanf(file, "%d", &v);
					fscanf(file, "%d", &v);
					numtriangles++;
					while(fscanf(file, "%d", &v) > 0) {
						numtriangles++;
					}
				}
				break;

			default:
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
		}
	}			// and next line

#if 1
	/* announce the model statistics */
	printf(" Vertices: %d\n", numvertices);
	printf(" Normals: %d\n", numnormals);
	printf(" Texcoords: %d\n", numtexcoords);
	printf(" Triangles: %d\n", numtriangles);
#endif

	/* set the stats in the model structure */
	nofMeshVertices = numvertices;
	nofMeshTexcoords = numtexcoords;
	nofMeshNormals = numnormals;
	nofMeshTriangles = numtriangles;

	/* allocate memory for the triangles in each group */

	printf(" Done!\nSecond Pass: get data... ");
	// SECOND PASS: allocate arrays and store all data
	rewind(file);
	meshVertexArray = new SbVec3f[numvertices];
	meshNormalArray = new SbVec3f[numnormals];
	meshTexcoordsArray = new SbVec2f[numtexcoords];
	triangleVertices = new int[numtriangles*4];		// (3 vertices + 1 seperator) per triangle
	triangleNormals = new int[numtriangles*4];
	triangleTextures = new int[numtriangles*4];

	numvertices = 0;		/* number of vertices in model */
	numnormals = 0;			/* number of normals in model */
	numtexcoords = 0;		/* number of texcoords in model */
	numtriangles = 0;		/* number of triangles in model */
	unsigned int material;			/* current material */

	//numvertices = numnormals = numtexcoords = 1;
	material = 0;
	int countVert = 0;
	int countTex = 0;
	int countNorm = 0;
	while(fscanf(file, "%s", buf) != EOF) {
		SbVec3f vec;
		SbVec2f vec2;
		switch(buf[0]) {
			case '#':				/* comment */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'v':				/* v, vn, vt */
				switch(buf[1]) {
					case '\0':			/* vertex */
						fscanf(file, "%f %f %f", &vec[0], &vec[1], &vec[2]);
						meshVertexArray[numvertices++] = vec;
						break;
					case 'n':				/* normal */
						fscanf(file, "%f %f %f", &vec[0], &vec[1], &vec[2]);
						meshNormalArray[numnormals++] = vec;
						break;
					case 't':				/* texcoord */
						fscanf(file, "%f %f", &vec2[0], &vec2[1]);
						meshTexcoordsArray[numtexcoords++];
						break;
				}
				break;
			case 'u':				// --- NOT IMPLEMENTED YET
				fgets(buf, sizeof(buf), file);
				break;
			case 'g':				/* group --- NOT IMPLEMENTED YET */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'f':				/* face */
				v = n = t = 0;
				fscanf(file, "%s", buf);
				/* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
				if (strstr(buf, "//")) {
					/* v//n */
					sscanf(buf, "%d//%d", &v, &n);
					triangleVertices[countVert++] = v;
					triangleNormals[countNorm++] = n;
					fscanf(file, "%d//%d", &triangleVertices[countVert++], &triangleNormals[countNorm++]);
					fscanf(file, "%d//%d", &triangleVertices[countVert++], &triangleNormals[countNorm++]);
					triangleVertices[countVert++] = -1;		// seperator between 2 faces
					triangleNormals[countNorm++]  = -1;
					while(fscanf(file, "%d//%d", &v, &n) > 0) {
						// handle polygon as Triangle-... strip?
						triangleVertices[countVert++] = triangleVertices[countVert - 4];	// = first vertex from last triangle
						triangleNormals[countNorm++]  = triangleNormals[countNorm - 4];
						triangleVertices[countVert++] = triangleVertices[countVert - 3];		// = third vertex from last triangle
						triangleNormals[countNorm++]  = triangleNormals[countNorm - 3];
						triangleVertices[countVert++] = v;		// = new vertex
						triangleNormals[countNorm++]  = n;
						triangleVertices[countVert++] = -1;		// seperator between 2 faces
						triangleNormals[countNorm++]  = -1;
					}
				} else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
					/* v/t/n */
					triangleVertices[countVert++] = v;
					triangleTextures[countTex++]  = t;
					triangleNormals[countNorm++]  = n;
					fscanf(file, "%d/%d/%d", &v, &t, &n);
					triangleVertices[countVert++] = v;
					triangleTextures[countTex++]  = t;
					triangleNormals[countNorm++]  = n;
					fscanf(file, "%d/%d/%d", &v, &t, &n);
					triangleVertices[countVert++] = v;
					triangleTextures[countTex++]  = t;
					triangleNormals[countNorm++]  = n;
					triangleVertices[countVert++] = -1;		// seperator between 2 faces
					triangleTextures[countTex++]  = -1;
					triangleNormals[countNorm++]  = -1;
					while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
						triangleVertices[countVert++] = triangleVertices[countVert - 4];
						triangleTextures[countTex++]  = triangleTextures[countTex - 4];
						triangleNormals[countNorm++]  = triangleNormals[countNorm - 4];
						triangleVertices[countVert++] = triangleVertices[countVert - 3];
						triangleTextures[countTex++]  = triangleTextures[countTex - 3];
						triangleNormals[countNorm++]  = triangleNormals[countNorm - 3];
						triangleVertices[countVert++] = v;
						triangleTextures[countTex++]  = t;
						triangleNormals[countNorm++]  = n;
						triangleVertices[countVert++] = -1;		// seperator between 2 faces
						triangleTextures[countTex++]  = -1;
						triangleNormals[countNorm++]  = -1;
					}
				} else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
					/* v/t */
					triangleVertices[countVert++] = v;
					triangleTextures[countTex++]  = t;
					fscanf(file, "%d/%d", &v, &t);
					triangleVertices[countVert++] = v;
					triangleTextures[countTex++]  = t;
					fscanf(file, "%d/%d", &v, &t);
					triangleVertices[countVert++] = v;
					triangleTextures[countTex++]  = t;
					triangleVertices[countVert++] = -1;		// seperator between 2 faces
					triangleTextures[countTex++]  = -1;
					while(fscanf(file, "%d/%d", &v, &t) > 0) {
						triangleVertices[countVert++] = triangleVertices[countVert - 4];
						triangleTextures[countTex++]  = triangleTextures[countTex - 4];
						triangleVertices[countVert++] = triangleVertices[countVert - 3];
						triangleTextures[countTex++]  = triangleTextures[countTex - 3];
						triangleVertices[countVert++] = v;
						triangleTextures[countTex++]  = t;
						triangleVertices[countVert++] = -1;		// seperator between 2 faces
						triangleTextures[countTex++]  = -1;
					}
				} else {
					/* v */
					sscanf(buf, "%d", &v);
					triangleVertices[countVert++] = v;
					fscanf(file, "%d", &v);
					triangleVertices[countVert++] = v;
					fscanf(file, "%d", &v);
					triangleVertices[countVert++] = v;
					triangleVertices[countVert++] = -1;
					while(fscanf(file, "%d", &v) > 0) {
						triangleVertices[countVert++] = triangleVertices[countVert - 4];
						triangleVertices[countVert++] = triangleVertices[countVert - 3];
						triangleVertices[countVert++] = v;
						triangleVertices[countVert++] = -1;
					}
				}
				break;

			default:
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
		}
		if (countVert > nofMeshTriangles*4) {
			printf("Error: out of array boundary %i\n", countVert);
			fclose(file);
			return false;
		}
	}
	// fix indices -> array starts with 0, but in wavefront indices begin with 1!
	for (int i=0; i<nofMeshTriangles*4; i += 4) {
		triangleVertices[i]   -= 1;
		triangleVertices[i+1] -= 1;
		triangleVertices[i+2] -= 1;
		triangleTextures[i]   -= 1;
		triangleTextures[i+1] -= 1;
		triangleTextures[i+2] -= 1;
		triangleNormals[i]    -= 1;
		triangleNormals[i+1]  -= 1;
		triangleNormals[i+2]  -= 1;
	}

#if 0
	/* announce the memory requirements */
	printf(" Memory: %d bytes\n", 1);
#endif

	printf("Done!\n\n");
	fclose(file);		// Close file

	//Allocate tetIndexArray&baryCoordArray with correct size
	tetIndexArray = new int[nofMeshVertices];	
	baryCoordArray = new SbVec4f[nofMeshVertices];
	meshTriangleNormArray = new SbVec3f[nofMeshTriangles];

	UpdateFaceNormals();		// initialize face normals

	return true;		// puh, finally...
}


/*****************************
	Calculate all face normals
********************************/
void Mesh::UpdateFaceNormals() {
	int count = 0;
	SbVec3f v1, v2, v3, v4, v5, v6;
	for (int i=0; i<nofMeshTriangles; i++) {
		v1 = meshVertexArray[triangleVertices[count++]];
		v2 = meshVertexArray[triangleVertices[count++]];
		v3 = meshVertexArray[triangleVertices[count++]];
		v4 = v2 - v1;
		v5 = v3 - v1;			// v4 and v5 span the plane of the polygon
		v6 = v4.cross(v5);
        v6.normalize();
		meshTriangleNormArray[i] = v6;
		count++;
	}

}

void Mesh::Render()
{
	int i=0,k=0,j=0;
	glMaterialfv(GL_FRONT, GL_DIFFUSE, diffmat);
	glColor3f(0,0,0);
	glEnable(GL_LIGHTING);
	glBegin(GL_TRIANGLES);		//ineffizient, sollte später mit VA & glDrawVertexArray gemacht werden
	for (i=0; i<nofMeshTriangles; i++)
	{
		glColor3f(1,1,1);
		glNormal3fv(meshTriangleNormArray[i].getValue());	//per face normals
		glVertex3fv(meshVertexArray[triangleVertices[k++]].getValue());
		glVertex3fv(meshVertexArray[triangleVertices[k++]].getValue());
		glVertex3fv(meshVertexArray[triangleVertices[k++]].getValue());
		k++;
	}
	glDisable(GL_LIGHTING);
	glEnd();
}




