#include <stdafx.h>

#include "lwoLoader.hpp"



using namespace std;




// reverse the order of 4 elements
template <class X> void reverse4 ( X *a ) {
	X t;
	t=a[2];	a[2]=a[1];	a[1]=t;
	t=a[3]; a[3]=a[0];	a[0]=t;
}
// reverse the order of 2 elements
template <class X> void reverse2 ( X *a ) {
	X t;
	t=a[0];
	a[0] = a[1];
	a[1] = t;
}
// compare 4 elements
template <class X> int matchTag ( X *a, X *b) {
	if ( ( a[0] == b[0] ) && ( a[1] == b[1] ) && ( a[2] == b[2] ) && ( a[3] == b[3] )  )
		return 1; // yes its matched

	return 0;	// no match
}
// copy 4 elements
template <class X> void copy4 ( X *dest, X *src ) {
	dest[0] = src[0];
	dest[1] = src[1];
	dest[2] = src[2];
	dest[3] = src[3];
}



//----------------------------------------------------------------------
// load the lightwave object, returns 0 if all is ok
//----------------------------------------------------------------------
int lwo::load ( string pth, string fnme ) {

	path=pth;
	filename=fnme;
	fileabs = path+filename;

	if ( ready ) return 0;
	ready=0;

	// open file as binary
	ifstream fp(fileabs.c_str(), ios::in | ios::binary );
	if (!fp) return 1;

	// get file size
	fp.seekg( 0L,ios::end );
	filesize = fp.tellg();
	fp.seekg( 0L,ios::beg );

	// allocate absolute memory
	data = new char [filesize];
	if ( !data ) return 1;

	fp.read ( data, filesize );

	if ( !fp.good() ) {
		fp.close();
		return 1;
	}

	format( data );
	calcPoly();


	// close file off
	ready = 1;
	fp.close();

	return 0;
}


void normVector (	float *fVert1, 
					float *fVert2,
					float *fVert3,
					float *fNorm
)
    {
    float Qx, Qy, Qz, Px, Py, Pz, len;

   Qx = fVert1[0]-fVert2[0];
   Qy = fVert1[1]-fVert2[1];
   Qz = fVert1[2]-fVert2[2];
   Px = fVert2[0]-fVert3[0];
   Py = fVert2[1]-fVert3[1];
   Pz = fVert2[2]-fVert3[2];

   fNorm[0] = Py*Qz - Pz*Qy;
   fNorm[1] = Pz*Qx - Px*Qz;
   fNorm[2] = Px*Qy - Py*Qx;

	len = fNorm[0]*fNorm[0] +	fNorm[1]*fNorm[1] +	fNorm[2]*fNorm[2];

	len = sqrt (len);

	fNorm[0] = fNorm[0]/len;
	fNorm[1] = fNorm[1]/len;
	fNorm[2] = fNorm[2]/len;

} 


void lwo::calcPoly () {

	float x,y,z;

	int i,j;

	VEC12 *vector;
	POLY *polygon;

	polygon=poly;
	vector=v;


	for ( i=0; i<polyCount; i++ ){
	
		// calc center for each polygon
		x=0;y=0;z=0;
		for (j=0; j<polygon[i].pointCount; j++ ) {
			x+=vector[ polygon[i].point[j] ].x;
			y+=vector[ polygon[i].point[j] ].y;
			z+=vector[ polygon[i].point[j] ].z;
		}
		
		polygon[i].center.x = x / (float)polygon[i].pointCount;
		polygon[i].center.y = y / (float)polygon[i].pointCount;
		polygon[i].center.z = z / (float)polygon[i].pointCount;
	
		normVector (	&vector[ polygon[i].point[0] ].x,
						&vector[ polygon[i].point[1] ].x,
						&vector[ polygon[i].point[2] ].x,
						&polygon[i].normal.x
					);
	}

}

//----------------------------------------------------------------------
// format the raw data into usable stuff
//----------------------------------------------------------------------
lwo::format (char *d) {
	int length;
	char tag[4] = "";

	//	is it a iff file, first 4 bytes are ascii 'FORM'
	if ( !matchTag ( d, "FORM" ) ) return 1;	d+=4;

	// length of remaining object in bytes
	length = * (int *) d;	reverse4 ( (char *) &length );
	
	// failure, filesize does not match expected size
	if ( filesize != (8+length ) )	return 1;	d+=4;

	//	is it a 'LWOB' iff file
	if ( !matchTag ( d, "LWOB" ) ) return 1; d+=4;


	// whats the next tag?
	copy4 (tag, d);
	d+=4;

	for (int l=0; l<4; l++ ) {

	// Surface Names
	if ( matchTag ( tag, "SRFS" ) ) {
		length = * (int *) d; reverse4 ( (char *) &length ); // length of this chunk
		d+=4;	getsurfacenames (d, length);
		d+=length;

		copy4 (tag, d);		d+=4;
	}

	// vertecies
	if ( matchTag ( tag, "PNTS" ) ) {
		length = * (int *) d; reverse4 ( (char *) &length ); // length of this chunk
		d+=4;	getpoints (d, length);
		d+=length;

		copy4 (tag, d);		d+=4;
	}

	// polygons
	if ( matchTag ( tag, "POLS" ) ) {
		length = * (int *) d; reverse4 ( (char *) &length ); // length of this chunk
		d+=4;	getpoly (d, length);
		d+=length;

		copy4 (tag, d);		d+=4;
	}

	// surface attributes
	if ( matchTag ( tag, "SRFS" ) ) {
		length = * (int *) d; reverse4 ( (char *) &length ); // length of this chunk
		d+=4;	getSurfAttrib (d, length);
		d+=length;

		copy4 (tag, d);		d+=4;
	}
	}
  return 0;

};

void lwo::getSurfAttrib (char *mem, int length ) {
}



// get all polygons from lwo file
void lwo::getpoly (char *mem, int length ) {
	int i=0, j=0, polycount=0;
	short *t, w;

	t=(short*)mem;

	// count all polygons first so we know how much memory is needed
	while  (i<length) {
		w= *t; reverse2 ((char*)&w); // num points in polygon
		t++; t+= w;	t++;	// skip past whole polygon
		i+= 2 + w*2 + 2;	// increment byte counter apropriately
		polycount++;
	}

	polyCount = polycount;

	poly  = new POLY[polyCount];

	t=(short*)mem;
	for (i=0; i< polyCount; i++ ) {
		w= *t; reverse2 ((char*)&w); // num points in polygon
		t++;
		poly[i].pointCount = w;
		poly[i].point = new short[w];
		//for ( j=0; j<w; j++ ){	// this was here originaly
		for ( j=w-1; j>=0; j-- ){	// this is here to reverse order of all polygons, cause ltwave has them opposite to opengl
			poly[i].point[j] = *t;t++;
			reverse2 ((char*)&poly[i].point[j]);
		}
	
	poly[i].surfaceId = *t; t++;
	reverse2 ((char*)&poly[i].surfaceId);
	
	}
}

// gets all vectors from lightwave file
void lwo::getpoints(char *mem, int length) {
	int add=1;
	float *m;
	vecCount = length/12;
	v = new VEC12[vecCount];
	
	m = (float*)mem;
//	m = mem;

	for (int i=0; i < vecCount; i++ ) {
		v[i].x = *m; m+=add;	reverse4 ( (char*)&v[i].x );
		v[i].y = *m; m+=add;	reverse4 ( (char*)&v[i].y );
		v[i].z = *m; m+=add;	reverse4 ( (char*)&v[i].z );

//		ge.print ("  \n %.2f %.2f %.2f", v[i].x, v[i].y, v[i].z);

	}
}


// extracts all surface names into a string array surfName, and gets num strings into surfCount
void lwo::getsurfacenames(char *mem, int length) {
		int namec=0;
		string name[100];

		// go through whole chunk
		while ( length>0 ) {
			name[namec] = mem;namec++;
			while ( *mem ) {mem++; length--; } // find end of string
			while ( ! *mem ) {mem++; length--; }  // find next string
			if ( namec>99 ) exit(9);
		}
		
		surfName = new string[namec];

		for (int i=0; i<namec; i++)
			surfName[i] = name[i];

		surfNameCount = namec;
}

//----------------------------------------------------------------------
// destruct
//----------------------------------------------------------------------
lwo::~lwo () {
	// free the memory object
	if ( data ) delete data;


	// free VEC12 vertecies
	if (v)
		delete v;
	ready = 0;

	// free all polygons

	POLY *pp = poly;
	if (pp)
		for (int i = polyCount ; i<polyCount; i++ )
			if (pp->point) delete pp->point;

	delete poly;

	// delete all the surface string names
	if (surfName)
		delete surfName;
};

//----------------------------------------------------------------------
// construct
//----------------------------------------------------------------------
lwo::lwo ()	{
	filesize=0;
	ready=0;
	path="";
	filename="";
	fileabs="";
	data=0;
};





/*
class lwo {
private:
	string path;
	string filename;
	string fileabs;
	int filesize;
	char *data;

//protected:
public:
	// polygons an array of POLY[polyCount]
	int polyCount;
	POLY *poly;

	// 3D vectors an array of VEC12[vecCount]
	int	vecCount;
	VEC12 *v;

	// surface names an array of strings[surfCount]
	int		surfNameCount;
	string *surfName;

	// various surface attributes
	//int surfCount;
	SURF *surfAttrib;

*/




// this will setup an artificial box mesh size X x Y for the purpose of simulating a wave

void lwo::makeWave ( int x, int y, char *name, float meshWidth, float meshHeight) {

	float cellWidth = meshWidth/x;
	float cellHeight = meshHeight/y;

	float offsetX = -(meshWidth/2);	// center it to origin in X (translate world co-ords)
	float offsetY = -(meshHeight/2);	// center it to origin in Y
	float offsetZ = 0;				// center it to origin in Z

	int numV = (x+1) * (y+1);
	int numTris = (x * y) * 2;
	int vCount = 0;
	int tCount = 0;


	// alloc memory for mesh
	this->polyCount = numTris;
	this->poly		= new POLY[numTris];

	this->vecCount = numV;
	this->v = new VEC12[numV];

	this->surfNameCount = 1;
	this->surfName = new string(name);

	this->surfAttrib = NULL;


	// generate the mesh verts
	
	// horizontal
	for ( int hh=0; hh<=x; hh++ ) {

		// vertical
		for ( int vv=0; vv<=y; vv++ ) {
		
		this->v[vCount].x = offsetX + (hh*cellWidth);
		this->v[vCount].y = offsetY + (vv*cellHeight);
		this->v[vCount].z = offsetZ;
		
		vCount++;
		}
	
	}

	// generate polygons
	
	// horizontal
	for ( int hh2=0; hh2<x; hh2++ ) {

		// vertical
		for ( int vv2=0; vv2<y; vv2++ ) {
		
			// GENERATE 2 TRIANGLES PER CELL
			this->poly[tCount].pointCount = 3;
			this->poly[tCount].point = new short[3];
		
			this->poly[tCount].point[0] = vv2+	(hh2*(y+1));
			this->poly[tCount].point[1] = vv2+1+	(hh2*(y+1));
			this->poly[tCount].point[2] = vv2+1+	((hh2+1)*(y+1));

			tCount++;

			this->poly[tCount].pointCount = 3;
			this->poly[tCount].point = new short[3];
		
			this->poly[tCount].point[0] = vv2+	(hh2*(y+1));
			this->poly[tCount].point[1] = vv2+1+	((hh2+1)*(y+1));
			this->poly[tCount].point[2] = vv2+	((hh2+1)*(y+1));
			tCount++;

		
		
		}
	
	}

	// poly centres and normals
	calcPoly();
}

