/*
 * API for loading and rendering 3D Studio r4 meshes
 */

#ifndef __OGL2_3DS_API__
#define __OGL2_3DS_API__

#include "objgl2.h"

#define BUFFER_SIZE_3DS 1024*1024

extern "C++" {

class Texture3DS : public Texture {
public:
  static bool voodoo;
  static bool lowtextures;
  void GL ();
};

class Textures3DS : public EntityList<Texture3DS> {
public:
  void GL ();
  Texture3DS* GetOrCreate (const char* s);
};

class Material3DS : public Material
{
  // Color parameters
  GLfloat ambient[4],
          diffuse[4],
          specular[4];

  // Additional parameters
  GLint shininess_percent,
        shininess_strenght_percent,
        transparency_percent,
        transparency_falloff_percent,
        self_illumination_percent,
        reflect_blur_percent;

  // Texture maps
  Texture3DS *texture1,
             *texture2,
             *opacity_map,
             *bump_map,
             *specular_map,
             *shininess_map,
             *self_illum_map,
             *reflection_map;

public:  
  // Constructor
  Material3DS ();
  void Reset ();
  void GL (GLenum face = GL_FRONT_AND_BACK);

  void Ambient (GLfloat r, GLfloat g, GLfloat b),
       Ambient (GLfloat* v),
       Ambient (GLubyte r, GLubyte g, GLubyte b),
       Ambient (GLubyte* v);
  void Diffuse (GLfloat r, GLfloat g, GLfloat b),
       Diffuse (GLfloat* v),
       Diffuse (GLubyte r, GLubyte g, GLubyte b),
       Diffuse (GLubyte* v);
  void Specular (GLfloat r, GLfloat g, GLfloat b),
       Specular (GLfloat* v),
       Specular (GLubyte r, GLubyte g, GLubyte b),
       Specular (GLubyte* v);
  void Shininess (GLint i),
       ShininessStrenght (GLint i),
       Transparency (GLint i),
       TransparencyFalloff (GLint i),
       SelfIllumination (GLint i),
       ReflectBlur (GLint i);
  void Texture1 (Texture3DS* t),
       Texture2 (Texture3DS* t),
       OpacityMap (Texture3DS* t),
       BumpMap (Texture3DS* t),
       SpecularMap (Texture3DS* t),
       ShininessMap (Texture3DS* t),
       SelfIlluminationMap (Texture3DS* t),
       ReflectionMap (Texture3DS* t);

  // Feedback
  const GLfloat* Ambient ();
  const GLfloat* Diffuse ();
  const GLfloat* Specular ();

  GLint Shininess (),
        ShininessStrenght (),
        Transparency (),
        TransparencyFalloff (),
        SelfIllumination (),
        ReflectBlur ();

  Texture3DS* Texture1 ();
  Texture3DS* Texture2 ();
  Texture3DS* OpacityMap ();
  Texture3DS* BumpMap ();
  Texture3DS* SpecularMap ();
  Texture3DS* ShininessMap ();
  Texture3DS* SelfIlluminationMap ();
  Texture3DS* ReflectionMap ();
};

template class EntityList<Material3DS>;
typedef EntityList<Material3DS> Materials3DS;

class OmniLight3DS : public Light
{
  DynamicSwitch<Vector3f, GLfloat> diffuse;
  DynamicSwitch<Vector3f, GLfloat> position;

  GLfloat constant_attenuation;
  GLfloat linear_attenuation;
  GLfloat quadratic_attenuation;

public:  
  OmniLight3DS ();

  void Reset ();

  void GL (GLenum light, double time = 0.0);

  void Diffuse (GLfloat r, GLfloat g, GLfloat b),
       Diffuse (GLubyte r, GLubyte g, GLubyte b),
       Diffuse (const Vector3f& V),
       Diffuse (Dynamic<Vector3f, GLfloat> *ptr, bool strong = false);
  void Position (GLfloat x, GLfloat y, GLfloat z),
       Position (const Vector3f& V),
       Position (Dynamic<Vector3f, GLfloat> *ptr, bool strong = false);
  void ConstantAttenuation (GLfloat f),
       LinearAttenuation (GLfloat f),
       QuadraticAttenuation (GLfloat f);

  // Feedback

  GLfloat ConstantAttenuation (),
       LinearAttenuation (),
       QuadraticAttenuation ();

  Vector3f Position (double time = 0.0),
           Diffuse (double time = 0.0);

};

struct FaceGroup3DS {
  Material3DS* material;
  int numfaces;
  GLuint* indices;

  FaceGroup3DS () :
    material(0), numfaces(0), indices(0) {}
  FaceGroup3DS (Material3DS* mat, int n, GLuint* ind) :
    material(mat), numfaces(n), indices(ind) {}
};

template class List<FaceGroup3DS>;
typedef List<FaceGroup3DS> FaceGroups3DS;

typedef InterleavedArrayElement_T4F_C4F_N3F_V4F Vertex3DS;

struct Edge3DS {
  GLuint a, b;
  GLfloat N1_x, N1_y, N1_z;
  GLfloat N2_x, N2_y, N2_z;
};

class Object3DS : public RenderableEntity {
  int numvertices;
  Vertex3DS* vertices;
  int numfaces;
  Vector3f* facenormals;
  int numevertices;
  Vertex3DS* evertices;
  int numedges;
  Edge3DS* edges;
  GLuint* edgeindices;

  BoundingBox bbox;

  FaceGroups3DS facegroups;

  DynamicSwitch<GLmatrix, GLfloat> lcs;

public:
  Object3DS () :
    numvertices(0), vertices(0),
    numfaces(0), facenormals(0),
    numevertices(0), evertices(0),
    numedges(0), edges(0), edgeindices(0) {
      GLmatrix M;
      M.LoadIdentity();
      lcs(M);
    }
  ~Object3DS ();

  void Render (double time = 0.0);
  void Rays (Vector3f C, Vector3f S, GLfloat I, GLenum sfactor, GLenum dfactor);
  void Outline (Vector3f C, GLfloat W, GLfloat r, GLfloat g, GLfloat b);
  void Object3DS::Inline ( Vector3f C, GLfloat W, GLfloat r, GLfloat g, GLfloat b);
  void ShortLine ( Vector3f C, GLfloat W, GLfloat r, GLfloat g, GLfloat b);
  void ShortLine ( Vector3f ang, Vector3f pos, GLfloat scale, GLfloat w, GLfloat r, GLfloat g, GLfloat b);
  void LongLine ( Vector3f ang, Vector3f pos, GLfloat scale, GLfloat w, GLfloat r, GLfloat g, GLfloat b);
  void ShadowVolume (Vector3f S, Vector3f C, GLfloat I, int j);

  void Vertices (int n, Vertex3DS* p);
  void AddFaceGroup (Material3DS* mat, int n, GLuint* ind);

  void EdgeVertices (int n, Vertex3DS* p);
  void Edges (int n, Edge3DS* p);
  void Facenormals (int n, Vector3f* p);

  void CoordinateSystem (const GLmatrix& V),
       CoordinateSystem (Dynamic<GLmatrix, GLfloat> *ptr, bool strong = false);

  Vertex3DS* Vertices () {
    return vertices;
  }
  int NumVertices () {
    return numvertices;
  }
};

template class EntityList<CameraTarget3DS>;
typedef EntityList<CameraTarget3DS> Cameras3DS;

class Scene3DS : public RenderableEntity {
  Lights lights;
  Textures3DS textures;
  Materials3DS materials;
  Cameras3DS cameras;
  RenderableEntities objects;

  GLfloat ambient[4];
  int allframes;
public:
  Scene3DS ();
  ~Scene3DS ();

  void Render (double time = 0.0);
  void TexturesGL () { textures.GL(); }

  void Frames (int n) { allframes = n; }
  int Frames () { return allframes; }

  void Add (Light* p);
  void Add (Texture3DS* p);
  void Add (Material3DS* p);
  void Add (CameraTarget3DS* p);
  void Add (RenderableEntity* p);
  void Add (const RenderableEntities & os);

  Light* GetLight (String s);
  Texture3DS* GetTexture (String s);
  Material3DS* GetMaterial (String s);
  CameraTarget3DS* GetCamera (String s);
  RenderableEntity* GetObject (String s);

  Lights GetLights ();
  Textures3DS GetTextures ();
  Materials3DS GetMaterials ();
  Cameras3DS GetCameras ();
  RenderableEntities GetObjects ();

  void Ambient (GLfloat r, GLfloat g, GLfloat b, GLfloat a=1.0);
};

struct chunk3ds {
  unsigned short type;
  char *ptr;
};

class Loader3DS
{
  enum {
    RGBfloat = 0x0010,
    RGBbyte = 0x0011,
    PERCENTword = 0x0030,
    PERCENTfloat = 0x0031,
   
       MAIN_3DS_CHUNK = 0x4D4D,

         EDITOR_CHUNK = 0x3D3D,
  
           AMBIENT_COLOR = 0x2100,
  
           OBJECT_BLOCK = 0x4000,

             TRIANGULAR_MESH = 0x4100,
               VERTICES = 0x4110,
               FACES = 0x4120,
                 FACES_MATERIALS = 0x4130,
                 SMOOTHINGS = 0x4150,
               MAPPING_COORDS = 0x4140,
               LOCAL_COORD_SYS = 0x4160,

             LIGHT = 0x4600,
               LIGHT_OFF = 0x4620,
  
             CAMERA = 0x4700,
  
           MATERIAL_BLOCK = 0xAFFF,
  
             MATERIAL_NAME = 0xA000,
             MATERIAL_AMBIENT_COLOR = 0xA010,
             MATERIAL_DIFFUSE_COLOR = 0xA020,
             MATERIAL_SPECULAR_COLOR = 0xA030,
             SHININESS_PERCENT = 0xA040,
             SHININESS_STRENGHT_PERCENT = 0xA041,
             TRANSPARENCY_PERCENT = 0xA050,
             TRANSPARENCY_FALLOFF_PERCENT = 0xA052,
             REFLECT_BLUR_PERCENT = 0xA053,
             SELF_ILLUMINATION = 0xA084,
             MAP1 = 0xA200,
             MAP2 = 0xA33A,
             OPACITY_MAP = 0xA210,
             BUMP_MAP = 0xA230,
             SPECULAR_MAP = 0xA204,
             SHININESS_MAP = 0xA33C,
             SELF_ILLUM_MAP = 0xA33D,
             REFLECTION_MAP = 0xA220,
               MAPPING_FILENAME = 0xA300,
               V_SCALE = 0xA354,
               U_SCALE = 0xA356,
               U_OFFSET = 0xA358,
               V_OFFSET = 0xA35A,

         KEYFRAMER_CHUNK = 0xB000,

           AMBIENT_LIGHT_INFORMATION_BLOCK = 0xB001,
           MESH_INFORMATION_BLOCK = 0xB002,
           CAMERA_INFORMATION_BLOCK = 0xB003,
           CAMERA_TARGET_INFORMATION_BLOCK = 0xB004,
           OMNI_LIGHT_INFORMATION_BLOCK = 0xB005,
           SPOT_LIGHT_TARGET_INFORMATION_BLOCK = 0xB006,
           SPOT_LIGHT_INFORMATION_BLOCK = 0xB007,
           FRAMES = 0xB008,

             OBJECT_INFO = 0xB010,
             OBJECT_PIVOT_POINT = 0xB013,
             POSITION_TRACK = 0xB020,
             ROTATION_TRACK = 0xB021,
             SCALE_TRACK = 0xB022,
             FOV_TRACK = 0xB023,
             ROLL_TRACK = 0xB024,
             COLOR_TRACK = 0xB025,
             MORPH_TRACK = 0xB026,
             HOTSPOT_TRACK = 0xB027,
             FALLOFF_TRACK = 0xB028,
             HIDE_TRACK = 0xB029,
             HIERARCHY_POSITION = 0xB030
  };

  struct Vector3DS {
    float x,y,z;
    };

  struct Face3DS {
    unsigned short a,b,c,flags;
    };

  struct Mapping3DS {
    float u,v;
    };

  struct iface {
    GLuint a, b, c;
    Vector3f N;
    unsigned long sgroup;
    bool hasmaterial;
    bool ab_done, ac_done, bc_done;
  };
  
  struct ftable {
    GLuint fi;
    GLuint group;
  };

  struct shinfo {
    int numfaces;
    int used;
    int splits;
    ftable* p;
    Vertex3DS* v;
    GLuint iv;
  };

  struct TCBEE
  {
    float tension;
    float continuity;
    float bias;
    float easeto;
    float easefrom;
    TCBEE() :
      tension(0.0F), continuity(0.0F), bias(0.0F),
      easeto(0.0F), easefrom(0.0F) {}
  };

  struct position_key : public TCBEE {
    int frame;
    Vector3f p;
    position_key() :
      frame(0) {}
  };

  struct position_track
  {
    int count;
    position_key *keys;
    unsigned int mode;
  
    position_track (int n, int m) : count(n), mode(m) {
      keys = new position_key[n];
    }
    position_key& operator [] (int i) {
      return keys[i];
    }
  
    Vector3f getv (int sel, int n);
  
    ~position_track () {
      delete[] keys;
    }
  };

  struct roll_key : public TCBEE
  {
    int frame;
    GLfloat p;
    roll_key() :
      frame(0) {}
  };

  struct roll_track
  {
    int count;
    roll_key *keys;
    unsigned int mode;

    roll_track(int n, int m) : count(n), mode(m) {
      keys = new roll_key[n];
    }
    roll_key& operator [] (int i) { return keys[i]; }

    GLfloat getv(int sel, int n);

    ~roll_track() {
      delete[] keys;
    }
  };

/*
  struct rotation_key : public TCBEE
  {
    int frame;
    float_quaternion q;
    rotation_key() : frame(0) {}
  };

  struct rotation_track
  {
    int count;
    rotation_key *keys;
    unsigned int mode;

    rotation_track(int n, int m) : count(n), mode(m) {
      keys = new rotation_key[n];
    }
    rotation_key& operator [] (int i) { return keys[i]; }

    float_quaternion getq(int sel, int n);

    ~rotation_track() {
      delete[] keys;
    }
  };
*/
  enum {an,bn,point};

  unsigned char getbyte(char *p) { return *(unsigned char *)p; }
  unsigned short getshort(char *p) { return *(unsigned short *)p; }
  unsigned long getlong(char *p) { return *(unsigned long *)p; }
  float getfloat(char *p) { return *(float *)p; }

  char* find_chunk(char *father, unsigned short, char *act);
  List<chunk3ds> subchunks(char *father, char *act);
  List<chunk3ds> subchunks_by_type(char *father, unsigned short chunk_type, char *act);

  void track_type_info(unsigned short flag);
  void tcb_info(unsigned short flag);
  char* fill_tcbee(unsigned short flag, char *p, TCBEE *tcb);
  Dynamic<Vector3f, GLfloat>* process_position_track(int allframes, int keys, char *track, unsigned short tracktype);
  Dynamic<GLfloat, GLfloat>* process_roll_track(int allframes, int keys, char *track, unsigned short tracktype);

public:
  static char *buffer;

  Loader3DS() {}
  Scene3DS* Load(const char *inputfile, Textures3DS* texturelib = 0);

};

} // extern "C++"

#endif
