#ifndef GE2LOADER_H
#define GE2LOADER_H

#include <string>
#include <iostream>
#include <strstream>
#include <fstream>

using namespace std;

class matrix {
public:
	float m[4][4];

	matrix () {
		m[0][0] = 1; m[0][1] = 0; m[0][2] = 0; m[0][3] = 0;
		m[1][0] = 0; m[1][1] = 1; m[1][2] = 0; m[1][3] = 0;
		m[2][0] = 0; m[2][1] = 0; m[2][2] = 1; m[2][3] = 0;
		m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1;
	}
};

class boundingBox {
public:
	float left;
	float right;
	float up;
	float down;
	float front;
	float back;

	boundingBox () {
		left = right =0;
		up = down = 0;
		front = back =0;
	}
};


class gObject {
public:
	string name;
	matrix m;
	boundingBox bbox;
	string bodyName;

	gObject () {
		name = "";
		bodyName = "";
	}

};


class gobjArray {
public:
	int count;
	class gObject *go;
	
	
	~gobjArray () {
		if ( go ) delete [] go;
	}
	
	gobjArray () {
		count = 0;
		go = NULL;
	}
};




//
// 2D Vector Class
//
class vec2f {
public:
	float x;
	float y;

	vec2f() {
		x=0,y=0;
	};
};

//
// 3D Vector Class
//
class vec3f {
public:
	float x;
	float y;
	float z;

	vec3f() {
		x=0,y=0,z=0;
	};
};

//
// ColourRGB Class
//
class rgb3f {
public:
	float r;
	float g;
	float b;

	rgb3f() {
		r=0,g=0,b=0;
	};
};

//
// Verticies Class
//
class vertArray {
public:
	int count;
	vec3f *v;

	~vertArray() {
		if ( v ) delete [] v;
	}

	vertArray () {
		count = 0;
		v = NULL;
	}
};

class normArray {
public:
	int count;
	vec3f *n;
	
	
	~normArray () {
		if ( n ) delete [] n;
	}
	
	normArray () {
		count = 0;
		n = NULL;
	}
};

class uvArray {
public:
	int count;
	vec2f *uv;
	
	~uvArray () {
		if ( uv ) delete [] uv;
	}
	
	uvArray () {
		count = 0;
		uv = NULL;
	}
};

class triFace {
public:
	int	v[3];
	int n[3];
	int uv[3];

	triFace () {
		v[0] = v[1] = v[2] = 0;
		n[0] = n[1] = n[2] = 0;
		uv[0] = uv[1] = uv[2] = 0;
	}
};
class quadFace {
public:
	int	v[4];
	int n[4];
	int uv[4];

	quadFace () {
		v[0] = v[1] = v[2] = v[3] = 0;
		n[0] = n[1] = n[2] = n[3] = 0;
		uv[0] = uv[1] = uv[2] = uv[3] = 0;
	}
};

class facePart {
public:
	string name;
	string materialName;
	class material *mat;

	int	indexCount;
	int	*i;
	facePart () {
		name = "";
		materialName = "";
		mat = NULL;
		indexCount = 0;
		i = NULL;
	}
};

//
// Camera class
//
class camera {
public:
	string name;
	float viewAngle;
	float hither;
	float yon;
	vec3f location;
	vec3f aim;
	vec3f lookupVector;

	camera() {
		name = "";
		viewAngle = 0, hither = 0, yon = 0;
	}

};

class camArray {
public:
	int count;
	class camera *c;

	~camArray () {
		if (c) delete [] c;
	}

	camArray () {
		count = 0;
		c = NULL;
	}
};

class object {
public:
	string name;

	class vertArray vertList;
	class normArray normList;
	class uvArray	uvList;
	class facePart  faceParts;

	int triCount;
	class triFace	*tri;

	~object () {
		if ( tri ) delete [] tri;
	}

	object () {
		triCount = 0;
	}
};

class objArray {
public:
	int count;
	class object *o;

	~objArray () {
		if (o) delete [] o;
	}

	objArray () {
		count = 0;
		o = NULL;
	}
};



//
// Material class
//
class material {
public:
	string  name;
	rgb3f	diffuse;
	rgb3f	specular;
	rgb3f	emission;
	float	opacity;
	float	shininess;
	rgb3f	ambient;

	// texture
	string	fileName;
	int		width;
	int		height;
	int		numChannels;
	int		numBitsPerChannel;

	material () {
		name = "";
		opacity = 0, shininess = 0;
		fileName = "";
		width = 0, height = 0, numChannels = 0, numBitsPerChannel = 0;
	}
};

class matArray {
public:
	int count;
	class material *m;

	~matArray () {
		if (m) delete [] m;
	}

	matArray () {
		count = 0;
		m = NULL;
	}
};


//
// Light Class
//

class light {
public:
	string	name;
	float	brightness;
	rgb3f	rgb;
	vec3f	direction;
	vec3f	position;
	vec3f	radius;
	bool	useAttenuation;
	string	pattern;
	float	fallOffAngle;
	vec2f	aspectRatio;

	light () {
		name = "";
		brightness=0;
		useAttenuation=false;
		pattern="";
		fallOffAngle = 0;
	}

};

class lhtArray {
public:
	int count;
	class light *l;

	~lhtArray() {
		if ( l ) delete [] l;
	}

	lhtArray () {
		this->l=NULL;
		count = 0;
	}
};


class file {

public:
	
	string fullName; // full name and path

	string path;
	string name; // file name
	string ext;	// file extension


	char *data;
	int size;

	~file () {
		if ( this->data != NULL )
			delete [] data;
	}

	file () {
		fullName = "";
		path = "";
		name = "";
		ext = "";
		data = NULL;
		size=0;
	}

	file ( char *f) {
		fullName = "";
		path = "";
		name = "";
		ext = "";
		data = NULL;
		size=0;
		load (f);
	}


	void close ( void ) {
		if ( this->data != NULL )
			delete [] data;

		fullName = "";
		path = "";
		name = "";
		ext = "";
		data = NULL;
		size=0;

	}

	// load whole file in and set up some other file statistics
	int load ( char *f ) {

		if ( f==NULL || this->data ) return NULL;
		fullName = f;

		int pathEnd = fullName.rfind('\\');
		path = fullName.substr (0,pathEnd+1); // this makes it c:\path\ instead of c:\path
		int nameEnd = fullName.rfind('.');
		name = fullName.substr (pathEnd+1,(nameEnd-pathEnd-1));
		ext =  fullName.substr (nameEnd, fullName.size());
	
		ifstream fp( fullName.c_str(), ios::in | ios::binary );

		if ( !fp.is_open() ) return NULL;

		fp.seekg(0, ios::end );
		size = fp.tellg();
		fp.seekg(0, ios::beg );
		data = new char[size];
		fp.read (data, size);
		fp.close();
		return size;
	}
};








class ge {
	
public:

	// Objects
	class objArray obj;
	// Camera
	class camArray cams;
	// Lights
	class lhtArray lhts;
	// Materials
	class matArray mats;
	// GObjects
	class gobjArray gobj;

	file *grpFile;
	file *gofFile;
	file *gmfFile;
	file *lgtFile;
	file *camFile;

	~ge () {
		if ( grpFile )	delete grpFile;
		if ( gofFile )  delete gofFile;
		if ( gmfFile )  delete gmfFile;
		if ( lgtFile )  delete lgtFile;
		if ( camFile )  delete camFile;
	}

	ge () {
		grpFile = NULL;
		gofFile = NULL;
		gmfFile = NULL;
		lgtFile = NULL;
		camFile = NULL;

	}


	ge ( char *fileName );

	
	int getMat ( file *f, matArray *mat );
	int getCam ( file *f, camArray *cam );
	int getLgt ( file *f, lhtArray *lht );
	char *getIncludeFile ( file *grp, char *ext );
	int loadGroup ( char *fileName );
	int getObj ( file *f, objArray *obj );

};




#endif