/*    
      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 "tcl3dUtilPortable.h"
#include "tcl3dModel.h"
#include "tcl3dModelFmtPof.h"

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

/* The magic numbers for head and tail. */

#define MAGIC_NUMBER_BEGIN 1926047081
#define MAGIC_NUMBER_END   1926050994

/* glmReadPOF: Reads a model description from a binary POF file.
 * Returns a pointer to the created object which should be free'd with
 * glmDelete().
 *
 * filename - name of the file containing the POF format data.  
 */
GLMmodel* 
glmReadPOF(char* filename)
{
    GLMmodel *model;
    FILE     *file;
    GLfloat  *vertices;
    GLMgroup *group;
    GLuint   magicHead, magicTail;
    GLushort nVerts;
    GLushort nTrias;
    GLfloat  coord[3];
    GLushort vtx[3];
    GLubyte  mat[2];
    int      i;
    int      haveIntel;
    GLbyte   buf[3 * sizeof (GLfloat)];
    
    /* open the file */
    file = fopen(filename, "rb");
    if (!file) {
        fprintf(stderr, "glmReadPOF() failed: can't open data file \"%s\".\n",
            filename);
        return NULL;
    }
    
    /* allocate a new model */
    model = glmNewModel (filename);
    /* make a default group */
    group = glmAddGroup(model, "default");
    
    /* Read the header information: Number of vertices and triangles. */
    fread (&magicHead, sizeof (magicHead),  1, file);
    haveIntel = -1;
    if (magicHead == MAGIC_NUMBER_BEGIN && tcl3dIsIntel()) {
        haveIntel = 1;
    } else if (magicHead != MAGIC_NUMBER_BEGIN) {
        if (!tcl3dIsIntel() && 
            MAGIC_NUMBER_BEGIN == tcl3dByteToGLuint ((const GLbyte *) magicHead)) {
            haveIntel = 0;
        }
    }
    if (haveIntel == -1) {
        fprintf(stderr, "glmReadPOF() failed: Magic header of \"%s\" incorrect.\n",
            filename);
        return NULL;
    }

    if (haveIntel) {
        fread (&nVerts, sizeof (nVerts), 1, file);
        fread (&nTrias, sizeof (nTrias), 1, file); 
        model->numvertices  = nVerts;
        model->numtriangles = nTrias;
    } else {
        fread (buf, 2*2, 1, file);
        model->numvertices  = tcl3dByteToGLushort (buf + 0);
        model->numtriangles = tcl3dByteToGLushort (buf + 2);
    }
    model->numnormals   = 0;
    model->numtexcoords = 0;

    /* set the stats in the group structure. */
    group->numtriangles = model->numtriangles;
    group->triangles = (GLuint*)malloc(sizeof(GLuint) * group->numtriangles);

    /* allocate memory */
    model->vertices = (GLfloat*)malloc(sizeof(GLfloat) *
        3 * (model->numvertices + 1));
    model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
        model->numtriangles);
    
    /* set the pointer shortcuts */
    vertices = model->vertices;

    /* Now read the actual data. */
    for (i=1; i<=model->numvertices; i++) {
        if (haveIntel) {
            fread (coord, sizeof (coord), 1, file);
            vertices[3*i + 0] = coord[0];
            vertices[3*i + 1] = coord[1];
            vertices[3*i + 2] = coord[2];
        } else {
            fread (buf, 3*4, 1, file);
            vertices[3*i + 0] = tcl3dByteToGLfloat (buf + 0);
            vertices[3*i + 1] = tcl3dByteToGLfloat (buf + 4);
            vertices[3*i + 2] = tcl3dByteToGLfloat (buf + 8);
        }
    }

    nTrias = 0;
    for (i=1; i<=model->numtriangles; i++) {
        if (haveIntel) {
            fread (vtx, sizeof (vtx), 1, file);
            T(nTrias).vindices[0] = vtx[0] + 1;
            T(nTrias).vindices[1] = vtx[1] + 1;
            T(nTrias).vindices[2] = vtx[2] + 1;
        } else {
            fread (buf, 3*2, 1, file);
            T(nTrias).vindices[0] = tcl3dByteToGLushort (buf + 0) + 1;
            T(nTrias).vindices[1] = tcl3dByteToGLushort (buf + 2) + 1;
            T(nTrias).vindices[2] = tcl3dByteToGLushort (buf + 4) + 1;
        }
        fread (mat, sizeof (mat), 1, file);
        /* TODO: Do something with the materials. */
        group->triangles[nTrias] = nTrias;
        nTrias++;
    }

    fread (&magicTail, sizeof (magicTail),  1, file);

    /* close the file */
    fclose(file);
    
    return model;
}

/* glmWritePOF: Writes a model description in Wavefront .POF format to
 * a file.
 *
 * model - initialized GLMmodel structure
 * filename - name of the file to write the Wavefront .POF format data to
 */
void glmWritePOF (GLMmodel* model, char* filename)
{
    int  i;
    FILE *file;
    GLMgroup* group;
    GLint     magic;
    GLushort  nVerts;
    GLushort  nTrias;
    GLfloat   coord[3];
    GLushort  vtx[3];
    GLubyte   mat[2];
    GLbyte    buf[3*sizeof (GLfloat)];
    int       haveIntel;
    
    assert(model);
    
    /* open the file */
    file = fopen(filename, "wb");
    if (!file) {
        fprintf(stderr, "glmWritePOF() failed: can't open file \"%s\" to write.\n",
            filename);
        exit(1);
    }
    
    haveIntel = tcl3dIsIntel ();

    /* spit out the header */
    magic = MAGIC_NUMBER_BEGIN;
    nVerts = model->numvertices;
    nTrias = model->numtriangles;
    if (haveIntel) {
        fwrite (&magic,  sizeof (magic),  1, file);
        fwrite (&nVerts, sizeof (nVerts), 1, file);
        fwrite (&nTrias, sizeof (nTrias), 1, file);
    } else {
        tcl3dGLuintToByte (magic,  buf + 0);
        tcl3dGLushortToByte (nVerts, buf + 4);
        tcl3dGLushortToByte (nTrias, buf + 6);
        fwrite (buf, 4+2+2, 1, file);
    }
    
    /* spit out the vertices */
    for (i=1; i<=model->numvertices; i++) {
        coord[0] = model->vertices[3 * i + 0];
        coord[1] = model->vertices[3 * i + 1];
        coord[2] = model->vertices[3 * i + 2];
        if (haveIntel) {
            fwrite (coord, sizeof (coord), 1, file);
        } else {
            tcl3dGLfloatToByte (coord[0], buf + 0);
            tcl3dGLfloatToByte (coord[1], buf + 4);
            tcl3dGLfloatToByte (coord[2], buf + 8);
            fwrite (buf, 3*4, 1, file);
        }
    }
    
    /* Spit out the triangles */
    group = model->groups;
    while (group) {
        for (i=0; i<group->numtriangles; i++) {
            vtx[0] = T(group->triangles[i]).vindices[0] -1;
            vtx[1] = T(group->triangles[i]).vindices[1] -1;
            vtx[2] = T(group->triangles[i]).vindices[2] -1;
            if (haveIntel) {
                fwrite (vtx, sizeof (vtx), 1, file);
            } else {
                tcl3dGLushortToByte (vtx[0], buf + 0);
                tcl3dGLushortToByte (vtx[1], buf + 2);
                tcl3dGLushortToByte (vtx[2], buf + 4);
                fwrite (buf, 3*2, 1, file);
            }
            mat[0] = 255;
            mat[1] = 255;
            fwrite (mat, 2*1, 1, file);
        }
        group = group->next;
    }
    
    magic = MAGIC_NUMBER_END;
    fwrite (&magic,  sizeof (magic), 1, file);

    fclose(file);
}
