//////////////////////////////////////////////////////////////////////////////////
// You may use this code freely as long as you leave all the comments as they are.
// GE2 Loader
//
// Author : Robertj@nettaxi.com
// Author : rj27au@yahoo.com
//
//  IMPORTANT: you must make usre youre exporting tri's from MAYA, if any quads do appear then it will crash!!!
//             later on I may support a mixture of tri's and quad's , but for now NO! remember that :)
//			   Please triangulate your exports before exporting with GE2, the tri tesselation in ge2 seems not to triangualte at all
//
//
// The intended way to use this is to load a GE2 file struture as exported by
// the Maya 4 GE2 exporter with all features turned ON, and tesselation set to TRI, please see IMPORTANT above
//
//	file .grp .gof .gmf .lgt .cam
//
// The above files are mandatory, for example a test scene with all 5 files located in c:\gescn\test would be loaded like so :
//
//	class ge a;
//	a.loadGroup("C:\\GeScn\\test\\scene.grp");
//
//
///////////////////////////////////////////////////////////////////////////////////

#include <stdafx.h>
 
#include <string>
#include <iostream>
#include <strstream>
#include <fstream>


#include "ge2loader.hpp"


	ge::ge ( char *fileName ) {
		loadGroup(fileName);
	}

// game exchange scene Main
	int ge::loadGroup ( char *fileName ) {

		// Main group files
		grpFile = new file(fileName);
		if ( !grpFile->data ) return -1;

		// Get all light sources in scene
		lgtFile = new file(getIncludeFile ( grpFile,".lgt"));
		if ( !lgtFile->data ) return -1;
		getLgt (lgtFile, &lhts);

		// Get camera pos, so far Im asuming it only has one camera
		camFile = new file(getIncludeFile ( grpFile,".cam"));
		if ( !camFile->data ) return -1;
		getCam (camFile, &cams);

		// Get materials and textures
		gmfFile = new file(getIncludeFile ( grpFile,".gmf"));
		if ( !gmfFile->data ) return -1;
		getMat (gmfFile, &mats);

		// the last remaining file Geometry Objects, the big one
		gofFile = new file(getIncludeFile ( grpFile,".gof"));
		if ( !gofFile->data ) return -1;
		getObj (gofFile, &obj);

		return 0;
	}




	// this is the most involved part of the whole thing
	// get ready , its a long one
	int ge::getObj ( file *f, objArray *obj ) {
		string mode("");
		int foundPos=0; 
		string buff("");

		// supporting up to  1000 bodies, with 10 attributes each
		// attributes are : 
		// body 1,  has 
		//          vertices[] , faces[], normals[] , uv-array[] , face-parts[]
		int count [1000][10];
		memset (&count,0, (sizeof(int)*100*10));
		int bodyCount=0;
		istrstream data(f->data, f->size);



	while ( data.good() ) {

		// reached end of things to count!!!
		if ( buff == "gobject" ) break;
	
		//-------------------------------------------------------
		// count number of bodies loop
		if ( buff == "body" ) {	 //body lr_hood_Shape2 (

				bodyCount++;

				data >> buff; data >> buff; // lr_hood_Shape2 (  . . . name & bracket parsed

				mode = "";
				while ( mode != ")" ) {
					//-------------------------------------------------------
					// count all the verts
					if ( mode == "vertices[]" ) {
						data >> buff; // "<"
						while ( buff != ">" ) {	// count number of verts loop
							data >> buff; if ( buff == ">" ) break; // finished
							data >> buff;data >> buff;
							data >> buff;data >> buff;data >> buff;
							data >> buff; // read in 7 items and discard
							count[bodyCount-1][0] +=1;
						}
					
					}
					//-------------------------------------------------------
					// count all the faces
					if ( mode == "faces[]" ) {
						data >> buff; // "<"
						while ( buff != ">" ) {	// count number of faces loop
							for (int y=0; y<18; y++ ) { // each face has 18 strings , it must !!
								data >> buff; 
								if ( buff == ">" ) break; // finished
							}
							count[bodyCount-1][1] +=1;
						}
						count[bodyCount-1][1] -=1; // always one too many
					}
					//-------------------------------------------------------
					// count all the normals
					if ( mode == "normals[]" ) {
						data >> buff; // "<"
						while ( buff != ">" ) {	// count number of normals loop
							data >> buff; if ( buff == ">" ) break;
							data >> buff;data >> buff;data >> buff;
							data >> buff;data >> buff;
							count[bodyCount-1][2] +=1;
						}
					}
					//-------------------------------------------------------
					// count all the UV's
					if ( mode == "uv-array[]" ) {
						data >> buff; // "<"
						while ( buff != ">" ) {	// count number of UV's loop
							data >> buff; if ( buff == ">" ) break;
							data >> buff;data >> buff;data >> buff;
							data >> buff;data >> buff;
							count[bodyCount-1][3] +=1;
						}
					}
					//-------------------------------------------------------
					// count all the Face Parts
					if ( mode == "face-parts[]" ) {
						data >> buff; // "<"
						data >> buff; data >> buff; // (name "phongE10";
						data >> buff; data >> buff; // material "phongE10";
						data >> buff; data >> buff; //face-indices[] <
						while ( buff != ">)" ) {	// count number of UV's loop
							data >> buff; 
							count[bodyCount-1][4] +=1;
						}
						count[bodyCount-1][4] -=1; // its always one too many
						data >> buff; // ">"
					}
					//-------------------------------------------------------
					// count all the segments
					if ( mode == "segments[]" ) {
						data >> buff; // "<"
						while ( buff != ">" ) {	// count number of segments loop
							data >> buff; if ( buff == ">" ) break; // finished
							data >> buff;data >> buff;
							data >> buff;data >> buff;data >> buff;
							data >> buff; // read in 7 items and discard
							count[bodyCount-1][5] +=1;
						}
					}
					data >> mode; // this is the type of segment coming up next
				}
		}

		data >> buff; // see whats next, could be a body or gobject

	} // end counting loop

	//////////////////////////////////////////////////////////////////////////////////////////
	// Half done here, so far I have counted quantities for all the various attributes
	// now ready to load them in!!!

	char skip;
	int loop=0;

	obj->count = bodyCount;
	obj->o = new class object[bodyCount];
	istrstream dat(f->data, f->size);

	while ( buff!="body" ) dat >> buff;

	// go read in all bodies
	for ( int j=0; j<bodyCount; j++ ) {

		//-------------------------------------------------------
		// parse bodies loop
		if ( buff == "body" ) {	 //body lr_hood_Shape2 (

			// get object name
			dat >> obj->o[j].name;		
			dat >> buff; // "("
			mode = "";
			while ( mode != ")" ) {

				//-------------------------------------------------------
				// parse all the verts
				if ( mode == "vertices[]" ) {
					dat >> buff; // "<"

					// alloc mem for all verts
					obj->o[j].vertList.count = count[j][0];
					obj->o[j].vertList.v = new class vec3f[count[j][0]];

					for ( loop=0; loop<count[j][0]; loop++ ) {
						dat >> buff; // (coord
						dat >> obj->o[j].vertList.v[loop].x;
						dat >> obj->o[j].vertList.v[loop].y;
						dat >> obj->o[j].vertList.v[loop].z;
						dat >> buff;dat >> buff;dat >> buff;dat >> buff; // ;)  # vertex 0
					}
					dat >> buff; // ">"
				}
				//-------------------------------------------------------
				// count all the faces
				if ( mode == "faces[]" ) {
					dat >> buff; // "<"

					// alloc mem for all faces
					obj->o[j].triCount = count[j][1];
					obj->o[j].tri = new class triFace[ count[j][1] ];

					for ( loop=0; loop<count[j][1]; loop++ ) {
				      //(vertex-indices[] < 214; 215; 2;>  # face 1
						dat >> buff; dat >> buff;
						dat >> obj->o[j].tri[loop].v[0]; dat >> buff;
						dat >> obj->o[j].tri[loop].v[1]; dat >> buff;
						dat >> obj->o[j].tri[loop].v[2]; dat >> buff;
						dat >> buff; dat >> buff; dat >> buff;
				       //vertex-normal-indices[] < 0; 1; 2;>
						dat >> buff; dat >> buff;
						dat >> obj->o[j].tri[loop].n[0]; dat >> buff;
						dat >> obj->o[j].tri[loop].n[1]; dat >> buff;
						dat >> obj->o[j].tri[loop].n[2]; dat >> buff;
						//uv-indices[] < 214; 2; 215;>)
						dat >> buff; dat >> buff;
						dat >> obj->o[j].tri[loop].uv[0]; dat >> buff;
						dat >> obj->o[j].tri[loop].uv[1]; dat >> buff;
						dat >> obj->o[j].tri[loop].uv[2]; dat >> buff;
					}
					dat >> buff; // ">"
				}
				//-------------------------------------------------------
				// count all the normals
				if ( mode == "normals[]" ) {
					dat >> buff; // "<"

					// alloc mem for all normals
					obj->o[j].normList.count = count[j][2];
					obj->o[j].normList.n = new  vec3f[ count[j][2] ];

					for ( loop=0; loop<count[j][2]; loop++ ) {
						dat >> obj->o[j].normList.n[loop].x;
						dat >> obj->o[j].normList.n[loop].y;
						dat >> obj->o[j].normList.n[loop].z;
						dat >> buff;dat >> buff;dat >> buff;dat >> buff;
					}
					dat >> buff; // ">"
				}
				//-------------------------------------------------------
				// count all the UV's
				if ( mode == "uv-array[]" ) {
					dat >> buff; // "<"

					// alloc mem for all normals
					obj->o[j].uvList.count = count[j][3];
					obj->o[j].uvList.uv = new class vec2f[ count[j][3] ];

					for ( loop=0; loop<count[j][3]; loop++ ) {
						dat >> buff;
						dat >> skip;
						dat >> obj->o[j].uvList.uv[loop].x;
						dat >> skip;
						dat >> obj->o[j].uvList.uv[loop].y;
						dat >> buff;dat >> buff;dat >> buff;dat >> buff;
					}
					dat >> buff; // ">"
				}
				//-------------------------------------------------------
				// count all the segments, not implemented yet
				if ( mode == "segments[]" ) {
						dat >> buff; // "<"

					for ( loop=0; loop<count[j][5]; loop++ ) {
							dat >> buff;dat >> buff;dat >> buff;
							dat >> buff;dat >> buff;dat >> buff;
							dat >> buff; // read in 7 items and discard
					}
					dat >> buff; // ">"
				}
				//-------------------------------------------------------
				// count all the Face Parts
				if ( mode == "face-parts[]" ) {
					dat >> buff; // "<"
					dat >> buff; // (name

					obj->o[j].faceParts.indexCount = count[j][4];
					// alloc mem for face parts
					obj->o[j].faceParts.i = new int[ count[j][4] ];

					dat >> obj->o[j].faceParts.name;
					dat >> buff;	// material
					dat >> obj->o[j].faceParts.materialName;

					dat >> buff;dat >> buff; // face-indices[] <
 
					for ( loop=0; loop<count[j][4]; loop++ ) {
						dat >> obj->o[j].faceParts.i[loop];
						dat >> buff;
					}
						dat >> buff; // >)
					dat >> buff; // >
				}
			dat >> mode; // ")" - it should be the end of the object

			} // ")"
		
		dat >> buff; // should be another body or gobject
		} // if in Body
		
	}	// bodycCount loop

	dat >> buff;
	
	// finaly lead in GObjects, note, the bounding box may be totaly mixed up, sorry :)
	gobj.count = this->obj.count;
	gobj.go = new class gObject [ gobj.count ] ;


		for ( int z=0; z<gobj.count; z++ ) {
			dat >> buff; dat >> buff; dat >> buff;// gobject insideFace (
			dat >> buff; dat >> gobj.go[z].name; //    name "insideFace";
			dat >> buff; dat >> buff;			//    matrix[] <

			for ( int my=0; my < 4; my ++ ) {
				for ( int mx=0; mx < 4; mx ++ ) {
					dat >> gobj.go[z].m.m[mx][my];
				}
				dat >> buff; // ";"
			}

			dat >> buff; //  >
			dat >> buff; dat >> buff; //     bounding-box[] <

			dat >> gobj.go[z].bbox.back;
			dat >> gobj.go[z].bbox.down;
			dat >> gobj.go[z].bbox.front;
			dat >> buff; // ";"
			dat >> gobj.go[z].bbox.left;
			dat >> gobj.go[z].bbox.right;
			dat >> gobj.go[z].bbox.up;

			dat >> buff; dat >> buff; // ; >
			dat >> buff; ")";

		}
		

	return 0;
	}

	int ge::getMat ( file *f, matArray *mat ) {
	// geting the materials (textures) is not too hard
		// first count how many there are then read them out straight forward
		// its important to remember that for now we are expecting the GLShade materials
		// the sega n64 psx ones will be ignored
		int foundPos=0; 
		int count = 0;
		string buff1("");

		istrstream data1(f->data, f->size);

		while ( data1.good() ) {
			data1 >> buff1;
			if ( buff1=="GL_Shade") count++;
			if ( data1.tellg() > f->size ) break;
		}
		// start over, this time for real
		mat->count = count;
		mat->m = new class material[count];

		istrstream data(f->data, f->size);
			
		buff1 = "";
		for ( int i=0; i<count; i++ ) {
			// get name
			while ( buff1 != "material" ) 
				data >> buff1;
			data >> mat->m[i].name;

			// use GL_Shade data for open gl, and ignore the rest
			while ( buff1 != "GL_Shade" ) data >> buff1;

			// grab the relevant stuff out of the definition
			while ( buff1 != ")" ) {
				if ( buff1 == "DIFFUSE-COLOR" )  { data >> mat->m[i].diffuse.r; data >> mat->m[i].diffuse.g; data >> mat->m[i].diffuse.b; }
				if ( buff1 == "SHININESS" )		 { data >> mat->m[i].shininess; }
				if ( buff1 == "SPECULAR-COLOR" ) { data >> mat->m[i].specular.r;data >> mat->m[i].specular.g;data >> mat->m[i].specular.b; }
				if ( buff1 == "EMISSION-COLOR" ) { data >> mat->m[i].specular.r;data >> mat->m[i].specular.g;data >> mat->m[i].specular.b; }
				if ( buff1 == "OPACITY" )		 { data >> mat->m[i].opacity; }
				 if ( buff1 == "TEXTURE-MAP" )	 {
					// this is a bit more involved
						while ( buff1 != ")" ) {
							if ( buff1 == "name" )				{ data >> mat->m[i].fileName; }
							if ( buff1 == "width" )				{ data >> mat->m[i].width; }
							if ( buff1 == "height" )			{ data >> mat->m[i].height; }
							if ( buff1 == "number-of-channels" ){ data >> mat->m[i].numChannels; }
							if ( buff1 == "bits-per-channel" )	{ data >> mat->m[i].numBitsPerChannel; }
							data >> buff1;
						}
				 }
				
				data >> buff1;
			}
		}
		return 0;
	}


	int ge::getCam ( file *f, camArray *cam ) {
	// geting the camera pos is simple
		// first there is a template structure, by-pass that and thereafter you will have the real camera data
		int foundPos=0; 
		int count = 0;
		string buff1("");

		istrstream data(f->data, f->size);

		// find the close bracket of the template
		while ( buff1 != ")" ) data >> buff1;

		// find the start of the camera
		while ( buff1 != "(" ) data >> buff1;

		// for now we asume only one camera in the file!!
		cam->count=1;
		cam->c = new class camera[cam->count];
		
		while ( buff1 != ")" ) {
		data >> buff1; 
		 if ( buff1 == "view-angle" )	  data >> cam->c[0].viewAngle;
		 if ( buff1 == "hither" )		  data >> cam->c[0].hither;
		 if ( buff1 == "yon" )			  data >> cam->c[0].yon;
		 if ( buff1 == "location" )     { data >> cam->c[0].location.x; data >> cam->c[0].location.y; data >> cam->c[0].location.z; }
		 if ( buff1 == "aim" )	        { data >> cam->c[0].aim.x;	data >> cam->c[0].aim.y;	data >> cam->c[0].aim.z; }
		 if ( buff1 == "lookup-vector" ){ data >> cam->c[0].lookupVector.x;	data >> cam->c[0].lookupVector.y;	data >> cam->c[0].lookupVector.z; }
		}
		return 0;
	}


	////////////////////////////////////////////////////////////////////////////////////////////
	//	
	// extract lights from the light file and put them in the light array
	//
	////////////////////////////////////////////////////////////////////////////////////////////
	int ge::getLgt ( file *f, lhtArray *lht ) {
		int foundPos=0; 
		int count = 0;
		string buff1("");


		istrstream data(f->data, f->size);
		buff1 = "";
		// find the beginning of light instances
		while ( buff1 != "<" ) {
			data >> buff1;
			if ( data.bad() ) return -1;
		}

		foundPos = data.tellg();

		// count all the lights ">"
		while ( buff1 != ">" ) {
			data >> buff1;
			if ( buff1=="(name") count++;
			if ( data.bad() ) return -1;
		}

		// make room for the lights
		lht->count = count;
		lht->l = new class light[count];
	
		// go back to where light instances start
		data.seekg(foundPos);

		// read in the light attributes
		for ( int i=0; i < count; i++ ) {
			buff1 = "";
			while ( buff1 != ")" ) {
				data >> buff1;
				// ######## NOTE not all attributes accounted for, add them as needed ########
				if ( buff1 == "(name" )		 { data >> lht->l[i].name; lht->l[i].name.erase(0,1); lht->l[i].name = lht->l[i].name.substr (0, lht->l[i].name.find_first_of('"') ); } // this mess gets the string between the quotation marks!
				if ( buff1 == "brightness" ) { data >> lht->l[i].brightness; }
				if ( buff1 == "rgb" )		 { data >> lht->l[i].rgb.r;data >> lht->l[i].rgb.g;data >> lht->l[i].rgb.b; }
				if ( buff1 == "direction" )  { data >> lht->l[i].direction.x;data >> lht->l[i].direction.y;data >> lht->l[i].direction.z; }
			}
		}
		return 0;
		
	}


	////////////////////////////////////////////////////////////////////////////////////////////
	//	
	// extract include fullPathName of a file with the given extension *ext
	//
	////////////////////////////////////////////////////////////////////////////////////////////
	char *ge::getIncludeFile ( file *grp, char *ext ) {
		string n( grp->path );
		string buff("");

		istrstream data(grp->data, grp->size);

		while ( data.good() ) {
			while (buff!="include")	data >> buff;

			data >> buff;		 // grab watch after the include eg: ""myfile.abc"" remember its in quotes

			if ( -1 != buff.find( ext,0) ) { // if found the right extension
				buff.erase(0,1);	// delete the first char which should be a "
				n =  n + buff.substr ( 0, buff.find_first_of('"'));
				char *ret = new char[n.length()+1];
				strcpy (ret,n.c_str());
				return ret;
			}
		}
		return NULL;	
	}


