/***************************************************************************
 *   Copyright (C) 2007 by A.J. Tavakoli                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef Q3MAP_H
#define Q3MAP_H

#include <cstddef>
#include <vector>
#include <set>
#include <map>
#include <string>

#include <SDL/SDL.h>

#include "common.h"
#include "Camera.h"
//#include "VertexContainer.h"
#include "Entity.h"


class Q3Map {
public:
  class Texture {
  public:
    char name[64];    // name of texture
    Sint32 flags;    // surface flags
    Sint32 contents; // content flags
  };

  class Plane {
  public:
    float normal[3]; // plane normal
    float d;         // plane distance
  };

  class Node {
  public:
    Sint32 plane;       // plane index
    Sint32 children[2]; // child nodes (negative for leaves)
    Sint32 min[3];      // AABB integer as integer
    Sint32 max[3];      // AABB integer as integer
  };

  class Leaf {
  public:
    Sint32 cluster; // visibility data cluster index
    Sint32 area;
    Sint32 min[3];
    Sint32 max[3];
    Sint32 leafFace;
    Sint32 numLeafFaces;
    Sint32 leafBrush;
    Sint32 numLeafBrushes;
  };

  class Model {
  public:
    float min[3]; // AABB min
    float max[3]; // AABB max;
    Sint32 face; // first face index
    Sint32 numFaces;
    Sint32 brush; // first brush index
    Sint32 numBrushes;
  };

  class Brush {
  public:
    Sint32 brushSide;
    Sint32 numBrushSides;
    Sint32 texture;
  };

  class BrushSide {
  public:
    Sint32 plane; // plane index
    Sint32 texture; // texture index
  };

  class Vertex {
  public:
    float pos[3];
    float surfaceTC[2];  // texture coords for surface
    float lightMapTC[2]; // texture coords for lightmap
    float normal[3];
    Uint8 color[4];
  };

  class Effect {
  public:
    char name[64];
    Sint32 brush;
    Sint32 unknown;
  };

  class Face {
  public:
    Sint32 texture;
    Sint32 effect;
    Sint32 type;
    Sint32 vertex;
    Sint32 numVertices;
    Sint32 meshVert;
    Sint32 numMeshVerts;
    Sint32 lightMapIndex;
    Sint32 lightMapStart[2];
    Sint32 lightMapSize[2];
    float lightMapOrigin[3];
    float lightMapVecs[2][3];
    float normal[3];
    Sint32 size[2];
  };

  class LightMap {
  public:
    Uint8 map[49152]; // 128*128*3 = 49152 (RGB lightmap)
  };

  class LightVol {
  public:
    Uint8 ambient[3];
    Uint8 directional[3];
    Uint8 dir[2]; // phi, theta    
  };

  
  Q3Map();
  ~Q3Map();

  // clears all map data
  void clear();

  // loads quake 3 map from disk
  bool load(const char *filename);

  // accessor methods
  inline const char*     getEnts()            const { return ents;           }
  inline const Texture&  getTexture(int i)    const { return textures[i];    }
  inline const Plane&    getPlane(int i)      const { return planes[i];      }
  inline const Node&     getNode(int i)       const { return nodes[i];       }
  inline const Leaf&     getLeaf(int i)       const { return leafs[i];       }
  inline Sint32          getLeafFace(int i)   const { return leafFaces[i];   }
  inline Sint32          getLeafBrush(int i)  const { return leafBrushes[i]; }
  inline Brush           getBrush(int i)      const { return brushes[i];     }
  inline BrushSide       getBrushSide(int i)  const { return brushSides[i];  }
  inline const Face&     getFace(int i)       const { return faces[i];       }
  inline const LightMap& getLightMap(int i)   const { return lightMaps[i];   }
  inline const Vertex&   getVertex(int i)     const { return vertices[i];    }
  inline Sint32          getMeshVert(int i)   const { return meshVerts[i];   }

  typedef std::map<std::string, std::string> EntAttrMap;
  typedef std::vector< std::map<std::string, std::string> > EntMaps;
  bool parseEnts(EntMaps &entMaps) const;

  inline int getNumTextures()   const { return numTextures;   }
  inline int getNumPlanes()     const { return numPlanes;     }
  inline int getNumNodes()      const { return numNodes;      }
  inline int getNumLeafs()      const { return numLeafs;      }
  inline int getNumLeafFaces()  const { return numLeafFaces;  }
  inline int getNumBrushes()    const { return numBrushes;    }
  inline int getNumBrushSides() const { return numBrushSides; }
  inline int getNumFaces()      const { return numFaces;      }
  inline int getNumLightMaps()  const { return numLightMaps;  }
  inline int getNumVertices()   const { return numVertices;   }
  inline int getNumMeshVerts()  const { return numMeshVerts;  }

  // visibility information (PVS)
  inline int getSizeVisVecs() const { return int(visVecSize); }
  inline int getNumVisVecs()  const { return int(numVisVecs); }
  inline const Uint8 *getVisVecs() const { return visVecs; }

private:
  void loadTextures();
  inline bool potentiallyVisible(int pvsIndex1, int pvsIndex2) const;
  void checkBrushesAABB(const AABB &aabbStart, const AABB &aabbEnd,
                        float &hitFraction, Vector3 &hitNormal,
                        const Leaf &leaf) const;

  static bool isBigEndian();
  static bool bigEndian;

  inline void fixEndian(void *data, size_t sz);

  // internal for entity parser
  enum ENT_TOKEN {
    ENT_TOKEN_INVALID,
    ENT_TOKEN_LBRACE,
    ENT_TOKEN_RBRACE,
    ENT_TOKEN_STRING,
    ENT_TOKEN_END
  };
  ENT_TOKEN getEntToken(size_t &pos,
                        size_t len,
                        std::string *strVal=NULL) const;

  // lump offsets
  enum {
    LUMP_ENTITIES_INDEX = 0,
    LUMP_TEXTURES_INDEX,
    LUMP_PLANES_INDEX,
    LUMP_NODES_INDEX,
    LUMP_LEAFS_INDEX,
    LUMP_LEAFFACES_INDEX,
    LUMP_LEAFBRUSHES_INDEX,
    LUMP_MODELS_INDEX,
    LUMP_BRUSHES_INDEX,
    LUMP_BRUSHSIDES_INDEX,
    LUMP_VERTICES_INDEX,
    LUMP_MESHVERTS_INDEX,
    LUMP_EFFECTS_INDEX,
    LUMP_FACES_INDEX,
    LUMP_LIGHTMAPS_INDEX,
    LUMP_LIGHTVOLS_INDEX,
    LUMP_VISDATA_INDEX,
    NUM_LUMP_ENTRIES
  };

  class DirEntry {
  public:
    Sint32 offset; // offset to begininning of lump (from start of file)
    Sint32 length; // length of lump (always multiple of 4)
  };

  class MapHeader {
  public:
    Sint32 magic;   // magic #, always "IBSP"
    Sint32 version; // version #, 0x2e for Quake 3 maps
    DirEntry direntries[NUM_LUMP_ENTRIES]; // lump directory
  };

  bool readLumps(Sint8 *lumps[], const MapHeader &header);

  Sint8 *lumps[NUM_LUMP_ENTRIES];

  // entity descrption string
  char *ents;

  // textures
  int numTextures;
  Texture *textures;

  // planes
  int numPlanes;
  Plane *planes;

  // nodes
  int numNodes;
  Node *nodes;

  // leafs
  int numLeafs;
  Leaf *leafs;

  // leaf faces
  int numLeafFaces;
  Sint32 *leafFaces;

  // leaf brushes
  int numLeafBrushes;
  Sint32 *leafBrushes;

  // models
  int numModels;
  Model *models;

  // brushes
  int numBrushes;
  Brush *brushes;

  // brush sides
  int numBrushSides;
  BrushSide *brushSides;

  // vertices
  int numVertices;
  Vertex *vertices;

  // mesh vertices
  int numMeshVerts;
  Sint32 *meshVerts;

  // effects
  int numEffects;
  Effect *effects;

  // faces
  int numFaces;
  Face *faces;

  // light maps
  int numLightMaps;
  LightMap *lightMaps;

  // light volumes
  int numLightVols;
  LightVol *lightVols;

  // visibility data  
  Sint32 numVisVecs;
  Sint32 visVecSize;
  Uint8 *visVecs;
};


// determines if one leaf is potentially visible from another
bool Q3Map::potentiallyVisible(int pvsIndex1, int pvsIndex2) const {
  if ( NULL == visVecs || pvsIndex1 == pvsIndex2 ||
       pvsIndex1 < 0   || pvsIndex2 < 0 )
    return true;

  int bitIndex = pvsIndex1*visVecSize + (pvsIndex2 >> 3);

  return ( visVecs[bitIndex] & (1 << (pvsIndex2 & 7)) ) != 0;
}


// flips byte order if platform is big endian
inline void Q3Map::fixEndian(void *data, size_t sz) {
  if ( !bigEndian )
    return;

  unsigned char *bytes = (unsigned char *)data;

  for ( size_t i=1; i < sz; i++ ) {
    // swap
    unsigned char temp = bytes[i];
    bytes[i] = bytes[sz-i-1];
    bytes[sz-i-1] = temp;
  }
}


#endif
