/*    
      glm.c
      Nate Robins, 1997, 2000
      nate@pobox.com, http://www.pobox.com/~nate
 
      Wavefront OBJ model file format reader/writer/manipulator.

      Includes routines for generating smooth normals with
      preservation of edges, welding redundant vertices & texture
      coordinate generation (spheremap and planar projections) + more.
  
      Modified for Tcl3D by Paul Obermeier 2006/01/05
      See www.tcl3d.org for the Tcl3D extension.
*/


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#if defined(WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include <winnt.h>
#endif

#include <GL/glew.h>

#include "tcl3dModel.h"
#include "tcl3dModelFmtSab.h"

#define T(x) (model->triangles[(x)])

/* glmFirstPass: first pass at a Wavefront OBJ file that gets all the
 * statistics of the model (such as #vertices, #normals, etc)
 *
 * model - properly initialized GLMmodel structure
 * file  - (fopen'd) file descriptor 
 */
static int
glmFirstPass(GLMmodel* model, FILE* file) 
{
    GLuint  numvertices;        /* number of vertices in model */
    GLuint  numtriangles;       /* number of triangles in model */
    GLuint  numpolygons;        /* number of polygons in model */
    GLMgroup* group;            /* current group */
    int     i, j, lineNum;
    int     vtxNum, faceNum, numVertsPerPgon;
    int     ind;
    GLfloat x, y, z;

    /* make a default group */
    group = glmAddGroup(model, "default");

    numvertices = numpolygons = numtriangles = 0;
    if (fscanf (file, "%d %d\n", &numvertices, &numpolygons) != 2) {
        printf ("glmFirstPass(): Cannot read header\n");
        return 0;
    }

    lineNum = 2;
    for (i=1; i<=numvertices; i++) {
        if ((fscanf (file, "%d %f %f %f\n", &vtxNum, &x, &y, &z) != 4) ||
                (i != vtxNum)) {
            printf ("glmFirstPass (Line %d): Inconsistent number of vertices\n", lineNum);
            return 0;
        }
        lineNum++;
    }

    for (i=1; i<=numpolygons; i++) {
        if ((fscanf (file, "%d %d", &faceNum, &numVertsPerPgon) != 2) ||
                (i != faceNum) || (numVertsPerPgon < 3)) {
            printf ("glmFirstPass (Line %d): Inconsistent number of polygons\n", lineNum);
            return 0;
        }
        for (j=0; j<numVertsPerPgon; j++) {
            if ((fscanf (file, "%d", &ind) != 1) || (ind > numvertices)) {
                printf ("glmFirstPass (Line %d): Inconsistent number of polygons\n", lineNum);
                return 0;
            }
        }
        numtriangles += (numVertsPerPgon -2);
        lineNum++;
    }

    /* set the stats in the group structure. */
    group->numtriangles = numtriangles;

    /* set the stats in the model structure. */
    model->numvertices  = numvertices;
    model->numnormals   = 0;
    model->numtexcoords = 0;
    model->numtriangles = numtriangles;

    /* allocate memory for the triangles in each group */
    group = model->groups;
    while(group) {
        group->triangles = (GLuint*)malloc(sizeof(GLuint) * group->numtriangles);
        group->numtriangles = 0;
        group = group->next;
    }
    return 1;
}

/* glmSecondPass: second pass at a Wavefront OBJ file that gets all
 * the data.
 *
 * model - properly initialized GLMmodel structure
 * file  - (fopen'd) file descriptor 
 */
static GLvoid
glmSecondPass(GLMmodel* model, FILE* file) 
{
    GLuint  numvertices;        /* number of vertices in model */
    GLuint  numtriangles;       /* number of triangles in model */
    GLuint  numpolygons;       /* number of triangles in model */
    GLfloat* vertices;           /* array of vertices  */
    GLMgroup* group;            /* current group pointer */
    int i, j;
    int ind, ind0, ind1, ind2;
    int vtxNum, faceNum, numVertsPerPgon;
    
    /* set the pointer shortcuts */
    vertices   = model->vertices;
    group      = model->groups;
    
    /* on the second pass through the file, read all the data into the
    allocated arrays */
    numtriangles = 0;

    fscanf (file, "%d %d\n", &numvertices, &numpolygons);

    for (i=1; i<=numvertices; i++) {
        fscanf (file, "%d %f %f %f\n", &vtxNum, 
                &vertices[3*i + 0], 
                &vertices[3*i + 1], 
                &vertices[3*i + 2]);
    }

    for (i=1; i<=numpolygons; i++) {
        fscanf (file, "%d %d", &faceNum, &numVertsPerPgon);
        fscanf (file, "%d %d %d", &ind0, &ind1, &ind2);
        T(numtriangles).vindices[0] = ind0;
        T(numtriangles).vindices[1] = ind1;
        T(numtriangles).vindices[2] = ind2;
        group->triangles[group->numtriangles++] = numtriangles;
        numtriangles++;
        for (j=3; j<numVertsPerPgon; j++) {
            fscanf (file, "%d", &ind);
            T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
            T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
            T(numtriangles).vindices[2] = ind;
            group->triangles[group->numtriangles++] = numtriangles;
            numtriangles++;
        }
    }
}

/* public functions */

/* glmReadSAB: Reads a model description from a Wavefront .SAB file.
 * Returns a pointer to the created object which should be free'd with
 * glmDelete().
 *
 * filename - name of the file containing the Wavefront .SAB format data.  
 */
GLMmodel* 
glmReadSAB(char* filename)
{
    GLMmodel* model;
    FILE*     file;
    
    /* open the file */
    file = fopen(filename, "r");
    if (!file) {
        fprintf(stderr, "glmReadSAB() failed: can't open data file \"%s\".\n",
            filename);
        return NULL;
    }
    
    /* allocate a new model */
    model = glmNewModel (filename);
    
    /* make a first pass through the file to get a count of the number
    of vertices and triangles */
    if (!glmFirstPass (model, file)) {
       return NULL;
    }
    
    /* allocate memory */
    model->vertices = (GLfloat*)malloc(sizeof(GLfloat) *
        3 * (model->numvertices + 1));
    model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
        model->numtriangles);
    
    /* rewind to beginning of file and read in the data this pass */
    rewind(file);
    
    glmSecondPass(model, file);
    
    /* close the file */
    fclose(file);
    
    return model;
}

/* glmWriteSAB: Writes a model description in Wavefront .SAB format to
 * a file.
 *
 * model - initialized GLMmodel structure
 * filename - name of the file to write the Wavefront .SAB format data to
 */
GLvoid
glmWriteSAB(GLMmodel* model, char* filename)
{
    int  i, triaIndex;
    FILE *file;
    GLMgroup* group;
    
    assert(model);
    
    /* open the file */
    file = fopen(filename, "w");
    if (!file) {
        fprintf(stderr, "glmWriteSAB() failed: can't open file \"%s\" to write.\n",
            filename);
        exit(1);
    }
    
    /* spit out the header */
    fprintf(file, "%d %d\n", model->numvertices, model->numtriangles);
    
    /* spit out the vertices */
    for (i=1; i<=model->numvertices; i++) {
        fprintf(file, "%6d %15.7f %15.7f %15.7f\n", i,
            model->vertices[3 * i + 0],
            model->vertices[3 * i + 1],
            model->vertices[3 * i + 2]);
    }
    
    group = model->groups;
    triaIndex = 1;
    while(group) {
        for (i=0; i<group->numtriangles; i++) {
            fprintf(file, "%d %d %d %d %d\n",
                    triaIndex, 3,
                    T(group->triangles[i]).vindices[0],
                    T(group->triangles[i]).vindices[1],
                    T(group->triangles[i]).vindices[2]);
            triaIndex++;
        }
        group = group->next;
    }
    
    fclose(file);
}
