#include <stdio.h>
#include <GL/glew.h>
#include <GL/gl.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include "include/logger.h"
#include <string.h>
#include "include/render.h"
#include "include/matrix.h"
#define _MAX_LINE_LEN 200
#define _MAX_MTL_LEN 50
#define _MAX_TEXTURE_LEN 128
#define _MAX_FILE_PATH 256

char lineBuf[_MAX_LINE_LEN];

struct object *loadSTL(const char* filename) {
    FILE* file_fd;
    if ((file_fd = fopen(filename, "r")) == NULL) {
        logData(LOGLEVEL_CRIT, "Could not open model file %s", __FILE__, __LINE__, (void*[]){(void*)1, &filename});
        perror("Could not open file");
        exit(-1);
    }
    // calculate amount of vertices and
    // find largest vector in order to normalize them
    struct object *out = malloc(sizeof(struct object));
    int vertices = 0;
    char temp = EOF;
    GLfloat vertexX, vertexY, vertexZ;
    float largestVertex = 0, vertexLength;
    while ((temp = fgetc(file_fd)) != EOF) {
        fscanf(file_fd, "%s", lineBuf);
        if (strncmp(lineBuf, "vertex", 6) == 0){
            fscanf(file_fd, "%f %f %f", &vertexX, &vertexY, &vertexZ);
            if ((vertexLength = lenVec4((vec4) {vertexX, vertexY, vertexZ, 1}))>largestVertex) largestVertex = vertexLength;
            vertices ++;
        }
    }
    printf("Largest model vertex: %f\n", largestVertex);
    out->indices_size = vertices;
    out->indices = malloc(vertices*sizeof(int));
    out->normals = malloc(vertices*sizeof(GLfloat)*3); // face normals, not vertex normals
    out->vertices = malloc(vertices*sizeof(GLfloat)*3); // not writing to size in struct, since we are doing indices optimization
    rewind(file_fd);

    // parse vertexes and find potencial indices
    int vertexCounter = 0;
    int indicesCounter = 0;
    int index = -1;
    while ((temp = fgetc(file_fd)) != EOF) {
        fscanf(file_fd, "%s", lineBuf);
        if (strncmp(lineBuf, "vertex", 6) == 0) {
            fscanf(file_fd, "%f %f %f", &vertexX, &vertexY, &vertexZ);
            vertexX /= largestVertex;
            vertexY /= largestVertex;
            vertexZ /= largestVertex;
            index = -1;
            // normals unfortunately need to be per vertex not per face and stl has only per face so this unfortunately breaks stuff
            //for (int i = 0; i < vertexCounter; i++) { 
            //    if (
            //        vertexX == out->vertices[i*3] &&
            //        vertexY == out->vertices[i*3+1] &&
            //        vertexZ == out->vertices[i*3+2]
            //    ) {
            //        index = i; break;
            //    }
            //}
            if (index != -1) {
                out->indices[indicesCounter] = index;
            } else {
                out->vertices[vertexCounter*3] = vertexX, out->vertices[vertexCounter*3+1] = vertexY, out->vertices[vertexCounter*3+2] = vertexZ;
                out->indices[indicesCounter] = vertexCounter;
                vertexCounter++;
            }
            indicesCounter++;
        }
    }
    out->vertices_size = vertexCounter;
    out->normals_size = vertexCounter;

    out->colors = malloc(3*sizeof(GLfloat)*vertexCounter);

    rewind(file_fd);
    // load normals
    GLfloat normalX, normalY, normalZ;
    int normalCounter = 0;
    while ((temp = fgetc(file_fd)) != EOF) {
        fscanf(file_fd, "%s", lineBuf);
        if (strncmp(lineBuf, "facet", 5) == 0) {
            fscanf(file_fd, " %s %f %f %f", lineBuf, &normalX, &normalY, &normalZ);
            for (int i = 0; i<3; i++) {
                out->normals[normalCounter*3*3+i*3] = normalX;
                out->normals[normalCounter*3*3+i*3+1] = normalY;
                out->normals[normalCounter*3*3+i*3+2] = normalZ;
            }

            normalCounter ++;
        }
    }

    for (int i = 0; i<vertexCounter; i++) {
        out->colors[i*3]   = 1;
        out->colors[i*3+1] = 1;
        out->colors[i*3+2] = 1;

    }

    /*for (int i = 0; i<vertexCounter; i++) {
        out->colors[i*3] = rand()/(float)RAND_MAX;
        out->colors[i*3+1] = rand()/(float)RAND_MAX;
        out->colors[i*3+2] = rand()/(float)RAND_MAX;

    }*/

    printf("--- Loaded STL file %s ---\n", filename);
    printf("Indices %d\n", indicesCounter);
    printf("Normals: %d\n", normalCounter);
    printf("Vertices: %d\n", vertexCounter);

    return out;
}

union BMPfilehdr {
    unsigned char rawheader[54];
    struct header {
        short hdrfield;
        int filesize;
        int __reserved;
        int starting_offset;
        int BITMAPINFOHEADER_size;
        int width;
        int height;
        short colorplanes;
        short bpp;
        int compression;
        unsigned int imagesize;
    } __attribute__((packed)) header;
} typedef BMPFileHeader;


struct object *loadOBJ(const char* filename) {
    struct timeval timer1, timer2;
    gettimeofday(&timer1, NULL);
    FILE* file_fd;
    if ((file_fd = fopen(filename, "r")) == NULL) {
        logData(LOGLEVEL_CRIT, "Could not open model file %s", __FILE__, __LINE__, (void*[]){(void*)1, &filename});
        perror("Could not open file");
        exit(-1);
    }
    //get vertex, normal, uv texture count
    int vertexCount = 0, normalCount = 0, UVTextureCount = 0, indexCount = 0, facesCount = 0, textureCount = 0;
    char temp;
    char usedMtl[_MAX_MTL_LEN];
    while ((temp = fgetc(file_fd)) != EOF) {
        fgets(lineBuf, _MAX_LINE_LEN, file_fd);
        if (temp == 'v') {
            if (lineBuf[0] == ' ') {
                vertexCount++;
            }
            if (lineBuf[0] == 'n') {
                normalCount++;
            }
            if (lineBuf[0] == 't') {
                UVTextureCount++;
            }
        } else if (temp == 'f') {
            facesCount ++;
        } else if (temp == 'u') {
            textureCount ++;
        } else if (temp == 'm') {
            sscanf(lineBuf, "tllib %s", usedMtl);
        }
    }
    rewind(file_fd);

    printf("--- Loading OBJ file \"%s\" ---\nVertices: %d\nNormals: %d\nUV Coordinates: %d\nFaces: %d\nMTL file: \"%s\"\nTexture count: %d\n", 
            filename, vertexCount, normalCount, UVTextureCount, facesCount, usedMtl, textureCount);
    if (vertexCount == 0) {
        logData(LOGLEVEL_ERR, "OBJ Model %s is either an invalid file or is corrupted", __FILE__, __LINE__, (void*[]) {(void*)1, &filename});
        return NULL;
    }
    struct object *object = malloc(sizeof(struct object));
    object->vertices_size = vertexCount;
    object->normals_size = normalCount;
    object->UV_coords_size = UVTextureCount;


    object->vertices = malloc(sizeof(GLfloat) * 3 * object->vertices_size);
    object->normals = malloc(sizeof(GLfloat) * 3 * object->normals_size);
    object->UV_coords = malloc(sizeof(GLfloat) * 2 * object->UV_coords_size);
    object->indices = malloc(sizeof(int)*object->vertices_size);

    struct object* final_object = malloc (sizeof(struct object));
    final_object->textures_size=textureCount;
    final_object->textures = malloc(sizeof(GLuint)*textureCount);
    final_object->textureNames = malloc(sizeof(char*)*textureCount);
    final_object->texture_counts = malloc(sizeof(int)*textureCount);

    int normalsUsed = object->normals_size>0?1:0; // some models do not have normals, ex: half life maps
    int textureUsed = object->UV_coords_size>0?1:0;
    struct face {
        int index_vertex_x;
        int index_vertex_y;
        int index_vertex_z;

        int index_normal_x;
        int index_normal_y;
        int index_normal_z;

        int index_uv_x;
        int index_uv_y;
        int index_uv_z;
    };

    struct face faces[facesCount];
    char textureName[_MAX_TEXTURE_LEN];

    rewind(file_fd);
    vertexCount = 0, normalCount = 0, UVTextureCount = 0, facesCount = 0, textureCount = 0;
    GLfloat vertexX, vertexY, vertexZ;
    while ((temp = fgetc(file_fd)) != EOF) {
        fgets(lineBuf, _MAX_LINE_LEN, file_fd);
        if (temp == 'v') {
            if (lineBuf[0] == ' ') {
                sscanf(lineBuf, " %f %f %f", &vertexX, &vertexY, &vertexZ);
                object->vertices[vertexCount*3] =   strcmp(filename, "res/hl_mp_stalkyard.obj")==0?vertexX/60:vertexX;     // TODO XYZ, hl map has XZY, also needs /100
                object->vertices[vertexCount*3+1] = strcmp(filename, "res/hl_mp_stalkyard.obj")==0?vertexZ/60:vertexY;     // TODO XYZ, hl map has XZY, also needs /100
                object->vertices[vertexCount*3+2] = strcmp(filename, "res/hl_mp_stalkyard.obj")==0?vertexY/60:vertexZ;     // TODO XYZ, hl map has XZY, also needs /100
                vertexCount++;
            }
            if (lineBuf[0] == 'n') {
                sscanf(lineBuf, "n %f %f %f", &vertexX, &vertexY, &vertexZ);
                object->normals[normalCount*3] =   vertexX;
                object->normals[normalCount*3+1] = vertexY;
                object->normals[normalCount*3+2] = vertexZ;
                normalCount++;
            }
            if (lineBuf[0] == 't') {
                sscanf(lineBuf, "t %f %f", &vertexX, &vertexY);
                object->UV_coords[UVTextureCount*2] =   vertexX;
                object->UV_coords[UVTextureCount*2+1] = vertexY;
                UVTextureCount++;
            }
        } else if (temp == 'f') {
            if (normalsUsed && textureUsed) {
                sscanf(lineBuf, " %d/%d/%d %d/%d/%d %d/%d/%d", 
                    &faces[facesCount].index_vertex_x,
                    &faces[facesCount].index_uv_x,
                    &faces[facesCount].index_normal_x,
                    &faces[facesCount].index_vertex_y,
                    &faces[facesCount].index_uv_y,
                    &faces[facesCount].index_normal_y,
                    &faces[facesCount].index_vertex_z,
                    &faces[facesCount].index_uv_z,
                    &faces[facesCount].index_normal_z);
            } else if(textureUsed && !normalsUsed) {
                sscanf(lineBuf, " %d/%d %d/%d %d/%d",
                    &faces[facesCount].index_vertex_x,
                    &faces[facesCount].index_uv_x,
                    &faces[facesCount].index_vertex_y,
                    &faces[facesCount].index_uv_y,
                    &faces[facesCount].index_vertex_z,
                    &faces[facesCount].index_uv_z
                );
            } else {
                sscanf(lineBuf, " %d//%d %d//%d %d//%d",
                    &faces[facesCount].index_vertex_x,
                    &faces[facesCount].index_normal_x,
                    &faces[facesCount].index_vertex_y,
                    &faces[facesCount].index_normal_y,
                    &faces[facesCount].index_vertex_z,
                    &faces[facesCount].index_normal_z
                );
            }
            facesCount++;
        } else if (temp == 'u') {
            sscanf(lineBuf, "semtl %s", textureName);
            final_object->textureNames[textureCount] = malloc(sizeof(char)*_MAX_TEXTURE_LEN);
            strncpy(final_object->textureNames[textureCount], textureName, _MAX_TEXTURE_LEN);
            final_object->texture_counts[textureCount] = facesCount*3;
            textureCount++;
        }
    }
    
    final_object->vertices= malloc(sizeof(GLfloat) * 3 * facesCount * 3); // worst case scenario
    final_object->normals = malloc(sizeof(GLfloat) * 3 * facesCount * 3); // worst case scenario
    final_object->UV_coords=malloc(sizeof(GLfloat) * 2 * facesCount * 3); // worst case scenario
    final_object->indices =     malloc(sizeof(int) * 3 * facesCount);

    vertexCount = 0; // vertex, uv, normal count is always the same
    indexCount = 0;
    int found = -1;
    for (int i = 0; i<facesCount; i++) {
        found = -1;
        for (int j = 0; j<vertexCount; j++) { 
            if (object->vertices[(faces[i].index_vertex_x-1)*3]   == final_object->vertices[j*3] && // OBJ indexes are 1 based
                object->vertices[(faces[i].index_vertex_x-1)*3+1] == final_object->vertices[j*3+1] &&
                object->vertices[(faces[i].index_vertex_x-1)*3+2] == final_object->vertices[j*3+2] &&
                ( !normalsUsed ||
                object->normals[(faces[i].index_normal_x-1)*3]    == final_object->normals[j*3] &&
                object->normals[(faces[i].index_normal_x-1)*3+1]  == final_object->normals[j*3+1] &&
                object->normals[(faces[i].index_normal_x-1)*3+2]  == final_object->normals[j*3+2]
                ) &&
                ( !textureUsed ||
                object->UV_coords[(faces[i].index_uv_x-1)*2]      == final_object->UV_coords[j*2] &&
                object->UV_coords[(faces[i].index_uv_x-1)*2+1]    == final_object->UV_coords[j*2+1])) {
                    found = j;
            }
        }
        if (found != -1) {
            final_object->indices[indexCount] = found;
        } else {
            final_object->vertices[vertexCount*3+0] = object->vertices[(faces[i].index_vertex_x-1)*3+0];
            final_object->vertices[vertexCount*3+1] = object->vertices[(faces[i].index_vertex_x-1)*3+1];
            final_object->vertices[vertexCount*3+2] = object->vertices[(faces[i].index_vertex_x-1)*3+2];
            if (normalsUsed) {
                final_object->normals[vertexCount*3+0] = object->normals[(faces[i].index_normal_x-1)*3+0];
                final_object->normals[vertexCount*3+1] = object->normals[(faces[i].index_normal_x-1)*3+1];
                final_object->normals[vertexCount*3+2] = object->normals[(faces[i].index_normal_x-1)*3+2];
            }
            if (textureUsed) {
                final_object->UV_coords[vertexCount*2+0] = object->UV_coords[(faces[i].index_uv_x-1)*2+0];
                final_object->UV_coords[vertexCount*2+1] = object->UV_coords[(faces[i].index_uv_x-1)*2+1];
            }
            final_object->indices[indexCount] = vertexCount;

            vertexCount++;
        }
        indexCount++;
        found = -1;
        for (int j = 0; j<vertexCount; j++) { 
            if (object->vertices[(faces[i].index_vertex_y-1)*3]   == final_object->vertices[j*3] && // OBJ indexes are 1 based
                object->vertices[(faces[i].index_vertex_y-1)*3+1] == final_object->vertices[j*3+1] &&
                object->vertices[(faces[i].index_vertex_y-1)*3+2] == final_object->vertices[j*3+2] &&
                ( !normalsUsed ||
                object->normals[(faces[i].index_normal_y-1)*3]    == final_object->normals[j*3] &&
                object->normals[(faces[i].index_normal_y-1)*3+1]  == final_object->normals[j*3+1] &&
                object->normals[(faces[i].index_normal_y-1)*3+2]  == final_object->normals[j*3+2]
                ) &&
                ( !textureUsed ||
                object->UV_coords[(faces[i].index_uv_y-1)*2]      == final_object->UV_coords[j*2] &&
                object->UV_coords[(faces[i].index_uv_y-1)*2+1]    == final_object->UV_coords[j*2+1])) {
                    found = j;
            }
        }
        if (found != -1) {
            final_object->indices[indexCount] = found;
        } else {
            final_object->vertices[vertexCount*3+0] = object->vertices[(faces[i].index_vertex_y-1)*3+0];
            final_object->vertices[vertexCount*3+1] = object->vertices[(faces[i].index_vertex_y-1)*3+1];
            final_object->vertices[vertexCount*3+2] = object->vertices[(faces[i].index_vertex_y-1)*3+2];
            if (normalsUsed) {
                final_object->normals[vertexCount*3+0] = object->normals[(faces[i].index_normal_y-1)*3+0];
                final_object->normals[vertexCount*3+1] = object->normals[(faces[i].index_normal_y-1)*3+1];
                final_object->normals[vertexCount*3+2] = object->normals[(faces[i].index_normal_y-1)*3+2];
            }
            if (textureUsed) {
                final_object->UV_coords[vertexCount*2+0] = object->UV_coords[(faces[i].index_uv_y-1)*2+0];
                final_object->UV_coords[vertexCount*2+1] = object->UV_coords[(faces[i].index_uv_y-1)*2+1];
            }
            final_object->indices[indexCount] = vertexCount;

            vertexCount++;
        }
        indexCount++;
        found = -1;
        for (int j = 0; j<vertexCount; j++) { 
            if (object->vertices[(faces[i].index_vertex_z-1)*3]   == final_object->vertices[j*3] && // OBJ indexes are 1 based
                object->vertices[(faces[i].index_vertex_z-1)*3+1] == final_object->vertices[j*3+1] &&
                object->vertices[(faces[i].index_vertex_z-1)*3+2] == final_object->vertices[j*3+2] &&
                ( !normalsUsed ||
                object->normals[(faces[i].index_normal_z-1)*3]    == final_object->normals[j*3] &&
                object->normals[(faces[i].index_normal_z-1)*3+1]  == final_object->normals[j*3+1] &&
                object->normals[(faces[i].index_normal_z-1)*3+2]  == final_object->normals[j*3+2]
                ) &&
                ( !textureUsed ||
                object->UV_coords[(faces[i].index_uv_z-1)*2]      == final_object->UV_coords[j*2] &&
                object->UV_coords[(faces[i].index_uv_z-1)*2+1]    == final_object->UV_coords[j*2+1])) {
                    found = j;
            }
        }
        if (found != -1) {
            final_object->indices[indexCount] = found;
        } else {
            final_object->vertices[vertexCount*3+0] = object->vertices[(faces[i].index_vertex_z-1)*3+0];
            final_object->vertices[vertexCount*3+1] = object->vertices[(faces[i].index_vertex_z-1)*3+1];
            final_object->vertices[vertexCount*3+2] = object->vertices[(faces[i].index_vertex_z-1)*3+2];
            if (normalsUsed) {
                final_object->normals[vertexCount*3+0] = object->normals[(faces[i].index_normal_z-1)*3+0];
                final_object->normals[vertexCount*3+1] = object->normals[(faces[i].index_normal_z-1)*3+1];
                final_object->normals[vertexCount*3+2] = object->normals[(faces[i].index_normal_z-1)*3+2];
            }
            if (textureUsed) {
                final_object->UV_coords[vertexCount*2+0] = object->UV_coords[(faces[i].index_uv_z-1)*2+0];
                final_object->UV_coords[vertexCount*2+1] = object->UV_coords[(faces[i].index_uv_z-1)*2+1];
            }
            final_object->indices[indexCount] = vertexCount;

            vertexCount++;
        }
        indexCount++;
    }
    final_object->normals_size = final_object->vertices_size = final_object->UV_coords_size = vertexCount;
    final_object->indices_size = indexCount;
    printf("Freeing temporary buffer\n");
    
    free(object->indices);
    free(object->vertices);
    free(object->UV_coords);
    free(object);

    final_object->colors = malloc(sizeof(GLfloat)*3*vertexCount); // just in case
    for (int i = 0; i<vertexCount; i++) {
        final_object->colors[i*3] = 1;
        final_object->colors[i*3+1] = 1;
        final_object->colors[i*3+2] = 1;
    }
    

    printf("Expanding vertices for collision detection from %d to %d\n", final_object->vertices_size, final_object->indices_size);
    final_object->unindexed_vertices = malloc(sizeof(GLfloat)*3*final_object->indices_size);
    if (final_object->unindexed_vertices == NULL) {
        perror("Failed to allocate enough memory for unindexed vertices!\n");
        exit(5);
    }
    for (int i = 0; i<final_object->indices_size;i++){
        memcpy(&final_object->unindexed_vertices[i*3], &final_object->vertices[final_object->indices[i]*3], sizeof(GLfloat)*3);
    }

    //parse MTL

    char pathname[_MAX_FILE_PATH];
    pathname[0] = '.';
    pathname[1] = '/';
    strncpy(pathname+2, filename, _MAX_FILE_PATH-2);
    int lastPath = 0;
    for (int i =0; i<strlen(filename); i++) {
        if (filename[i] == '/') lastPath = i+1;
    }
    strcpy(pathname+lastPath+2, usedMtl); // FIX THIS ACTUALLY BROKEN AND VULNERABLE AF TODO
    printf("MTL path: %s\n", pathname);

    fclose(file_fd);
    file_fd = NULL;
    if ((file_fd = fopen(pathname, "r")) == NULL) {
        logData(LOGLEVEL_CRIT, "Could not open MTL file %s for model %s", __FILE__, __LINE__, (void*[]){(void*)2, &pathname, &filename});
        perror("Could not open file");
        exit(-1);
    }

    char paths[textureCount][_MAX_FILE_PATH];
    int index = -1;

    while ((temp = fgetc(file_fd)) != EOF) {
        fgets(lineBuf, _MAX_LINE_LEN, file_fd);
        if (temp == '\n') {
            sscanf(lineBuf, "newmtl %s", textureName);
        } else if (temp == 'm') {
            sscanf(lineBuf, "ap_Kd %s", pathname+lastPath+2); // TODO INSANELY VULNERABLE SHOULD DEFINITELY NOT HIT PRODUCTION
            index = -1;
            for (int i = 0; i<textureCount; i++) {
                if (!strcmp(final_object->textureNames[i], textureName)) {
                    index = i;
                    strncpy(paths[i], pathname, _MAX_FILE_PATH);
                }
            }
            if (index == -1) {
                printf("Erronious material %s, wrong name?\n", textureName);
            }
        }
    }

    BMPFileHeader bmphdr;
    glGenTextures(final_object->textures_size, final_object->textures);
    for (int i = 0; i<textureCount; i++) {
        printf("Loading texture %s from file %s\n", final_object->textureNames[i], paths[i]);

        if (file_fd != NULL) fclose(file_fd);
        file_fd = NULL;
        if ((file_fd = fopen(paths[i], "r")) == NULL) {
            logData(LOGLEVEL_CRIT, "Could not open texture file for texture %s for model %s", __FILE__, __LINE__, (void*[]){(void*)2, &final_object->textureNames[i], &filename});
            continue;
            //perror("Could not open file");
            //exit(-1);
        }

        if (fread(bmphdr.rawheader, 1, 54, file_fd)!=54 || (char)bmphdr.header.hdrfield != 'B') {
            logData(LOGLEVEL_CRIT, "Texture file for texture %s for model %s either corrupt or truncated", __FILE__, __LINE__, (void*[]){(void*)2, &final_object->textureNames[i], &filename});
            continue;
            //perror("Could not open file");
            //exit(-1);
        }

        unsigned char* colordata = malloc(sizeof(unsigned char)*bmphdr.header.width*bmphdr.header.height*(bmphdr.header.bpp/8));
        if (colordata == NULL) {
            logData(LOGLEVEL_CRIT, "Texture file for texture %s for model %s - OOM", __FILE__, __LINE__, (void*[]){(void*)2, &final_object->textureNames[i], &filename});
            continue;
            //perror("Could not open file");
            //exit(-1);
        } 
        if (bmphdr.header.starting_offset == 0) bmphdr.header.starting_offset = 54;


        fseek(file_fd, bmphdr.header.starting_offset, SEEK_SET);

        if (fread(colordata, 1, bmphdr.header.width*bmphdr.header.height*(bmphdr.header.bpp/8), file_fd) != bmphdr.header.width*bmphdr.header.height*(bmphdr.header.bpp/8)) {
            logData(LOGLEVEL_CRIT, "Texture file for texture %s for model %s either corrupt or truncated - failed to read colors", __FILE__, __LINE__, (void*[]){(void*)2, &final_object->textureNames[i], &filename});
            goto ALLOCATED;
            //perror("Could not open file");
            //exit(-1);
        }

        glBindTexture(GL_TEXTURE_2D, final_object->textures[i]);
        switch (bmphdr.header.bpp) {
            case 24:
                glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, bmphdr.header.width, bmphdr.header.height, 0, GL_BGR, GL_UNSIGNED_BYTE, colordata);
                break;
            case 32:
                glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, bmphdr.header.width, bmphdr.header.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, colordata);
                break;
            default:
                logData(LOGLEVEL_WARN, "Unknown bpp for texture %s for model %s: %d, falling back to 24", __FILE__, __LINE__, (void*[]) {(void*)3, &final_object->textureNames[i], &filename, &bmphdr.header.bpp});
                glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, bmphdr.header.width, bmphdr.header.height, 0, GL_BGR, GL_UNSIGNED_BYTE, colordata);
        }

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

        
        glGenerateMipmap(GL_TEXTURE_2D);
        ALLOCATED:
        free(colordata);
        printf("Loaded texture of size %dx%d\n", bmphdr.header.width, bmphdr.header.height);
    }

    gettimeofday(&timer2, NULL);
    float timeTaken = (timer2.tv_sec*1000000+timer2.tv_usec) - (timer1.tv_sec*1000000 + timer1.tv_usec);
    printf("Loaded %s in %lf msec, with final vertex count %d and index count %d\n", filename, timeTaken/1000, vertexCount, final_object->indices_size);
    return final_object;
}