#ifndef __XENTAX_LWO_H
#define __XENTAX_LWO_H

// CLIP chunk (list of images)
// TAGS chunk (list of surface names + skeleton surface name + bone part names)
// for each layer
//     LAYR chunk
//     PNTS chunk (list of points)
//     VMAP chunk #1 (UV #1)
//     VMAP chunk #2 (UV #2)
//     VMAP chunk #3 (Weight)
//     VMAP chunk #4 (Weight)
//     VMAP chunk #5 (Morphological)
//     VMAP chunk #6 (Morphological)
//     VMAP chunk #7 (Color)
//     VMAP chunk #8 (Color)
//     VMAP chunk #N
//     POLS chunk (list of polygons)
//     PTAG chunk (list of surface-polygon associations)
//     for each skeleton
//         PNTS chunk (skeleton list of points)
//         POLS chunk (skeleton list of polygons)
//         PTAG chunk (skeleton list of bone-surface associations)
//         PTAG chunk (skeleton list of bone-name associations)
//         PTAG chunk (skeleton list of bone-part associations)
//         PTAG chunk (skeleton list of bone-uphandle associations)
//         PTAG chunk (skeleton list of bone-weightmap associations)
// for each surface
//     SURF chunk

//
// CHUNKTYPES
//

inline uint32 LWO_TAG(uint32 a, uint32 b, uint32 c, uint32 d)
{
 return ((a << 24) | (b << 16) | (c << 8) | (d));
}

#define LWO_FORM LWO_TAG('F', 'O', 'R', 'M')
#define LWO_LWO2 LWO_TAG('L', 'W', 'O', '2')
#define LWO_TAGS LWO_TAG('T', 'A', 'G', 'S')
#define LWO_LAYR LWO_TAG('L', 'A', 'Y', 'R')
#define LWO_PNTS LWO_TAG('P', 'N', 'T', 'S')
#define LWO_VMAP LWO_TAG('V', 'M', 'A', 'P')
#define LWO_WGHT LWO_TAG('W', 'G', 'H', 'T')
#define LWO_TXUV LWO_TAG('T', 'X', 'U', 'V')
#define LWO_MORF LWO_TAG('M', 'O', 'R', 'F')
#define LWO_POLS LWO_TAG('P', 'O', 'L', 'S')
#define LWO_FACE LWO_TAG('F', 'A', 'C', 'E')
#define LWO_BONE LWO_TAG('B', 'O', 'N', 'E')
#define LWO_PART LWO_TAG('P', 'A', 'R', 'T')
#define LWO_ENVL LWO_TAG('E', 'N', 'V', 'L')
#define LWO_CLIP LWO_TAG('C', 'L', 'I', 'P')
#define LWO_SURF LWO_TAG('S', 'U', 'R', 'F')
#define LWO_PTAG LWO_TAG('P', 'T', 'A', 'G')
#define LWO_COLR LWO_TAG('C', 'O', 'L', 'R')
#define LWO_DIFF LWO_TAG('D', 'I', 'F', 'F')
#define LWO_SPEC LWO_TAG('S', 'P', 'E', 'C')
#define LWO_TRAN LWO_TAG('T', 'R', 'A', 'N')
#define LWO_CLIP LWO_TAG('C', 'L', 'I', 'P')
#define LWO_STIL LWO_TAG('S', 'T', 'I', 'L')
#define LWO_FLAG LWO_TAG('F', 'L', 'A', 'G')
#define LWO_BLOK LWO_TAG('B', 'L', 'O', 'K')
#define LWO_IMAP LWO_TAG('I', 'M', 'A', 'P')
#define LWO_TMAP LWO_TAG('T', 'M', 'A', 'P')
#define LWO_CHAN LWO_TAG('C', 'H', 'A', 'N')
#define LWO_OPAC LWO_TAG('O', 'P', 'A', 'C')
#define LWO_ENAB LWO_TAG('E', 'N', 'A', 'B')
#define LWO_NEGA LWO_TAG('N', 'E', 'G', 'A')
#define LWO_CNTR LWO_TAG('C', 'N', 'T', 'R')
#define LWO_SIZE LWO_TAG('S', 'I', 'Z', 'E')
#define LWO_ROTA LWO_TAG('R', 'O', 'T', 'A')
#define LWO_FALL LWO_TAG('F', 'A', 'L', 'L')
#define LWO_OMAP LWO_TAG('O', 'M', 'A', 'P')
#define LWO_CSYS LWO_TAG('C', 'S', 'Y', 'S')
#define LWO_PROJ LWO_TAG('P', 'R', 'O', 'J')
#define LWO_AXIS LWO_TAG('A', 'X', 'I', 'S')
#define LWO_IMAG LWO_TAG('I', 'M', 'A', 'G')
#define LWO_WRAP LWO_TAG('W', 'R', 'A', 'P')
#define LWO_WRPW LWO_TAG('W', 'R', 'P', 'W')
#define LWO_WRPH LWO_TAG('W', 'R', 'P', 'H')
#define LWO_VMAP LWO_TAG('V', 'M', 'A', 'P')
#define LWO_AAST LWO_TAG('A', 'A', 'S', 'T')
#define LWO_PIXB LWO_TAG('P', 'I', 'X', 'B')

//
// LAYER MACROS
//

#define LWO_MAX_LAYERS 128

//
// SURFACE MACROS
//

#define LWO_SURF_FLAGS_COLR      0x0001
#define LWO_SURF_FLAGS_DIFF      0x0002
#define LWO_SURF_FLAGS_SPEC      0x0004
#define LWO_SURF_FLAGS_TRAN      0x0008
#define LWO_SURF_FLAGS_COLR_BLOK 0x0010
#define LWO_SURF_FLAGS_DIFF_BLOK 0x0020
#define LWO_SURF_FLAGS_SPEC_BLOK 0x0040
#define LWO_SURF_FLAGS_TRAN_BLOK 0x0080

//
// GENERAL STRUCTURES
//

struct lwVertex1D { real32 x; };
struct lwVertex2D { real32 x; real32 y; };
struct lwVertex3D { real32 x; real32 y; real32 z; };
struct lwTriangle { uint32 a; uint32 b; uint32 c; };
struct lwSkelegon { uint32 a; uint32 b; std::string name; };

struct lwRefVertex1D { uint32 reference; real32 x; };
struct lwRefVertex2D { uint32 reference; real32 x; real32 y; };
struct lwRefVertex3D { uint32 reference; real32 x; real32 y; real32 z; };

//
// CHUNK STRUCTURES (TAGS)
//

struct lwTAGS {
 uint32 chunksize;
 uint32 currindex;
 boost::bimap<uint16, std::string> surftags;
 boost::bimap<uint16, std::string> bonetags;
 boost::bimap<uint16, std::string> parttags;
 boost::bimap<uint16, std::string> bnuptags;
};

struct lwPTAG {
 uint32 chunksize;
 uint32 type;
 std::map<uint32, uint16> tags;
};

//
// CHUNK STRUCTURES (CLIPS)
//

struct lwCLIP {
 uint32 chunksize;
 std::string filename;
};

//
// CHUNK STRUCTURES (VERTEX MAPS)
//

struct lwTXUV {
 uint32 chunksize;
 boost::shared_array<lwRefVertex2D> data;
 uint32 elem;
};

struct lwWGHT {
 uint32 chunksize;
 boost::shared_array<lwRefVertex1D> data;
 uint32 elem;
};

struct lwMORF {
 uint32 chunksize;
 boost::shared_array<lwRefVertex3D> data;
 uint32 elem;
};

//
// CHUNK STRUCTURES (POLYGON TYPES)
//

struct lwPOLS {
 uint32 chunksize;
 boost::shared_array<lwTriangle> data;
 uint32 elem;
 std::string name; // TODO: remove
 lwPTAG surf;
};

// lwSKEL
struct lwSKEL {
 uint32 chunksize;
 boost::shared_array<lwSkelegon> data;
 uint32 elem;
 std::string name;
 lwPTAG stag;
 lwPTAG btag;
 lwPTAG ptag;
 lwPTAG utag;
};

//
// CHUNK STRUCTURES (POINT TYPES)
//

struct lwPNTS {
 uint32 chunksize;
 boost::shared_array<lwVertex3D> data;
 uint32 elem;
 std::map<std::string, lwTXUV> txuv;
 std::map<std::string, lwWGHT> wght;
 std::map<std::string, lwMORF> morf;
 std::deque<lwPOLS> face;
};

struct lwJNTS {
 uint32 chunksize;
 boost::shared_array<lwVertex3D> data;
 uint32 elem;
 std::deque<lwSKEL> skel;
};

//
// CHUNK STRUCTURES (LAYERS)
//

struct lwLAYR {
 uint32 chunksize;
 uint16 flags;
 real32 pivot_x;
 real32 pivot_y;
 real32 pivot_z;
 std::string name;
 std::map<uint16, lwPNTS> pntss;
 std::map<uint16, lwJNTS> jntss;
};

//
// CHUNK STRUCTURES (SURFACE SUBCHUNKS)
//

struct lwCHAN {
 uint16 chunksize;
 uint32 channel;
};

struct lwENAB {
 uint16 chunksize;
 uint16 enable;
};

struct lwOPAC {
 uint16 chunksize;
 uint16 type;
 real32 opacity;
};

struct lwAXIS {
 uint16 chunksize;
 uint16 axis;
};

struct lwNEGA {
 uint16 chunksize;
 uint16 enable;
};

struct lwCNTR {
 uint16 chunksize;
 real32 x;
 real32 y;
 real32 z;
};

struct lwSIZE {
 uint16 chunksize;
 real32 x;
 real32 y;
 real32 z;
};

struct lwROTA {
 uint16 chunksize;
 real32 x;
 real32 y;
 real32 z;
};

struct lwFALL {
 uint16 chunksize;
 uint16 type;
 real32 x;
 real32 y;
 real32 z;
};

struct lwCSYS {
 uint16 chunksize;
 uint16 type;
};

struct lwPROJ {
 uint16 chunksize;
 uint16 mode;
};

struct lwIMAG {
 uint16 chunksize;
 uint32 clipindex;
};

struct lwWRAP {
 uint16 chunksize;
 uint16 wrap_w;
 uint16 wrap_h;
};

struct lwVMAP {
 uint16 chunksize;
 std::string name;
};

struct lwAAST {
 uint16 chunksize;
 uint16 flags;
 real32 strength;
};

struct lwPIXB {
 uint16 chunksize;
 uint16 flags;
};

//
// CHUNK STRUCTURES (SURFACES)
//

struct lwIMAP {
 uint16 chunksize;
 lwCHAN chan;
 lwENAB enab;
 lwNEGA nega;
 lwOPAC opac;
 lwAXIS axis;
};

struct lwTMAP {
 uint16 chunksize;
 lwCNTR cntr;
 lwSIZE size;
 lwROTA rota;
 lwFALL fall;
 lwCSYS csys;
};

struct lwBLOK {
 uint16 chunksize;
 lwIMAP imap;
 lwTMAP tmap;
 lwPROJ proj;
 lwAXIS axis;
 lwIMAG imag;
 lwWRAP wrap;
 lwVMAP vmap;
 lwAAST aast;
 lwPIXB pixb;
};

struct lwSURF {
 uint32 chunksize;
 uint32 flags;
 real32 colr[3];
 real32 diff;
 real32 spec;
 real32 tran;
 lwBLOK colr_map;
 lwBLOK diff_map;
 lwBLOK spec_map;
 lwBLOK tran_map;
};

//
// LWO2 CLASS
//

class lwFileSaver {
 private :
  lwTAGS tags;
  std::map<uint32, lwCLIP> clips;
  std::map<uint16, lwLAYR> layrs;
  std::map<std::string, lwSURF> surfs;
 private :
  bool hasTxuv(const char* name)const;
 public :
  bool insertClip(uint32 clipindex, const char* filename);
  bool insertLayr(uint16 layer, const char* name);
  bool insertPnts(uint16 layer, uint16 pointset, const boost::shared_array<lwVertex3D>& data, uint32 elem);
  bool insertTxuv(uint16 layer, uint16 pointset, const char* name, const boost::shared_array<lwRefVertex2D>& data, uint32 elem);
  bool insertWght(uint16 layer, uint16 pointset, const char* name, const boost::shared_array<lwRefVertex1D>& data, uint32 elem);
  bool insertMorf(uint16 layer, uint16 pointset, const char* name, const boost::shared_array<lwRefVertex3D>& data, uint32 elem);
  bool insertPols(uint16 layer, uint16 pointset, const char* name, const boost::shared_array<lwTriangle>& data, uint32 elem);
  bool insertJnts(uint16 layer, uint16 jointset, const boost::shared_array<lwVertex3D>& data, uint32 elem);
  bool insertSkel(uint16 layer, uint16 jointset, const char* name, const boost::shared_array<lwSkelegon>& data, uint32 elem);
 public :
  bool insertBone(const char* name);
  bool insertPart(const char* name);
  bool insertSurf(const char* name);
  bool setSurfColr(const char* name, real32 r, real32 g, real32 b);
  bool setSurfDiff(const char* name, real32 intensity);
  bool setSurfSpec(const char* name, real32 intensity);
  bool setSurfTran(const char* name, real32 intensity);
 public :
  bool enableSurfColrImag(const char* name);
  bool enableSurfDiffImag(const char* name);
  bool enableSurfSpecImag(const char* name);
  bool enableSurfTranImag(const char* name);
 public :
  bool setSurfVmap(const char* surface, const char* mapname, uint32 type);
  bool setSurfColrVmap(const char* surface, const char* mapname);
  bool setSurfDiffVmap(const char* surface, const char* mapname);
  bool setSurfSpecVmap(const char* surface, const char* mapname);
  bool setSurfTranVmap(const char* surface, const char* mapname);
 public :
  bool setSurfImag(const char* surface, uint32 clipindex, uint32 type);
  bool setSurfColrImag(const char* surface, uint32 clipindex);
  bool setSurfDiffImag(const char* surface, uint32 clipindex);
  bool setSurfSpecImag(const char* surface, uint32 clipindex);
  bool setSurfTranImag(const char* surface, uint32 clipindex);
 public :
  bool setSurfNega(const char* surface, uint16 state, uint32 type);
  bool setSurfColrNega(const char* surface, uint16 state);
  bool setSurfDiffNega(const char* surface, uint16 state);
  bool setSurfSpecNega(const char* surface, uint16 state);
  bool setSurfTranNega(const char* surface, uint16 state);
 public :
  bool save(const char* filename)const;
 public :
  lwFileSaver();
 ~lwFileSaver();
};

//
// LWO2 CLASS EXAMPLE
//

void lwFileSaverExample(void);

#endif
