#include <GL/glew.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "wavefrontobj.h"

#define OBJ_LINE_BUFFER_SIZE 256

/**
 * 
 * ADJUSTMENT NEEDED FOR
 *     - Face Definitions other than vertex/texture/normal
 *     - Vertex positions including w
 *     - Any faces using vertices yet to be defined
 *       (File is read top to bottom. A face using a vertex
 *       defined underneath it in the file will not work)
 * 
 */

void storeFace(
		face* f,
		vec3* v1, vec2* vt1, vec3* vn1,
		vec3* v2, vec2* vt2, vec3* vn2,
		vec3* v3, vec2* vt3, vec3* vn3
	) {

	memcpy(&f->v1.position, v1, sizeof(vec3));
	memcpy(&f->v2.position, v2, sizeof(vec3));
	memcpy(&f->v3.position, v3, sizeof(vec3));

	memcpy(&f->v1.normal, vn1, sizeof(vec3));
	memcpy(&f->v2.normal, vn2, sizeof(vec3));
	memcpy(&f->v3.normal, vn3, sizeof(vec3));

	memcpy(&f->v1.texture, vt1, sizeof(vec2));
	memcpy(&f->v2.texture, vt2, sizeof(vec2));
	memcpy(&f->v3.texture, vt3, sizeof(vec2));
}

void storeTB(face* f,
		vec3* v1, vec2* vt1,
		vec3* v2, vec2* vt2,
		vec3* v3, vec2* vt3
	) {
	// https://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/
	vec3 deltaPos1;
	vec3 deltaPos2;

	vec3Subtract(&deltaPos1, v2, v1);
	vec3Subtract(&deltaPos1, v3, v1);

	vec2 deltaTex1;
	vec2 deltaTex2;

	vec2Subtract(&deltaTex1, vt2, vt1);
	vec2Subtract(&deltaTex2, vt3, vt1);

	GLfloat r = 1.0f / (deltaTex1.x * deltaTex2.y - deltaTex1.y * deltaTex2.x);

	vec3Multiply(&deltaPos1, &deltaPos1, deltaTex2.y);
	vec3Multiply(&deltaPos2, &deltaPos2, deltaTex1.y);

	vec3 tangent;
	vec3Subtract(&tangent, &deltaPos1, &deltaPos2);
	vec3Multiply(&tangent, &tangent, r);

	memcpy(&f->v1.tangent, &tangent, sizeof(vec3));
	memcpy(&f->v2.tangent, &tangent, sizeof(vec3));
	memcpy(&f->v3.tangent, &tangent, sizeof(vec3));
}

ParsedObjFile readObjFile(char* path) {
	ParsedObjFile parsedFile;

	FILE* fp = fopen(path, "r");

	if (fp == NULL) {
		fprintf(stderr, "File could not be opened: %s", path);
		parsedFile.faces = NULL;
		parsedFile.length = 0;
	}

	uint numVertices = 0;
	uint numVertexNormals = 0;
	uint numFaces = 0;
	uint numTextureCoords = 0;

	char buf[OBJ_LINE_BUFFER_SIZE];

	while (fgets(buf, OBJ_LINE_BUFFER_SIZE, fp)) {
		if (buf[0] == 'v') {
			if (buf[1] == ' ') {
				numVertices++;
			} else if (buf[1] == 't') {
				numTextureCoords++;
			} else if (buf[1] == 'n') {
				numVertexNormals++;
			}
		}
		if (buf[0] == 'f') {
			int numSpaces = 0;
			for (int i = 0; i < strlen(buf); i++) {
				if (buf[i] == ' ') {
					numSpaces++;
				}
			}
			numFaces += numSpaces - 2;
		}
	}

	// printf("Vertices: %d\nFaces: %d\nNormals:%d\nTextures:%d\n", numVertices, numFaces, numVertexNormals, numTextureCoords);

	vec3* vertices = (vec3*) malloc(sizeof(vec3) * numVertices);
	vec3* normals = (vec3*) malloc(sizeof(vec3) * numVertexNormals);

	vec2* textures = (vec2*) malloc(sizeof(vec2) * numTextureCoords);

	face* faces = (face*) malloc(sizeof(face) * numFaces);

	parsedFile.faces = faces;
	parsedFile.length = numFaces;

	rewind(fp);

	uint curVertex = 0;
	uint curNormal = 0;
	uint curFace = 0;
	uint curTexture = 0;

	while (fgets(buf, OBJ_LINE_BUFFER_SIZE, fp)) {
		if (buf[0] == 'v') {
			if (buf[1] == ' ') {

				sscanf(buf,
					"v %f %f %f",
					&vertices[curVertex].x,
					&vertices[curVertex].y,
					&vertices[curVertex].z
				);
				curVertex++;

			} else if (buf[1] == 't') {
				
				int readValues = sscanf(buf,
					"vt %f %f",
					&textures[curTexture].x,
					&textures[curTexture].y
				);

				if (readValues != 2) {
					textures[curTexture].y = 0;
				}

				curTexture++;


			} else if (buf[1] == 'n') {

				sscanf(buf,
					"vn %f %f %f",
					&normals[curNormal].x,
					&normals[curNormal].y,
					&normals[curNormal].z
				);
				curNormal++;

			}
		}

		if (buf[0] == 'f') {
			int v1, v2, v3;
			int vt1, vt2, vt3;
			int vn1, vn2, vn3;

			sscanf(buf,
				"f %d/%d/%d %d/%d/%d %d/%d/%d",
				&v1, &vt1, &vn1,
				&v2, &vt2, &vn2,
				&v3, &vt3, &vn3
			);

			storeFace(&faces[curFace],
				&vertices[v1 - 1], &textures[vt1 - 1], &normals[vn1 - 1],
				&vertices[v2 - 1], &textures[vt2 - 1], &normals[vn2 - 1],
				&vertices[v3 - 1], &textures[vt3 - 1], &normals[vn3 - 1]
			);

			storeTB(&faces[curFace],
				&vertices[v1 - 1], &textures[vt1 - 1],
				&vertices[v2 - 1], &textures[vt2 - 1],
				&vertices[v3 - 1], &textures[vt3 - 1]
			);

			curFace++;

			int numSpaces = 0;
			for (int i = 0; i < strlen(buf); i++) {
				if (buf[i] == ' ') {
					numSpaces++;
				}
			}

			if (numSpaces == 4) {
				storeTB(&faces[curFace],
					&vertices[v1 - 1], &textures[vt1 - 1],
					&vertices[v2 - 1], &textures[vt2 - 1],
					&vertices[v3 - 1], &textures[vt3 - 1]
				);
				
				sscanf(buf,
					"f %d/%d/%d %*d/%*d/%*d %d/%d/%d %d/%d/%d",
					&v1, &vt1, &vn1,
					&v2, &vt2, &vn2,
					&v3, &vt3, &vn3
				);

				storeFace(&faces[curFace],
					&vertices[v1 - 1], &textures[vt1 - 1], &normals[vn1 - 1],
					&vertices[v2 - 1], &textures[vt2 - 1], &normals[vn2 - 1],
					&vertices[v3 - 1], &textures[vt3 - 1], &normals[vn3 - 1]
				);

				curFace++;
			}


			// TODO: textures
		}

	}

	free(vertices);
	free(normals);
	fclose(fp);

	return parsedFile;
}

void clearParsedFile(ParsedObjFile file) {
	free(file.faces);
}