/**
 * Parsing a txt file into a complex struct and saving it as binary
 * Uses C99 dialect, so compile with -std=gnu99 or -std=c99
 * or via IDE-s Visual Studio 2013 (and higher) / DevC++5.7 (and higher)
 */
#include <stdlib.h>    // malloc / free
#include <stdio.h>     // printf / fopen / fread / ...


typedef struct _Vertex {
    struct { float x, y, z; } pos;   // vertex position
    struct { float x, y, z; } norm;  // vertex normal
    struct { float u, v;    } tex;   // vertex texture coordinates
} Vertex;


typedef struct _Model3D
{
    int     numVertices;  // number of allocated vertices
    Vertex  vertices[0];  // dynamically allocated vertex buffer, actually vertices[numVertices]
} Model3D;


Model3D* model_parsetxt(char* buffer, int size)
{
    char* ptr = buffer;

    // first value should be vertices count
    int vertexCount = strtol(ptr, &ptr, 10);
    if (vertexCount < 0 || vertexCount > 65536)
        return NULL; // failed, return NULL

    // initialize Model3D - a dynamic struct 'hack' - very common in all game engines
    Model3D* model = malloc(sizeof(Model3D) + sizeof(Vertex) * vertexCount);
    model->numVertices = vertexCount;

    for (int i = 0; i < vertexCount; ++i) // parse the specified number of vertices
    {
        Vertex* v = &model->vertices[i];
        v->pos.x  = strtof(ptr, &ptr); // consume floats and advance the &end pointer
        v->pos.y  = strtof(ptr, &ptr);
        v->pos.z  = strtof(ptr, &ptr);
        v->norm.x = strtof(ptr, &ptr);
        v->norm.y = strtof(ptr, &ptr);
        v->norm.z = strtof(ptr, &ptr);
        v->tex.u  = strtof(ptr, &ptr);
        v->tex.v  = strtof(ptr, &ptr);
    }
    return model;
}

void model_saveobj(const char* filename, Model3D* model)
{
    FILE* f = fopen(filename, "wb");
    if (f != NULL)
    {
        fwrite(&model->numVertices, sizeof(int), 1, f);
        fwrite(model->vertices, sizeof(Vertex) * model->numVertices, 1, f);

        printf("Model3D_Save:  %d bytes and %d vertices to '%s'\n", ftell(f), model->numVertices, filename);
        fclose(f);
    }
}

Model3D* model_loadobj(const char* filename)
{
    FILE* f = fopen(filename, "rb");
    if (f != NULL)
    {
        fseek(f, 0, SEEK_END);
        int size = ftell(f);    // get file size right after file open
        fseek(f, 0, SEEK_SET);

        Model3D* model = malloc(size); // model3d files are complete objects
        fread(model, size, 1, f);
        fclose(f);

        printf("Model3D_Load:  %d bytes and %d vertices from '%s'\n", size, model->numVertices, filename);

        // Model3D is now ready for rendering
        return model;
    }
    return NULL;
}

void model_destroy(Model3D* model)
{
    free(model);
}

int main(int argc, char** argv)
{
    static const char srcFile[] = "cube.txt";
    // cube.txt contents:
/*36
-1.0  1.0 -1.0 0.0 0.0  0.0  0.0 -1.0
 1.0  1.0 -1.0 1.0 0.0  0.0  0.0 -1.0
-1.0 -1.0 -1.0 0.0 1.0  0.0  0.0 -1.0
-1.0 -1.0 -1.0 0.0 1.0  0.0  0.0 -1.0
 1.0  1.0 -1.0 1.0 0.0  0.0  0.0 -1.0
 1.0 -1.0 -1.0 1.0 1.0  0.0  0.0 -1.0
 1.0  1.0 -1.0 0.0 0.0  1.0  0.0  0.0
 1.0  1.0  1.0 1.0 0.0  1.0  0.0  0.0
 1.0 -1.0 -1.0 0.0 1.0  1.0  0.0  0.0
 1.0 -1.0 -1.0 0.0 1.0  1.0  0.0  0.0
 1.0  1.0  1.0 1.0 0.0  1.0  0.0  0.0
 1.0 -1.0  1.0 1.0 1.0  1.0  0.0  0.0
 1.0  1.0  1.0 0.0 0.0  0.0  0.0  1.0
-1.0  1.0  1.0 1.0 0.0  0.0  0.0  1.0
 1.0 -1.0  1.0 0.0 1.0  0.0  0.0  1.0
 1.0 -1.0  1.0 0.0 1.0  0.0  0.0  1.0
-1.0  1.0  1.0 1.0 0.0  0.0  0.0  1.0
-1.0 -1.0  1.0 1.0 1.0  0.0  0.0  1.0
-1.0  1.0  1.0 0.0 0.0 -1.0  0.0  0.0
-1.0  1.0 -1.0 1.0 0.0 -1.0  0.0  0.0
-1.0 -1.0  1.0 0.0 1.0 -1.0  0.0  0.0
-1.0 -1.0  1.0 0.0 1.0 -1.0  0.0  0.0
-1.0  1.0 -1.0 1.0 0.0 -1.0  0.0  0.0
-1.0 -1.0 -1.0 1.0 1.0 -1.0  0.0  0.0
-1.0  1.0  1.0 0.0 0.0  0.0  1.0  0.0
 1.0  1.0  1.0 1.0 0.0  0.0  1.0  0.0
-1.0  1.0 -1.0 0.0 1.0  0.0  1.0  0.0
-1.0  1.0 -1.0 0.0 1.0  0.0  1.0  0.0
 1.0  1.0  1.0 1.0 0.0  0.0  1.0  0.0
 1.0  1.0 -1.0 1.0 1.0  0.0  1.0  0.0
-1.0 -1.0 -1.0 0.0 0.0  0.0 -1.0  0.0
 1.0 -1.0 -1.0 1.0 0.0  0.0 -1.0  0.0
-1.0 -1.0  1.0 0.0 1.0  0.0 -1.0  0.0
-1.0 -1.0  1.0 0.0 1.0  0.0 -1.0  0.0
 1.0 -1.0 -1.0 1.0 0.0  0.0 -1.0  0.0
 1.0 -1.0  1.0 1.0 1.0  0.0 -1.0  0.0*/

    FILE* f;
    if (f = fopen(srcFile, "rb"))
    {
        fseek(f, 0, SEEK_END);
        int size = ftell(f);    // get file size right after file open
        fseek(f, 0, SEEK_SET);

        char* buffer = malloc(size);        // allocate buffer
        fread(buffer, size, 1, f);          // read all data
        fclose(f);                          // close the file


        // load model from .txt and save it as a binary file
        Model3D* model = model_parsetxt(buffer, size);
        free(buffer);       // free the allocated buffer
        printf("Model3D_Parse: %d bytes and %d vertices from '%s'\n", size, model->numVertices, srcFile);


        model_saveobj("cube.bin", model);
        model_destroy(model);


        // reload cube.bin for testing:
        model = model_loadobj("cube.bin");
        model_destroy(model);
    }
    else
    {
        printf("Failed to open file '%s'\n", srcFile);
    }

    system("pause");
    return 0;
}