#ifndef _OBJECTS_H

#ifdef __APPLE__ 
#include "definitions_mac.h" 
#else 
#include "definitions.h" 
#endif 

 
#include <stdio.h>			// Header File For Standard Input/Output
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL.h>
#ifdef __APPLE__
#include <SDL_image/SDL_image.h>
#else
#include <SDL/SDL_image.h>
#endif
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <cassert>

#include <list>
#include <map>

//#include "terrain.h"
#include "utility.h"
#include "quat.h"
#include "camera.h"
#include "model.h"
#include "joepack.h"

#include "globals.h"

#define TRIANGLES_PER_BBOX 1

#define COLLIDE_AND_DRIVE_TOGETHER true

class OBJECTTRI
{
public:
	VERTEX v1;
	VERTEX v2;
	VERTEX v3;
};

class OBJECTMODEL
{
public:
	JOEMODEL jmodel;
	string name;
	OBJECTMODEL * next;
	bool fullbright;
	bool skybox;
	bool blend;
};

class OBJECTNODE
{
public:
	VERTEX pos;
	bool driveable;
	bool cancollide;
	//OBJECTNODE * next;
	QUATERNION dir;
	OBJECTMODEL * model;
	string texture; //used to group and draw objects based on texture (for speed)
	float friction1; //the friction coefficient for non-treaded tires
	float friction2; //the friction coefficient for treaded tires
	float bumplength;
	float bumpmag;
	float rolling_resistance_factor;
	float rolling_drag;

	OBJECTNODE();
	bool operator<(const OBJECTNODE & other) {return texture < other.texture;}
};

class OBJCOLNODE
{
public:
	OBJCOLNODE();
	OBJCOLNODE(const OBJCOLNODE & other);
	OBJCOLNODE& operator= (const OBJCOLNODE &other);
	bool operator==(const OBJCOLNODE & other);
	bool operator<(const OBJCOLNODE & other);
	void SortVerts();
	bool EqualGeom(const OBJCOLNODE & other);
	
//private:
	OBJECTNODE * object;
	short vertexIndex[3];
};

class OBJCOLBRANCH
{
public:
	OBJCOLBRANCH();
	void DeleteChildren();
	AABB bbox;
	OBJCOLBRANCH * left;
	OBJCOLBRANCH * right;
	list <OBJCOLNODE *> leaves;
};

class OBJECTCOLLISION
{
public:
	~OBJECTCOLLISION();
	
	void AddColNode(OBJECTNODE * newobject, short * newvi);
	//bool Collide(VERTEX origin, VERTEX direction, VERTEX &outtri, bool closest, VERTEX & normal);
	bool CollideDriveable(VERTEX origin, VERTEX direction, VERTEX &outtri, bool closest, VERTEX & normal);
	bool CollideAABB(VERTEX origin, VERTEX direction, VERTEX &outtri, bool closest, VERTEX & normal, float seglen, OBJECTNODE * &colnode);
	bool CollideAABB_double(VERTEXD origin, VERTEXD direction, VERTEXD &outtri, bool closest, VERTEXD & normal, double seglen);
	bool CollideModelAABB(VERTEX * modelverts, int numfaces, AABB modelbbox, VERTEX & outtri, bool closest, VERTEX & normal, float & depth);
	void GenerateCollisionTree();
	void Clear();
	void GetTrisInBBox(AABB bbox, list <OBJECTTRI> & trilist);

private:
	list <OBJCOLNODE> colnodes;
	list <OBJCOLNODE> drvnodes;

	OBJCOLBRANCH coltree;
	void GenerateBranches(OBJCOLBRANCH * branch);
	bool CollideBranch(VERTEX origin, VERTEX direction, VERTEX &outtri, bool closest, OBJCOLBRANCH * branch, VERTEX & normal, float seglen, int & testcount, OBJECTNODE * &colnode);
	bool CollideBranch_double(VERTEXD origin, VERTEXD direction, VERTEXD &outtri, bool closest, OBJCOLBRANCH * branch, VERTEXD & normal, double seglen);
	bool CollideBranchModel(VERTEX * modelverts, int numfaces, AABB & modelbbox, VERTEX & outtri, bool closest, OBJCOLBRANCH * branch, VERTEX & normal, float & depth, int & testcount);
	bool CollideBranchGetTrisInBBox(AABB bbox, list <OBJECTTRI> & trilist, OBJCOLBRANCH * branch);
};

class OBJECTS
{
private:
	float lod_far;
	int display_x, display_y;
	float field_of_view;
	ofstream error_log;
	OBJECTMODEL * model_list;
	void delmodel();

	//OBJECTNODE * object_list;
	list <OBJECTNODE> object_list;
	//void delobject();

	bool object_list_cached;
	list <OBJECTNODE *> object_list_skybox;
	list <OBJECTNODE *> object_list_skybox_and_blend;
	list <OBJECTNODE *> object_list_normal;
	list <OBJECTNODE *> object_list_blend;

	bool DrawObject(OBJECTNODE * object, float max_view_dist, bool envmap, bool lightmap, bool cubelighting);

	OBJECTMODEL * AddModel(string modelname, string texname, bool mip, bool fullbright, bool skybox, bool blend, JOEPACK * pack);

	string path;

	OBJECTCOLLISION collision;

	map <string, TEXTURE_HANDLE> texture_db;

	void GroupObjectListByTexture();

	TEXTURE_HANDLE sphere_reflection;
	bool sphere_reflection_loaded;
	
	bool verticaltrackingskyboxes;
	
public:
	OBJECTS();
	~OBJECTS();
	void Draw(bool cull, float maxrange, float FOV, float hwratio, bool envmap, bool lightmap);
	void Draw(bool cull, float maxrange, float FOV, float hwratio) {Draw(cull,maxrange,FOV,hwratio,false,false);}
	void Draw(bool cull);
	void UpdateSettings();
	OBJECTNODE * Add(VERTEX pos, float rotation, string modelname, string texname, bool mip, bool fullbright, bool skybox, bool drv, bool col, bool blend, JOEPACK * pack, float f1, float f2, float bl, float bm, float rr, float rd);
	void DeleteAll();

	bool Collide(VERTEX origin, VERTEX direction, VERTEX &outtri, bool closest, VERTEX & normal, float seglen, OBJECTNODE * &colnode);
	bool CollideD(VERTEXD origin, VERTEXD direction, VERTEXD &outtri, bool closest, VERTEXD & normal, double seglen);
	bool CollideDriveable(VERTEX origin, VERTEX direction, VERTEX &outtri, bool closest, VERTEX & normal);
	//bool Collide(VERTEX origin, VERTEX direction, VERTEX &outtri, bool closest, OBJECTNODE * &collideobject);

	bool CollideModel(VERTEX * modelverts, int numfaces, AABB bbox, VERTEX & outtri, bool closest, VERTEX & normal, float & depth);

	void LoadObjectsFromFolder(string objectpath);
	bool GetCollideAndDriveTogether() {return COLLIDE_AND_DRIVE_TOGETHER;}
	
	void GetTrisInBBox(AABB bbox, list <OBJECTTRI> & trilist);
	
	void SetVerticalSkyboxTracking(bool vtsky) {verticaltrackingskyboxes = vtsky;}
};

#define _OBJECTS_H
#endif
