#ifndef _CASI_H
  #define _CASI_H

#ifndef Uint32
  #define Uint32  unsigned int
#endif
#ifndef Uint8
  #define Uint8   unsigned char
#endif
#ifndef Scalar
  #define Scalar  float
#endif

#include <stdio.h>


class CASINodeTM {
public:
  char    *name;
  Scalar  pos[3];
  Scalar  rotAxis[3];
  Scalar  rotAngle;

  CASINodeTM(){ name = NULL; };
  ~CASINodeTM(){ if(name) free(name); };
  void printStats(){
    printf("\nStatistics for CASINodeTM\n");
    printf(" o Pos:  (%.4f, %.4f, %.4f)\n", pos[0], pos[1], pos[2]);
  };
};

class CASIMap {
public:
  char    *mapName;
  char    *mapClass;
  char    *mapType;
  char    *imageFilename;
  Scalar  uvwUOffset;
  Scalar  uvwVOffset;
  Scalar  uvwUTiling;
  Scalar  uvwVTiling;
  Scalar  uvwAngle;
  char    *imageFilter;

  CASIMap(){
    mapName       = NULL;
    mapClass      = NULL;
    mapType       = NULL;
    imageFilename = NULL;
  };
  ~CASIMap(){
    if(mapName)       free(mapName);
    if(mapClass)      free(mapClass);
    if(mapType)       free(mapType);
    if(imageFilename) free(imageFilename);
    if(imageFilter)   free(imageFilter);
  };
  void printStats()
  {
    printf("   - Map name:  %s\n", mapName);
    printf("   - Map class: %s\n", mapClass);
    printf("   - Map type:  %s\n", mapType);
    printf("   - Map image filename: %s\n", imageFilename);

  };
};

class CASIMaterial {
public:
  char    *materialName;
  char    *materialClass;
  CASIMap *mapList;
  Uint32  mapCount;

  Scalar  ambient[3];
  Scalar  diffuse[3];
  Scalar  specular[3];
  //Scalar  emission[3];
  Scalar  shine;
  Scalar  shineStrength;
  Scalar  alpha;
  bool    useAlpha;

  CASIMaterial() {
    materialName  = NULL;
    materialClass = NULL;
    mapList       = NULL;
    mapCount      = 0;
  };
  ~CASIMaterial(){
    if(materialName)  free(materialName);
    if(materialClass) free(materialClass);
    delete[] mapList;
  };
  void printStats()
  {
    printf("\nStatistics for CASIMaterial\n");
    printf(" o Material name: %s\n", materialName);
    printf(" o Material class: %s\n", materialClass);
    printf(" o Material properties\n");
    printf("   - Ambient:  (%.4f, %.4f, %.4f)\n", ambient[0], ambient[1], ambient[2]);
    printf("   - Diffuse:  (%.4f, %.4f, %.4f)\n", diffuse[0], diffuse[1], diffuse[2]);
    printf("   - Specular: (%.4f, %.4f, %.4f)\n", specular[0], specular[1], specular[2]);
    printf("   - Shine: %.4f (with strength %.4f)\n", shine, shineStrength);
    printf("   - Alpha: %.4f\n", alpha);
    printf(" o Maps\n");
    for( Uint32 n=0; n<mapCount; n++ )
      mapList[n].printStats();
  };
};

class CASIGeometric {
public:
  char    *name;
  Uint32  materialReference;

  Uint32  numVertices;
  Uint32  numFaces;
  Uint32  numTexVertices;
  Uint32  numTexFaces;
  Uint32  numFaceNormals;
  Uint32  numVertexNormals;     // = 3 * numfacenormals

  Scalar  *vertices;
  Uint32  *faces;
  Scalar  *texVertices;
  Uint32  *texFaces;
  Scalar  *faceNormals;
  Scalar  *vertexNormals;

  bool    isLoaded;

  CASIGeometric(){
    name          = NULL;
    vertices      = NULL;
    faces         = NULL;
    texVertices   = NULL;
    texFaces      = NULL;
    faceNormals   = NULL;
    vertexNormals = NULL;
  };
  ~CASIGeometric(){
    if( isLoaded )
    {
      free(name);
      delete[] vertices;
      delete[] faces;
      delete[] texVertices;
      delete[] texFaces;
      delete[] faceNormals;
      delete[] vertexNormals;
    }
  };
  void printStats(){
    printf("\nStatistics for CASIGeometric\n");
    printf(" o Mesh name: %s\n", name);
    printf(" o Mesh data statistics:\n");
    printf("   - Material reference: %d\n", materialReference);
    printf("   - numVertices:        %d\n", numVertices);
    printf("   - numFaces:           %d\n", numFaces);
    printf("   - numTexVertices:     %d\n", numTexVertices);
    printf("   - numTexFaces:        %d\n", numTexFaces);
    printf("   - numFaceNormals:     %d\n", numFaceNormals);
    printf("   - numVertexNormals:   %d\n", numVertexNormals);

  };
};

class CASILight {
public:
  char        *name;
  char        *lightType;
  CASINodeTM  node;
  Scalar      color[3];
  Scalar      intensity;

  CASILight(){
    name      = NULL;
    lightType = NULL;
  };
  ~CASILight(){
    if(name)      free(name);
    if(lightType) free(lightType);
  };
  void printStats(){
    printf("\nStatistics for CASILight\n");
    printf(" o Light name: %s\n", name);
    printf(" o Light type: %s\n", lightType);
    printf(" o Color: (%.4f, %.4f, %.4f)\n", color[0], color[1], color[2]);
    printf(" o Intensity: %.4f\n", intensity);
    node.printStats();
  };
};

class CASI {
private:
  int  jumpTo( char *key );
  void readS( char **container );
  void readQS( char **container );
  void readI( Uint32 *container );
  void readF( float *container );
  void read3I( Uint32 *container );
  void read3F( float *container );
  void readMESH(CASIGeometric *geom);
  void readNODE(CASINodeTM *node);

  void readVersion( );
  void readScene( );
  void readMaterialList( );
  void readGeometricList( );
  void readLightList( );

public:
  // Version
  Uint32      version;
  char        *comment;

  // Scene
  char    *sceneFilename;
  char    *sceneSourceFilename;
  Scalar  sceneStatic[3];
  Scalar  sceneAmbient[3];

  // Material list
  Uint32        materialCount;
  CASIMaterial  *materialList;
  
  // Geometric object list
  Uint32        geometricCount;
  CASIGeometric **geometricList;
  Uint32        vertexCount;
  Uint32        faceCount;
  Uint32        texVertexCount;
  Uint32        texFaceCount;

  // Light list
  Uint32    lightCount;
  CASILight *lightList;

  //Lightmap
  Scalar  *lmTexVertices;
  Uint32  *lmTexFaces;
  Uint32  numLMTexVertices;
  Uint32  numLMTexFaces;

  bool isLoaded;
  bool silentMode;
  FILE *in;
  long lastFilePos;

  CASI();
  ~CASI();
  void import( char *fname );
  void readLightmapUVWs( char *fname );
  void flip3DSCoords();
  void calcNormals();
  void printStats();
  void testDraw();
};

#endif
