class DPLRenderer;
#include <winsock2.h>
#include "VGCDivLoader.h"

using namespace std;

hash_map<string, DivMaterial*> VGCDivLoader::mMaterials;
hash_map<string, DivTexture*> VGCDivLoader::mTextures;
hash_map<string, DivRamp*> VGCDivLoader::mRamps;
hash_map<string, DivObject*> VGCDivLoader::mObjects;

VGCDivLoader::VGCDivLoader()
{
	//Initialize header to defaults
	mHeaderInfo.precision = PRECISION_32BIT;
	mHeaderInfo.scale = 1;
	mHeaderInfo.type = TYPE_GEOMETRY;
	mHeaderInfo.unit = UNIT_INCHES;

	//Initialize surface props to defaults
	mMasterProps.b_material_type = 2;
	mMasterProps.b_material_name = 0;
	mMasterProps.decal_offset = 0;
	mMasterProps.drawmode = 0;
	mMasterProps.drawmode_width = 1;
	mMasterProps.f_material_type = 2;
	mMasterProps.f_material_name = 0;
	mMasterProps.faceted = 0;
	mMasterProps.locks = 0;
	mMasterProps.name = 0;
	mMasterProps.plane = 0;
	mMasterProps.specialInst = 0;
	mMasterProps.vertex_mask = 0;
	mMasterProps.isImmune = false;
	mMasterProps.drawSpheres = false;

	if (mTextures.find("NULL") == mTextures.end())
	{
		DivTexture *tex = new DivTexture;
		memset(tex,0,sizeof(DivTexture));
		tex->name = new char[5];
		strcpy_s(tex->name,5,"NULL");
		tex->texBuffer = new TexBuffer;
		tex->texBuffer->height = 1;
		tex->texBuffer->width = 1;
		tex->texBuffer->m_buffer = new RGBA*[1];
		tex->texBuffer->m_buffer[0] = new RGBA;
		tex->texBuffer->m_buffer[0]->r = 255;
		tex->texBuffer->m_buffer[0]->g = 255;
		tex->texBuffer->m_buffer[0]->b = 255;
		tex->texBuffer->m_buffer[0]->a = 255;
		mTextures.insert(std::pair<string,DivTexture*>(string("NULL"),tex));
	}
}

VGCDivLoader::~VGCDivLoader()
{
	delete[] mMasterProps.b_material_name;
	delete[] mMasterProps.name;
	delete[] mMasterProps.f_material_name;
}

void VGCDivLoader::parse(LPDIRECT3DDEVICE9 device, char *fileName)
{
	const char BIZ_IDSTR[] = "DIV-BIZ2";
	const char VTX_IDSTR[] = "DIV-VTX2";
   FILE file;
   FILE *mFile = &file;
	if (fopen_s(&mFile, fileName, "rb") != 0)
		return;

	char id[8];
	
	fread(&id, 8, 1, mFile);

	//Compare first 8 bits to make sure it's a biz2 file
	if (strncmp(id,"DIV-BIZ2",8) != 0)
	{
		fclose(mFile);
		return;
	}

	BIZ_IDENTITIES ident;
	unsigned int len;
	while(!feof(mFile))
	{
		//Retrieve ID and length of upcoming block
		getBlock(mFile, ident, len);
		
		switch(ident)
		{
		case HEADER:
			parseHeader(mFile, len);
			break;
		case TRAILER:
			//Output code here
			fclose(mFile);
         return;
		case TEXTURE:
			//This should be a geometry file
			fseek(mFile,len,SEEK_CUR);
			break;
		case MATERIAL:
			//This should be a geometry file
			fseek(mFile,len,SEEK_CUR);
			break;
		case RAMP:
			//This should be a geometry file
			fseek(mFile,len,SEEK_CUR);
			break;
		case OBJECT:
			parseObject(mFile, len, fileName);
			break;
		default:
			//Unknown!  Skip it
			if (!feof(mFile))
			{
				fseek(mFile,len,SEEK_CUR);
			}
			break;
		}
	}

	fclose(mFile);
}

void VGCDivLoader::parseHeader(FILE *file, unsigned int len)
{
	//Set identifiable header props
	BIZ_IDENTITIES ident;
	unsigned int nlen;

	while(len > 0)
	{
		len -= getBlock(file, ident,nlen);
		len -= nlen;

		switch(ident)
		{
		case HDR_VERSION:
			fread(&mHeaderInfo.verMajor,1,1,file);
			fread(&mHeaderInfo.verMinor,1,1,file);
			break;
		case HDR_DATE:
			fread(&mHeaderInfo.dateDay,1,1,file);
			fread(&mHeaderInfo.dateMon,1,1,file);
			fread(&mHeaderInfo.dateYear,1,1,file);
			break;
		case HDR_TIME:
			fread(&mHeaderInfo.timeHour,1,1,file);
			fread(&mHeaderInfo.timeMin,1,1,file);
			break;
		case HDR_SCALE:
			fread(&mHeaderInfo.scale,floatSize(),1,file);
			break;
		case HDR_PRECISION:
			char precision;
			fread(&precision,1,1,file);
			mHeaderInfo.precision = (FloatPrecision)precision;
			
			if (mHeaderInfo.precision == PRECISION_64BIT)
			{
				//Don't believe we have any 64 bit files, so fuck it
				MessageBox(NULL,_T("PANIC 64 BIT"),_T("OH NOES"),MB_OK);
				std::cout << "PANIC: 64 BIT PRECISION." << std::endl;
				std::cin.get();
				exit(1);
			}
			break;
		case HDR_FILETYPE:
			char type;
			fread(&type,1,1,file);
			mHeaderInfo.type = (FileType)type;
			break;
		case HDR_UNIT:
			char unit;
			fread(&unit,1,1,file);
			mHeaderInfo.unit = (Unit)unit;
			break;
		default:
			cout << "UNKNOWN HEADER ENTRY " << ident << endl;
		}
	}
}

TexBuffer *VGCDivLoader::LoadVTXFile(char *fileName, DivTexture *tex)
{
	int len = strlen(fileName) + 5;
	char *fullFileName = new char[len];
	strcpy_s(fullFileName, len, fileName);
	strcat_s(fullFileName, len, ".VTX");

	FILE *texFile;
	if (fopen_s(&texFile, fullFileName, "rb") != 0)
	{
		delete [] fullFileName;
		return NULL;
	}

	delete [] fullFileName;
	char id[8];
	fread(id, 1, 8, texFile);

	if (strncmp(id, "DIV-VTX2", 8) != 0)
	{
		fclose(texFile);
		return NULL;
	}

	unsigned int blen;
	BIZ_IDENTITIES ident;
	while(!feof(texFile))
	{
		//Retrieve ID and length of upcoming block
		getBlock(texFile, ident, blen);

		switch(ident)
		{
		case HEADER:
			fseek(texFile, blen, SEEK_CUR);
			break;
		case VTX_TEX:
			tex->texBuffer = new TexBuffer;
			unsigned int nlen;
			while(blen > 0)
			{
				blen -= getBlock(texFile, ident, nlen);
				blen -= nlen;

				unsigned char pixelWidth;
				unsigned char colorSettings;

				switch(ident)
				{
				case VTX_COLOR:
					fread(&colorSettings, 1, 1, texFile);
					break;
				case VTX_SIZE:
					fread(&pixelWidth, 1, 1, texFile);
					break;
				case VTX_DIMENSIONS:
					fread(&tex->texBuffer->width, 4, 1, texFile);
					fread(&tex->texBuffer->height, 4, 1, texFile);
					break;
				default:
					if (ident >= 0x68 && ident == 0x68 + colorSettings)
					{
						int dataPoints = 1;
						if (colorSettings & 0x01)
						{
							dataPoints++;
						}
						if (colorSettings & 0x02)
						{
							dataPoints += 2;
						}

						int sampleSize = 1;
						if (pixelWidth != 0x01)
						{
							MessageBox(NULL, _T("PANIC: UNEXPECTED SAMPLE SIZE FOR TEX"), _T("PROBLEM"), MB_OK);
						}

						int tLen = nlen / (dataPoints * sampleSize);
						tex->texBuffer->m_buffer = new RGBA*[tex->texBuffer->height];

						for (int i=0; i < tex->texBuffer->height; i++)
						{
							tex->texBuffer->m_buffer[i] = new RGBA[tex->texBuffer->width];

							for (int j=0; j< tex->texBuffer->width; j++)
							{
								fread(&tex->texBuffer->m_buffer[i][j].r, 1, 1, texFile);

								if (colorSettings & 0x02)
								{
									fread(&tex->texBuffer->m_buffer[i][j].g, 1, 1, texFile);
									fread(&tex->texBuffer->m_buffer[i][j].b, 1, 1, texFile);
								} else
								{
									tex->texBuffer->m_buffer[i][j].g = tex->texBuffer->m_buffer[i][i].r;
									tex->texBuffer->m_buffer[i][j].b = tex->texBuffer->m_buffer[i][i].r;
								}

								if (colorSettings & 0x01)
								{
									fread(&tex->texBuffer->m_buffer[i][j].a, 1, 1, texFile);
								} else 
								{
									tex->texBuffer->m_buffer[i][j].a = 255;
								}
							}
						}
					} else 
					{
						if (ident >= 0x68)
						{
							MessageBox(NULL, _T("BAD TEX INFO"), _T("PROBLEMS"), MB_OK);
						} else
						{
							MessageBox(NULL, _T("UNKNOWN TAG IN VTX"), _T("PROBLEM"), MB_OK);
						}
						fseek(texFile, nlen, SEEK_CUR);
					}
				}
			}
			break;
		case TRAILER:
			fclose(texFile);
			return tex->texBuffer;
			break;
		default:
			MessageBox(NULL, _T("UNKNOWN VTX INFO"), _T("PROBLEM"), MB_OK);
			fseek(texFile, blen, SEEK_CUR);
			break;
		}
	}

	fclose(texFile);
	return tex->texBuffer;;
}

unsigned char getBSLData(int channel, int value)
{
	int shift = (channel + (((channel + 1) % 2) * 2)) * 4;
	int mask = 0xF0 << shift;
	return (unsigned char)((value & mask) >> shift);
}

TexBuffer *VGCDivLoader::LoadBSLFile(char *fileName, char channelIndex, DivTexture *tex)
{
	int len = strlen(fileName) + 5;
	char *fullFileName = new char[len];
	strcpy_s(fullFileName, len, fileName);
	strcat_s(fullFileName, len, ".BSL");

	FILE *texFile;
	if (fopen_s(&texFile, fullFileName, "rb") != 0)
	{
		delete [] fullFileName;
		return NULL;
	}

	char id[8];
	fread(id, 1, 8, texFile);

	delete [] fullFileName;
	if (strncmp(id, "DIV-BSL2", 8) != 0)
	{
		fclose(texFile);
		return NULL;
	}

	int width, height, depth, nTextures;
	
	fread(&width, 4, 1, texFile);
	fread(&height, 4, 1, texFile);
	fread(&depth, 4, 1, texFile);
	fseek(texFile, 4, SEEK_CUR);
	fread(&nTextures, 4, 1, texFile);

	int usefulChannel = channelIndex;

	for (int i=0; i < nTextures; i++)
	{
		int header, channel;
		fread(&header, 4, 1, texFile);
		fread(&channel, 4, 1, texFile);

		char *chanName = new char[header-4];
		fread(chanName, 1, header - 4, texFile);

		delete [] chanName;
	}

	tex->texBuffer = new TexBuffer;
	tex->texBuffer->height = height;
	tex->texBuffer->width = width;
	tex->texBuffer->m_buffer = new RGBA*[height];
	for (int i=0; i <height; i++)
	{
		tex->texBuffer->m_buffer[i] = new RGBA[width];
		for (int j=0; j < width; j++)
		{
			int val;
			fread(&val, 4, 1, texFile);
			if (usefulChannel < 6)
			{
				unsigned char color = getBSLData(usefulChannel,val);
				tex->texBuffer->m_buffer[i][j].a = 255;
				tex->texBuffer->m_buffer[i][j].r = color;
				tex->texBuffer->m_buffer[i][j].g = color;
				tex->texBuffer->m_buffer[i][j].b = color;
			} else
			{
				if (usefulChannel == 8)
				{
					tex->texBuffer->m_buffer[i][j].a = getBSLData(2, val);
				} else
				{
					tex->texBuffer->m_buffer[i][j].a = 255;
				}
				tex->texBuffer->m_buffer[i][j].r = getBSLData(5, val);
				tex->texBuffer->m_buffer[i][j].g = getBSLData(4, val);
				tex->texBuffer->m_buffer[i][j].b = getBSLData(3, val);
			}
		}
	}
	return tex->texBuffer;
}

void VGCDivLoader::parseTexture(FILE *file, unsigned int len)
{
	BIZ_IDENTITIES ident;
	unsigned int nlen;

	DivTexture *texture = new DivTexture;
	memset(texture,0,sizeof(DivTexture));
	texture->alpha = 255;

	char *texMapName = NULL;
   char channelIndex = 0;

	while(len > 0)
	{
		len -= getBlock(file, ident,nlen);
		len -= nlen;

		char *fileName;	//Name of tex raster file
		char *ran; //Temp place for strings

		switch(ident)
		{
		case GEN_NAME:
			{
				texture->name = new char[nlen];
				fread(texture->name, 1, nlen, file);
				break;
			}
		case GEN_SCOPE:
			fseek(file,1,SEEK_CUR);
			break;
		case TEX_MAP:
			texMapName = new char[nlen];
			fread(texMapName,1,nlen,file);
			
			break;
		case TEX_MINIFY:
			fread(&texture->minify,1,1,file);
			break;
		case TEX_MAGNIFY:
			fread(&texture->magnify,1,1,file);
			break;
		case TEX_ALPHA:
			fread(&texture->alpha,1,1,file);
			break;
		case TEX_WRAP_U:
			fread(&texture->wrap_u,1,1,file);
			break;
		case TEX_WRAP_V:
			fread(&texture->wrap_v, 1, 1, file);
			break;
		case TEX_DETAIL:
			fread(&texture->detail_type, 1, 1, file);
			if (nlen > 1)
			{
				texture->detail_name = new char[nlen - 1];
				fread(texture->detail_name, 1, nlen - 1, file);
			}
			break;
		case TEX_BSL_CHANNEL:
			//This is the channel index being loaded for a BSL file
			fread(&channelIndex, 1, 1, file);
			break;
		case SRF_SPECIAL:
			{
				char *pos;
				//Awesome
				ran = new char[nlen];
				fread(ran,1,nlen,file);

				if ((pos = strstr(ran,"SCROLL")) != NULL)
				{
					float startu,startv,endu,endv;
					sscanf(pos, "SCROLL %f %f %f %f",&startu, &startv, &endu, &endv);
					texture->doScroll = true;
					texture->scrollU = endu;
					texture->scrollV = endv;
				}
				delete [] ran;
				break;
			}
		default:
			MessageBox(NULL,_T("UNKNOWN TEXTURE ENTRY"),_T("PROBLEM"),MB_OK);
			if (!feof(file))
			{
				fseek(file, nlen, SEEK_CUR);
			}
		}
	}

   //Load up the texture buffer 
	//Try to load as a BSL first
	if ((texture->texBuffer = LoadBSLFile(texMapName, channelIndex, texture)) == NULL)
	{
		//If that didn't work, do VTX
		texture->texBuffer = LoadVTXFile(texMapName, texture);
	}

	delete [] texMapName;

	if (texture->name == NULL)
	{
		delete [] texture->name;
		if (texture->detail_name != NULL)
		{
			delete [] texture->detail_name;
		}
		if (texture->texBuffer != NULL)
		{
			delete texture->texBuffer;
		}
		delete texture;
		fseek(file,len,SEEK_CUR);
	} else
	{
		//Add material
		mTextures.insert(pair<string, DivTexture*>(string(texture->name),texture));
	}
}

bool VGCDivLoader::parseMaterial(FILE *file, unsigned int len, char *filename)
{
	BIZ_IDENTITIES ident;
	unsigned int nlen;

	DivMaterial *material = new DivMaterial;
	memset(material,0,sizeof(DivMaterial));
	material->amb_b = 1;
	material->amb_g = 1;
	material->amb_r = 1;
	material->dif_b = 1;
	material->dif_g = 1;
	material->dif_r = 1;
	material->emi_b = 0;
	material->emi_g = 0;
	material->emi_r = 0;
	material->spe_b = 0;
	material->spe_g = 0;
	material->spe_p = 1;
	material->spe_r = 0;
	material->opa_b = 1;
	material->opa_g = 1;
	material->opa_r = 1;
	material->isImmune = false;

	while(len > 0)
	{
		len -= getBlock(file, ident, nlen);
		len -= nlen;

		char *ran; //Temp place for strings

		switch(ident)
		{
		case GEN_NAME:
			material->name = new char[nlen];
			fread(material->name, 1, nlen, file);
			break;
		case GEN_SCOPE:
			fseek(file,1,SEEK_CUR);
			break;
		case MAT_TEXTURE:
			fread(&material->tex_type, 1, 1, file);
			if (nlen > 1)
			{
				material->tex_name = new char[nlen - 1];
				fread(material->tex_name, 1, nlen - 1, file);
			} 

			//Precaching textures moves the file offset, so:
			/*loc = ftell(file);
			precacheTexture(material->tex_name,file);
			fseek(file,loc,SEEK_SET);*/
			//We don't precache textures anymore- they should be local, so screw it
			break;
		case MAT_ENVIRONMENT:
			//Anyone know what this is for?
			fread(&material->env_type, 1, 1, file);
			material->env_name = new char[nlen - 1];
			fread(material->env_name, 1, nlen - 1, file);
			break;
		case MAT_AMBIENT:
			fread(&material->amb_r, floatSize(), 1, file);
			fread(&material->amb_g, floatSize(), 1, file);
			fread(&material->amb_b, floatSize(), 1, file);
			break;
		case MAT_DIFFUSE:
			fread(&material->dif_r, floatSize(), 1, file);
			fread(&material->dif_g, floatSize(), 1, file);
			fread(&material->dif_b, floatSize(), 1, file);
			break;
		case MAT_SPECULAR:
			fread(&material->spe_r, floatSize(), 1, file);
			fread(&material->spe_g, floatSize(), 1, file);
			fread(&material->spe_b, floatSize(), 1, file);
			fread(&material->spe_p, floatSize(), 1, file);
			break;
		case MAT_EMISSIVE:
			fread(&material->emi_r, floatSize(), 1, file);
			fread(&material->emi_g, floatSize(), 1, file);
			fread(&material->emi_b, floatSize(), 1, file);
			break;
		case MAT_OPACITY:
			fread(&material->opa_r, floatSize(), 1, file);
			fread(&material->opa_g, floatSize(), 1, file);
			fread(&material->opa_b, floatSize(), 1, file);
			break;
		case MAT_RAMP:
			material->ramp_name = new char[nlen];
			fread(material->ramp_name, 1, nlen, file);
			break;
		case SRF_SPECIAL:
         {
			   //Awesome!
			   ran = new char[nlen];
			   fread(ran,1,nlen,file);
            char *immuneParam = strstr(ran, "IMMUNE");
			   if (immuneParam != NULL)
			   {
				   material->isImmune = true;
			   }
			   delete [] ran;
			   break;
         }
		default:
			MessageBox(NULL,_T("UNKNOWN MATERIAL ENTRY"),_T("PROBLEM"),MB_OK);
			if (!feof(file))
			{
				fseek(file, nlen, SEEK_CUR);
			}
		}
	}

	if (material->name == NULL)
	{
		//This material is nameless?
		if (material->env_name)
		{
			delete [] material->env_name;
		}

		if (material->tex_name)
		{
			delete [] material->tex_name;
		}

		if (material->ramp_name)
		{
			delete [] material->ramp_name;
		}

		delete material;
		return false;
	} else
	{
		//If there's no material, set it to null.
		if (material->tex_type != 2)
		{
			if (material->tex_name != NULL)
			{
				delete [] material->tex_name;
			}
			material->tex_type = 2;
			material->tex_name = new char[5];
			strcpy_s(material->tex_name, 5, "NULL");
		}
		//Add material
		string fullMatName = string(filename);
		fullMatName += string(":");
		fullMatName += string(material->name);
		mMaterials.insert(pair<string, DivMaterial*>(fullMatName,material));
		return true;
	}
}

void VGCDivLoader::precacheMaterial(char *name)
{
	FILE *matFile = NULL;

	//Get material file name
	char *colon = strstr(name, ":");
	int len = colon - name;
	char *fname = new char[(len + 5)];
	char *matFileName = new char[len + 1];
	strncpy(matFileName,name,len);
	matFileName[len] = 0;
	strncpy(fname, name, len);
	fname[len] = 0;
	strcat_s(fname,len + 5, ".BMF");

	//Get actual material name
	int matNameLen = strlen(name) - len;
	char *mname = new char[matNameLen];
	strcpy_s(mname, matNameLen, colon + 1);

	if (mMaterials.find(string(name)) != mMaterials.end())
	{
		//Already precached
		return;
	}

	//Open the BMF file
	int fullDirLen = strlen(fname) + 1;
	char *fulldir = new char[fullDirLen];
	strcpy_s(fulldir, fullDirLen, fname);
	delete [] fname;

	if (fopen_s(&matFile,fulldir,"rb") != 0)
	{
		MessageBox(NULL,_T("PANIC MAT FILE FAILED TO OPEN"),_T("SHIT"),MB_OK);
	}

	delete [] fulldir;

	//Parse the file
	char id[8];
	
	fread(&id, 8, 1, matFile);

	//Compare first 8 bits to make sure it's a biz2 file
	if (strncmp(id,"DIV-BIZ2",8) != 0)
	{
		fclose(matFile);
		MessageBox(NULL, _T("PANIC MAT FILE WAS NOT MAT FILE"),_T("Oh no"),MB_OK);
	}

	unsigned int blen;
	BIZ_IDENTITIES ident;
	bool done = false;
	while(!feof(matFile))
	{
		//Retrieve ID and length of upcoming block
		getBlock(matFile, ident, blen);

		switch(ident)
		{
		case MATERIAL:
			//This is the only relevant thing right now
			parseMaterial(matFile, blen,matFileName);
			break;
		case RAMP:
			parseRamp(matFile,blen);
			break;
		case TEXTURE:
			parseTexture(matFile, blen);
			break;
		default:
			if (!feof(matFile))
			{
				fseek(matFile,blen,SEEK_CUR);
			}
		}
	}
	delete [] mname;
	delete [] matFileName;
}

void VGCDivLoader::parseRamp(FILE *file, unsigned int len)
{
	BIZ_IDENTITIES ident;
	unsigned int nlen;

	DivRamp *ramp = new DivRamp;
	memset(ramp, 0, sizeof(DivRamp));

	char *name;

	while(len > 0)
	{
		len -= getBlock(file, ident, nlen);
		len -= nlen;

		switch(ident)
		{
		case GEN_NAME:
			name = new char[nlen];
			fread(name, 1, nlen, file);

			if (mRamps.find(string(name)) != mRamps.end())
			{
				delete [] name;
				delete ramp;
				fseek(file,len,SEEK_CUR);
				return;
			}
			break;
		case GEN_SCOPE:
			if (nlen > 0)
			{
				fseek(file, nlen, SEEK_CUR);
			}
			break;
		case RMP_DATA:
			fread(&ramp->r1, floatSize(), 1, file);
			fread(&ramp->g1, floatSize(), 1, file);
			fread(&ramp->b1, floatSize(), 1, file);
			fread(&ramp->r2, floatSize(), 1, file);
			fread(&ramp->g2, floatSize(), 1, file);
			fread(&ramp->b2, floatSize(), 1, file);
			break;
		default:
			MessageBox(NULL, _T("UNRECOGNIZED RAMP BLOCK"), _T("PROBLEM"), MB_OK);
			if (nlen > 0)
			{
				fseek(file, nlen, SEEK_CUR);
			}
			break;
		}
	}

	mRamps[string(name)] = ramp;
	delete [] name;
}

void VGCDivLoader::parseObject(FILE *file, unsigned int len, char *fileName)
{
	BIZ_IDENTITIES ident;
	unsigned int nlen;

   DivObject *obj = new DivObject;
   obj->objName = new char[strlen(fileName) + 1];
   strcpy_s(obj->objName, strlen(fileName) + 1, fileName);

   while(len > 0)
	{
		len -= getBlock(file, ident,nlen);
		len -= nlen;
		
		switch(ident)
		{
			//Set the master set of surface props
		case GEN_NAME:
		case SRF_F_MATERIAL:
		case SRF_B_MATERIAL:
		case SRF_PLANE:
		case SRF_DRAWMODE:
		case SRF_DECAL:
		case SRF_FACETED:
		case SRF_VERTEX:
		case SRF_SPECIAL:
		case SRF_LOCK:
			parseSurface(file, ident,nlen,mMasterProps);
			break;
		case LOD:
			parseLOD(obj, file, nlen);
			break;
		case PATCH:
			parsePatch(obj, file, nlen);
			break;
		}
	}

	VGCDivLoader::mObjects.insert(std::pair<string, DivObject*>(string(obj->objName), obj));

}

void VGCDivLoader::parseLOD(DivObject *obj, FILE *file, unsigned int len)
{
	//LOD's are only relevant if it's the closest LOD level,
	//and the only relevant part are the patches
	BIZ_IDENTITIES ident;
	unsigned int nlen;
	bool parse = false;

	while(len > 0)
	{
		len -= getBlock(file, ident,nlen);
		len -= nlen;
		
		switch(ident)
		{
		case LOD_DISTANCE:
			float in, out;
			fread(&in,floatSize(),1,file);
			fread(&out,floatSize(),1,file);

			if (in < 5.0f)
			{
				//Don't even bother if this isn't a seriously close-up LOD
				parse = true;
			}
			break;
		case PATCH:
			if (parse)
			{
				//If this is the right LOD, parse patches
				parsePatch(obj, file, nlen);
			} else
			{
				fseek(file,nlen,SEEK_CUR);
			}
			break;
		default:
			fseek(file,nlen,SEEK_CUR);
		}
	}
}

void VGCDivLoader::parsePatch(DivObject *obj, FILE *file, unsigned int len)
{
	BIZ_IDENTITIES ident;
	unsigned int nlen;
	bool parse = false;

   DivPatch *patch = new DivPatch;
   patch->surface = mMasterProps;

	int *indices;
	DivVertex *verts;

	while(len > 0)
	{
		len -= getBlock(file, ident,nlen);
		len -= nlen;

		int n;
		
		switch(ident)
		{
		case GEN_NAME:
		case SRF_F_MATERIAL:
		case SRF_B_MATERIAL:
		case SRF_PLANE:
		case SRF_DRAWMODE:
		case SRF_DECAL:
		case SRF_FACETED:
		case SRF_VERTEX:
		case SRF_SPECIAL:
		case SRF_LOCK:
			//Edit our patch's surface props
			parseSurface(file, ident,nlen,patch->surface);
			break;
		case POLYGON:
		case POLYSTRIP:
			//Polystrip is a tri fan.  Incidentally, we're treating
			//>3 sided polys like tri fans
			nlen = 0;
			while (!(ident & 0x80))
			{
				//Poly lists are the only relevant blocks
				if (nlen > 0)
				{
					fseek(file,nlen,SEEK_CUR);
				}
				getBlock(file, ident,nlen);
			}

			//Parse list of polys
			verts = parseVList(file, nlen,(unsigned char)(ident & 0x7F),n);

			indices = new int[n];
			for (int i=0; i<n; i++)
			{
				//Read in vertices to a list of indices
				indices[i] = getVertex(verts[i], obj);
			}
			//addPolygon is what breaks down our list of indices down
			//into multiple tris
			addPolygon(indices,n,patch);

			delete [] verts;
			delete [] indices;

			break;
		case TRISTRIP:
			nlen = 0;
			while (!(ident & 0x80))
			{
				if (nlen > 0)
				{
					fseek(file,nlen,SEEK_CUR);
				}
				getBlock(file, ident,nlen);
			}

			verts = parseVList(file, nlen,(unsigned char)(ident & 0x7F),n);

			indices = new int[n];
			for (int i=0; i<n; i++)
			{
				//Read in vertices
				indices[i] = getVertex(verts[i], obj);
			}

			for (int i=0; i < n-2; i++)
			{
				//For every tri in the strip, add the tri
				addPolygon(indices,3,patch);
			}

			delete [] verts;
			delete [] indices;
			break;
		case PMESH:
			//PMESHES ARE HARD
			parsePMesh(file, nlen,patch, obj);
			break;
		case SPHERE_LIST:
			{
				unsigned int inlen;
				BIZ_IDENTITIES inident;
				//Skip the width, read the verts
				patch->surface.drawSpheres = true;
				vector<DivVertex> verts;

				while (nlen > 0)
				{
					nlen -= getBlock(file, inident, inlen);
					nlen -= inlen;
					if (inident == SPHERE)
					{
						for (int i=0; i < (inlen/(floatSize()*4)); i++)
						{
							float x,y,z,radius;
							fread(&x,floatSize(),1,file);
							fread(&y,floatSize(),1,file);
							fread(&z,floatSize(),1,file);
							fread(&radius,floatSize(),1,file);
							DivVertex vert;
							vert.x = x * this->mHeaderInfo.scale;
							vert.y = y * this->mHeaderInfo.scale;
							vert.z = z * this->mHeaderInfo.scale;
							vert.mask = 0;
							verts.insert(verts.end(),vert);
						}
					} else if (inlen > 0)
					{
						fseek(file,nlen,SEEK_CUR);
					}
				}

				for (int i=0; i < verts.size(); i++)
				{
					getVertex(verts[i], obj);
				}
				//setupDrawOps(props);
				break;
			}
		case LINE_LIST:
			//Ignore these
			fseek(file,nlen,SEEK_CUR);
			break;
		case TEXT:
			//Ignore these
			fseek(file,nlen,SEEK_CUR);
			break;
		default:
			cout << "UNKNOWN PATCH VAR " << ident << endl;
		}
	}

   obj->patches.insert(obj->patches.end(), patch);

}

void VGCDivLoader::parseSurface(FILE *file, BIZ_IDENTITIES ident, unsigned int len, SurfaceProps &props)
{
	//These are really straightforward- just move the value
	//from the file to the SurfaceProps struct
	switch(ident)
	{
	case GEN_NAME:
		props.name = new char[len];
		fread(props.name,1,len,file);
		break;
	case SRF_F_MATERIAL:
		fread(&props.f_material_type,1,1,file);
      props.faceted |= 0x02;
		if (len > 1)
		{
			props.f_material_name = new char[len-1];
			fread(props.f_material_name,1,len-1,file);
			precacheMaterial(props.f_material_name);
		}
		break;
	case SRF_B_MATERIAL:
		fread(&props.b_material_type,1,1,file);
      props.faceted |= 0x01;
		if (len > 1)
		{
			props.b_material_name = new char[len-1];
			fread(props.b_material_name,1,len-1,file);
			precacheMaterial(props.b_material_name);
		}
		break;
	case SRF_PLANE:
		fread(&props.plane,1,1,file);
		break;
	case SRF_DRAWMODE:
		fread(&props.drawmode,1,1,file);
		fread(&props.drawmode_width,1,1,file);
		break;
	case SRF_DECAL:
		fread(&props.decal_offset,4,1,file);
		break;
	case SRF_FACETED:
		fread(&props.faceted,1,1,file);
		break;
	case SRF_VERTEX:
		fread(&props.vertex_mask,1,1,file);
		break;
	case SRF_SPECIAL:
		props.specialInst = new char[len];
		fread(props.specialInst,1,len,file);

		break;
	case SRF_LOCK:
		fread(&props.locks,1,1,file);
		break;
	default:
		cout << "UNKNOWN SURFACE VAR " << ident << endl;
	}
}

void VGCDivLoader::parsePMesh(FILE *file, unsigned int len, DivPatch *patch, DivObject *obj)
{
	//PMeshes are a list of vertices, and a list of vertex indices
	//The pmeshes default to 3 side per poly, but can be set different
	BIZ_IDENTITIES ident;
	unsigned int nlen;
	bool parse = false;

	std::vector<ConList *> conLists;
	DivVertex *vertList;

	while(len > 0)
	{
		len -= getBlock(file, ident,nlen);
		len -= nlen;

		ConList *list;
		
		switch(ident)
		{
		case PMESH_CONLIST:
			//old conlists are tris only
			list = new ConList;
			list->sideCount = 3;
			list->numIndices = nlen / 4;
			list->indices = new int[list->numIndices];
			fread(list->indices,4,list->numIndices,file);
			conLists.insert(conLists.begin(),list);
			break;
		case PMESH_CONLIST_NEW:
			//Newer ones specify the sideage of the polys
			list = new ConList;
			fread(&list->sideCount,1,1,file);
			list->numIndices = (nlen-1) / 4;
			list->indices = new int[list->numIndices];
			fread(list->indices,4,list->numIndices,file);
			conLists.insert(conLists.begin(),list);
			break;
		default:
			if (ident & 0x80)
			{
				//Read in verts like normal
				int n;
				vertList = parseVList(file, nlen,(unsigned char)(ident & 0x7F), n);
			} else
			{
				cout << "UNKNOWN PMESH VAR " << ident << endl;
				fseek(file,nlen,SEEK_CUR);
			}
		}
	}

	while(!conLists.empty())
	{
		//For each connection list, add polygons from the conlist
		//and destroy the connection list

		ConList *list = *conLists.begin();

		//Replace local vertex indexes with global ones
		for (int i=0; i < list->numIndices; i++)
		{
			list->indices[i] = getVertex(vertList[list->indices[i]], obj);
		}

		//Add the individual polygons
		for (int i=0; i < list->numIndices; i += list->sideCount)
		{
			addPolygon(list->indices + i,list->sideCount, patch);
		}

		delete [] list->indices;
		delete list;
		conLists.erase(conLists.begin());
	}
	delete [] vertList;
}

DivVertex *VGCDivLoader::parseVList(FILE *file, unsigned int len, unsigned char mask, int &n)
{
	//The passed-in mask is the format of the vertices, and
	//determines the in-file structure

	//Figure out the size-per-vertex
	int size = floatSize() * 3;

	if (mask & 0x01)
	{
		//Normals
		size += floatSize() * 3;
	}

	if (mask & 0x02)
	{
		//RGB
		size += floatSize() * 4;
	} else if (mask & 0x04)
	{
		//Luminance
		size += floatSize() * 2;
	}

	if (mask & 0x08)
	{
		//2D Tex
		size += floatSize() * 2;
	} else if (mask & 0x10)
	{
		//3D Tex
		size += floatSize() * 3;
	}

	//Use the size-per-vertex to derive the number of vertices
	n = len / size;

	DivVertex *buffer = new DivVertex[n];

	//Read in each vertex
	for (int i=0; i < n; i++)
	{
		buffer[i].mask = mask;
		fread(&buffer[i].x,floatSize(),1,file);
		fread(&buffer[i].y,floatSize(),1,file);
		fread(&buffer[i].z,floatSize(),1,file);
		//buffer[i].z = -buffer[i].z;

		buffer[i].x *=  this->mHeaderInfo.scale;
		buffer[i].y *= this->mHeaderInfo.scale;
		buffer[i].z *= this->mHeaderInfo.scale;
		if (mask & 0x01)
		{
			//Normals
			fread(&buffer[i].Nx,floatSize(),1,file);
			fread(&buffer[i].Ny,floatSize(),1,file);
			fread(&buffer[i].Nz,floatSize(),1,file);
		} else
		{
			buffer[i].Nx = 0;
			buffer[i].Ny = 0;
			buffer[i].Nz = 0;
		}

		if (mask & 0x02)
		{
			//RGB
			fread(&buffer[i].r,floatSize(),1,file);
			fread(&buffer[i].g,floatSize(),1,file);
			fread(&buffer[i].b,floatSize(),1,file);
		}

		if (mask & 0x04)
		{
			//Luminance
			fread(&buffer[i].l,floatSize(),1,file);
		}

		if (mask & 0x06)
		{
			//RGB or Luminance
			fread(&buffer[i].a,floatSize(),1,file);
		}

		if (mask & 0x18)
		{
			//2D or 3D texture
			fread(&buffer[i].u,floatSize(),1,file);
			//buffer[i].u = 1.0f - buffer[i].u;
			fread(&buffer[i].v,floatSize(),1,file);
		}

		if (mask & 0x10)
		{
			//3D texture
			fread(&buffer[i].w,floatSize(),1,file);
		}
	}

	return buffer;
}

int VGCDivLoader::getBlock(FILE *file, BIZ_IDENTITIES &id, unsigned int &len)
{
	//BIZ_IDENTTAG is ryan's structure that mimics the in-file structure
	//of the first two bytes of a block
	BIZ_IDENTTAG ident;

	fread(&ident,sizeof(BIZ_IDENTTAG),1,file);

	//Get ID value
	int idVal = ident.identity;
	idVal = (ident.configBit)?(idVal+2000):idVal;
	id = (BIZ_IDENTITIES)idVal;

	//Get File Length
	if (ident.headerSize == 2)
	{
		fread(&len,4,1,file);
		return 6;
	} else if (ident.headerSize == 1)
	{
		unsigned short size;
		fread(&size,2,1,file);
		len = size;
		return 4;
	} else
	{
		unsigned char size;
		fread(&size,1,1,file);
		len = size;
		return 3;
	}
}

int VGCDivLoader::floatSize()
{
	return 4;
}

int VGCDivLoader::getVertex(DivVertex vert, DivObject *obj)
{
	//Try and find vertices, if the vertex doesn't exist, add it
	std::vector<DivVertex>::iterator it = obj->vertices.begin();
	int i=0;

	while(it != obj->vertices.end() && memcmp(&(*it),&vert,sizeof(DivVertex)) != 0) {++it; ++i;}

	if (it == obj->vertices.end())
	{
		obj->vertices.insert(obj->vertices.end(),vert);
	}

	//Return the vertex's index in the VGCDivLoader's vertex list
	return i;
}

void VGCDivLoader::addPolygon(int *indices, int numIndices, DivPatch *patch)
{
	//Break the polygon down into a triangle fan and add it to the tris
	char facets = patch->surface.faceted;

	for (int i=0; i < numIndices - 2; i++)
	{
		if (facets & 0x02)
		{
			Triangle tri;
			tri.indices[2] = indices[0];
			tri.indices[1] = indices[i+1];
			tri.indices[0] = indices[i+2];
         patch->frontTriangles.insert(patch->frontTriangles.end(),tri);
		}

		if (facets & 0x01)
		{
			Triangle tri;
			tri.indices[0] = indices[0];
			tri.indices[1] = indices[i+1];
			tri.indices[2] = indices[i+2];
			patch->backTriangles.insert(patch->backTriangles.end(),tri);
		}
	}
}

