#include <iostream>
#include <map>
#include <windows.h>
#include <sys/stat.h>
#include <sstream>
#include <fstream>
#include <math.h>
#include "gzstream.h"
#include "OgreScene.h"
#include "xmlParser.h"
#include "Miniball.h"
#include "Matrix44.h"

using namespace std;

template <class T> void from_string(T& t, const string& s, const T& defaultValue, ios_base& (*f)(ios_base&))
{
	std::istringstream iss(s);
	if((iss >> f >> t).fail())
		t = defaultValue;
}

template <class T> void from_string(T& t, const char* s, const T& defaultValue, ios_base& (*f)(ios_base&))
{
	if(s)
		from_string<T>(t, string(s), defaultValue, f);
	else
		t = defaultValue;
}

bool fileExists(string strFilename)
{
	struct stat stFileInfo;
	bool blnReturn;
	int intStat;

	// Attempt to get the file attributes
	intStat = stat(strFilename.c_str(),&stFileInfo);
	if(intStat == 0)
	{
		// We were able to get the file attributes
		// so the file obviously exists.
		blnReturn = true;
	}
	else
	{
		// We were not able to get the file attributes.
		// This may mean that we don't have permission to
		// access the folder which contains this file. If you
		// need to do that level of checking, lookup the
		// return values of stat which will give you
		// more details on why stat failed.
		blnReturn = false;
	}

	return(blnReturn);
}

OgreSubMesh* loadSubmesh(XMLNode& submeshNode, OgreScene* scene)
{
	OgreSubMesh* submesh = new OgreSubMesh;

	// read general data
	submesh->useSharedVertices = (submeshNode.isAttributeSet("usesharedvertices") ? string(submeshNode.getAttribute("usesharedvertices")) == string("true") : false);
	submesh->use32BitIndices = (submeshNode.isAttributeSet("use32bitindexes") ? string(submeshNode.getAttribute("use32bitindexes")) == string("true") : false);
	submesh->material = (submeshNode.isAttributeSet("material") ? scene->materials[submeshNode.getAttribute("material")] : NULL);
	submesh->operationType = 
	(
		submeshNode.isAttributeSet("operationtype") ? 
		(
			string(submeshNode.getAttribute("operationtype")) == string("triangle_list") ? 
			TRIANGLE_LIST : 
			(
				string(submeshNode.getAttribute("operationtype")) == string("triangle_fan") ? 
				TRIANGLE_FAN : 
				TRIANGLE_STRIP
			)
		) : 
		TRIANGLE_LIST
	);

	// read geometry
	int numGeometries = submeshNode.nChildNode("geometry");
	int geometryIterator = 0;
	for(int geometryIndex=0; geometryIndex<numGeometries; geometryIndex++)
	{
		XMLNode geometryNode = submeshNode.getChildNode("Geometry", &geometryIterator);

		int numVertexBuffer = geometryNode.nChildNode("vertexbuffer");
		int vertexBufferIterator = 0;
		for(int vertexBufferIndex=0; vertexBufferIndex<numVertexBuffer; vertexBufferIndex++)
		{
			XMLNode vertexBufferNode = geometryNode.getChildNode("vertexbuffer", &vertexBufferIterator);
		
			if(vertexBufferIndex == 0)
			{
				submesh->hasPositions	= (vertexBufferNode.isAttributeSet("positions") ? string(vertexBufferNode.getAttribute("positions")) == string("true") : true);
				submesh->hasNormals		= (vertexBufferNode.isAttributeSet("normals") ? string(vertexBufferNode.getAttribute("normals")) == string("true") : true);
				submesh->hasTangents	= (vertexBufferNode.isAttributeSet("tangents") ? string(vertexBufferNode.getAttribute("tangents")) == string("true") : false);
				submesh->hasBinormals	= (vertexBufferNode.isAttributeSet("binormals") ? string(vertexBufferNode.getAttribute("binormals")) == string("true") : false);
				from_string<int>(submesh->tangentDimension, vertexBufferNode.getAttribute("tangent_dimensions"), 3, dec);
				int texture_coords;		from_string<int>(texture_coords, vertexBufferNode.getAttribute("texture_coords"), 0, dec);
				submesh->textureCoordinates.resize(texture_coords, 0);
				
				if(texture_coords > 0)
					from_string<int>(submesh->textureCoordinates[0], vertexBufferNode.getAttribute("texture_coord_dimensions_0"), 2, dec);
				if(texture_coords > 1)
					from_string<int>(submesh->textureCoordinates[1], vertexBufferNode.getAttribute("texture_coord_dimensions_1"), 2, dec);
				if(texture_coords > 2)
					from_string<int>(submesh->textureCoordinates[2], vertexBufferNode.getAttribute("texture_coord_dimensions_2"), 2, dec);
				if(texture_coords > 3)
					from_string<int>(submesh->textureCoordinates[3], vertexBufferNode.getAttribute("texture_coord_dimensions_3"), 2, dec);
				if(texture_coords > 4)
					from_string<int>(submesh->textureCoordinates[4], vertexBufferNode.getAttribute("texture_coord_dimensions_4"), 2, dec);
				if(texture_coords > 5)
					from_string<int>(submesh->textureCoordinates[5], vertexBufferNode.getAttribute("texture_coord_dimensions_5"), 2, dec);
				if(texture_coords > 6)
					from_string<int>(submesh->textureCoordinates[6], vertexBufferNode.getAttribute("texture_coord_dimensions_6"), 2, dec);
				if(texture_coords > 7)
					from_string<int>(submesh->textureCoordinates[7], vertexBufferNode.getAttribute("texture_coord_dimensions_7"), 2, dec);
			}

			int vertexCount = vertexBufferNode.nChildNode("vertex");
			submesh->vertices.reserve(vertexCount);
			cout << "                                                                                                    \r";
			for(int vertexIndex=0; vertexIndex<vertexCount; vertexIndex++)
			{
				cout << "Vertices: " << (vertexIndex+1) << "/" << vertexCount << "\r";
				XMLNode vertexNode = vertexBufferNode.getChildNode(vertexIndex);
				if(strcmp(vertexNode.getName(), "vertex") != 0)
					continue;
			
				OgreVertex* vertex = new OgreVertex;
				submesh->vertices.push_back(vertex);

				// vertex position
				if(vertexNode.nChildNode("position"))
				{
					XMLNode positionNode = vertexNode.getChildNode("position");
					vertex->position = new Vector3;
					from_string<float>((*vertex->position)[0], positionNode.getAttribute("x"), 0.0f, dec);
					from_string<float>((*vertex->position)[1], positionNode.getAttribute("y"), 0.0f, dec);
					from_string<float>((*vertex->position)[2], positionNode.getAttribute("z"), 0.0f, dec);
				}
			
				// vertext normal
				if(vertexNode.nChildNode("normal"))
				{
					XMLNode normalNode = vertexNode.getChildNode("normal");
					vertex->normal = new Vector3;
					from_string<float>((*vertex->normal)[0], normalNode.getAttribute("x"), 0.0f, dec);
					from_string<float>((*vertex->normal)[1], normalNode.getAttribute("y"), 0.0f, dec);
					from_string<float>((*vertex->normal)[2], normalNode.getAttribute("z"), 0.0f, dec);
				}
			
				// vertext binormal
				if(vertexNode.nChildNode("binormal"))
				{
					XMLNode binormalNode = vertexNode.getChildNode("binormal");
					vertex->binormal = new Vector3;
					from_string<float>((*vertex->binormal)[0], binormalNode.getAttribute("x"), 0.0f, dec);
					from_string<float>((*vertex->binormal)[1], binormalNode.getAttribute("y"), 0.0f, dec);
					from_string<float>((*vertex->binormal)[2], binormalNode.getAttribute("z"), 0.0f, dec);
				}
			
				// vertex tanget
				if(vertexNode.nChildNode("tangent"))
				{
					XMLNode tangentNode = vertexNode.getChildNode("tangent");
					vertex->tangent = new Vector4;
					from_string<float>((*vertex->tangent)[0], tangentNode.getAttribute("x"), 0.0f, dec);
					from_string<float>((*vertex->tangent)[1], tangentNode.getAttribute("y"), 0.0f, dec);
					from_string<float>((*vertex->tangent)[2], tangentNode.getAttribute("z"), 0.0f, dec);
					if(submesh->tangentDimension == 4)
						from_string<float>((*vertex->tangent)[3], tangentNode.getAttribute("w"), 0.0f, dec);
				}
			
				int numTexCoords = submesh->textureCoordinates.size();
				vertex->texcoords.resize(numTexCoords);
				// vertex texcoords (number of texcoords is stored in vertexbuffer->texture_coords, min:0, max:8)
				int texcoordIterator = 0;
				for(int texcoordIndex=0; texcoordIndex<numTexCoords; texcoordIndex++)
				{
					XMLNode texcoordNode = vertexNode.getChildNode("texcoord", &texcoordIterator);
					vertex->texcoords[texcoordIndex] = new Vector3;
					if(texcoordNode.isEmpty())
						continue;
			
					// the number of dimensions can be different for each set of tex coords
					int texCoordDimension = submesh->textureCoordinates[texcoordIndex];
			
					if(texCoordDimension > 0)
					{
						from_string<float>((*vertex->texcoords[texcoordIndex])[0], texcoordNode.getAttribute("u"), 0.0f, dec);
						if(texCoordDimension > 1)
						{
							from_string<float>((*vertex->texcoords[texcoordIndex])[1], texcoordNode.getAttribute("v"), 0.0f, dec);
							if(texCoordDimension > 2)
								from_string<float>((*vertex->texcoords[texcoordIndex])[2], texcoordNode.getAttribute("w"), 0.0f, dec);
						}
					}
				}
			}
		}
	}

	// read faces
	XMLNode facesNode = submeshNode.getChildNode("faces");
	int facesCount = facesNode.nChildNode("face");
	submesh->faces.reserve(facesCount);
	cout << "                                                                                                    \r";
	for(int faceIndex=0; faceIndex<facesCount; faceIndex++)
	{
		cout << "Faces: " << (faceIndex+1) << "/" << facesCount << "\r";
		XMLNode faceNode = facesNode.getChildNode(faceIndex);
		if(strcmp(faceNode.getName(), "face") != 0)
			continue;
		
		int indexV1, indexV2, indexV3;
		from_string<int>(indexV1, faceNode.getAttribute("v1"), -1, dec);
		from_string<int>(indexV2, faceNode.getAttribute("v2"), -1, dec);
		from_string<int>(indexV3, faceNode.getAttribute("v3"), -1, dec);

		OgreFace* face = new OgreFace;
		submesh->faces.push_back(face);

		face->v1 = submesh->vertices[indexV1];
		face->v2 = submesh->vertices[indexV2];
		face->v3 = submesh->vertices[indexV3];
	}

	return submesh;
}

string loadSubmeshName(XMLNode& submeshNamesNode, int submeshIndex)
{
	// submesh name
	char tempName[11];
	sprintf(tempName, "submesh_%i", submeshIndex);
	string submeshName = string(tempName);

	if(!submeshNamesNode.isEmpty())
	{
		int numSubmeshes = submeshNamesNode.nChildNode("submeshname");
		int submeshIterator = 0;
		for(int submeshnameIndex=0; submeshnameIndex<numSubmeshes; submeshnameIndex++)
		{
			XMLNode submeshname = submeshNamesNode.getChildNode("submeshname",&submeshIterator);
			int currentIndex;
			from_string<int>(currentIndex, submeshname.getAttribute("index"), -1, dec);
			if(currentIndex == submeshIndex && submeshname.isAttributeSet("name"))
				submeshName = string(submeshname.getAttribute("name"));
		}
	}
	return submeshName;
}

OgreMesh* loadMesh(string meshFilename, OgreScene* scene)
{
	OgreMesh* mesh = scene->meshes[meshFilename];
	
	if(!mesh)
	{
		mesh = new OgreMesh;
		scene->meshes[meshFilename] = mesh;

		cout << "Loading Mesh from " << meshFilename.c_str() << "." << endl;

		cout << "Parsing XML...\r";
		XMLNode root = XMLNode::parseFile(meshFilename.c_str(),"mesh", NULL);

		if(root.nChildNode("submeshes"))
		{
			XMLNode submeshesNode = root.getChildNode("submeshes");

			int numSubmeshes = submeshesNode.nChildNode("submesh");
			int submeshIterator = 0;
			for(int submeshIndex=0; submeshIndex<numSubmeshes; submeshIndex++)
			{
				// submesh node
				XMLNode submeshNode = submeshesNode.getChildNode("submesh", &submeshIterator);

				OgreSubMesh *submesh = loadSubmesh(submeshNode, scene);

				string submeshName = loadSubmeshName(root.getChildNode("submeshnames"), submeshIndex);

				submesh->index = submeshIndex;
				submesh->name = submeshName;

				// add submesh to map
				mesh->submeshes.push_back(submesh);
			}
		}
		else
		{
			delete mesh;
			mesh = NULL;
		}
	}
	return mesh;
}

OgreNode* loadOgreNode(XMLNode &sceneNodeNode, string scenePath, OgreScene* scene)
{
	OgreNode* node = new OgreNode();

	node->name = (sceneNodeNode.isAttributeSet("name") ? string(sceneNodeNode.getAttribute("name")) : string());

	if(sceneNodeNode.nChildNode("scale"))
	{
		XMLNode scaleNode = sceneNodeNode.getChildNode("scale");
		node->scale = new Vector3;
		from_string<float>((*node->scale)[0], scaleNode.getAttribute("x"), 1.0f, dec);
		from_string<float>((*node->scale)[1], scaleNode.getAttribute("y"), 1.0f, dec);
		from_string<float>((*node->scale)[2], scaleNode.getAttribute("z"), 1.0f, dec);
	}
	if(sceneNodeNode.nChildNode("position"))
	{
		XMLNode positionNode = sceneNodeNode.getChildNode("position");
		node->position = new Vector3;
		from_string<float>((*node->position)[0], positionNode.getAttribute("x"), 0.0f, dec);
		from_string<float>((*node->position)[1], positionNode.getAttribute("y"), 0.0f, dec);
		from_string<float>((*node->position)[2], positionNode.getAttribute("z"), 0.0f, dec);
	}
	if(sceneNodeNode.nChildNode("rotation"))
	{
		XMLNode rotationNode = sceneNodeNode.getChildNode("rotation");
		from_string<float>(node->qx, rotationNode.getAttribute("qx"), 0.0f, dec);
		from_string<float>(node->qy, rotationNode.getAttribute("qy"), 0.0f, dec);
		from_string<float>(node->qz, rotationNode.getAttribute("qz"), 0.0f, dec);
		from_string<float>(node->qw, rotationNode.getAttribute("qw"), 1.0f, dec);
	}
	if(sceneNodeNode.nChildNode("entity"))
	{
		XMLNode entityNode = sceneNodeNode.getChildNode("entity");
		string meshName = entityNode.isAttributeSet("name") ? string(entityNode.getAttribute("name")) : string();
		string meshFilename = entityNode.isAttributeSet("meshFile") ? string(entityNode.getAttribute("meshFile")) : string();
		bool castShadows = entityNode.isAttributeSet("castShadows") ? string(entityNode.getAttribute("castShadows")) == string("true") : false;
		bool receiveShadows = entityNode.isAttributeSet("receiveShadows") ? string(entityNode.getAttribute("receiveShadows")) == string("true") : false;
		
		meshFilename = scenePath + "models\\" + meshFilename + ".xml";

		OgreMesh* mesh = loadMesh(meshFilename, scene);

		if(mesh)
		{
			mesh->name = meshName;
			mesh->meshFilename = meshFilename;
			mesh->castShadows = castShadows;
			mesh->receiveShadows = receiveShadows;
			node->meshes.push_back(mesh);
		}
		else
		{
			delete node;
			node = NULL;
		}
	}
	else
	{
		delete node;
		node = NULL;
	}

	return node;
}

void loadMaterials(OgreScene* scene, string materialFilename)
{
	if(!fileExists(materialFilename))
		return;

	cout << "Loading Materials from " << materialFilename.c_str() << "." << endl;

	ifstream file(materialFilename.c_str());
	string line;

	OgreMaterial* currentMaterial = NULL;

	int braceCounter = 0;

	while(getline(file, line))
	{
		// trim line
		size_t startpos = line.find_first_not_of(" \t"); // Find the first character position after excluding leading blank spaces
		size_t endpos = line.find_last_not_of(" \t"); // Find the first character position from reverse af

		// if all spaces or empty return an empty string
		if((line.npos == startpos) || (line.npos == endpos))
			line = "";
		else
			line = line.substr(startpos, endpos-startpos+1);

		if(line == string(""))
			continue;

		string buf;
		stringstream ss(line);
		vector<string> tokens;
		while (ss >> buf)
			tokens.push_back(buf);

		if(tokens.size() == 1 && tokens[0] == string("{"))
		{
			braceCounter++;
			continue;
		}

		if(tokens.size() == 1 && tokens[0] == string("}"))
		{
			braceCounter--;
			continue;
		}

		if(braceCounter == 0 && tokens.size() == 2 && tokens[0] == string("material"))
		{
			currentMaterial = new OgreMaterial();
			scene->materials[tokens[1]] = currentMaterial;
			currentMaterial->name = tokens[1];
			continue;
		}

		if(braceCounter >= 3)
		{
			if(tokens.size() >= 4 && tokens[0] == string("ambient"))
			{
				//tokens[1-3] should contain 3 floats
				currentMaterial->hasAmbient = true;
				currentMaterial->ambient = new Vector4;
				from_string<float>((*currentMaterial->ambient)[0], tokens[1], 0.6f, dec);
				from_string<float>((*currentMaterial->ambient)[1], tokens[2], 0.6f, dec);
				from_string<float>((*currentMaterial->ambient)[2], tokens[3], 0.6f, dec);
				if(tokens.size() == 4)
					(*currentMaterial->ambient)[3] = 1.0f;
				else
					from_string<float>((*currentMaterial->ambient)[3], tokens[4], 1.0f, dec);
			}
			if(tokens.size() >= 4 && tokens[0] == string("diffuse"))
			{
				//tokens[1-3] should contain 3 floats
				currentMaterial->hasDiffuse = true;
				currentMaterial->diffuse = new Vector4;
				from_string<float>((*currentMaterial->diffuse)[0], tokens[1], 0.6f, dec);
				from_string<float>((*currentMaterial->diffuse)[1], tokens[2], 0.6f, dec);
				from_string<float>((*currentMaterial->diffuse)[2], tokens[3], 0.6f, dec);
				if(tokens.size() == 4)
					(*currentMaterial->diffuse)[3] = 1.0f;
				else
					from_string<float>((*currentMaterial->diffuse)[3], tokens[4], 1.0f, dec);
			}
			if(tokens.size() >= 5 && tokens[0] == string("specular"))
			{
				//tokens[1-4] should contain 4 floats
				currentMaterial->hasSpecular = true;
				currentMaterial->specular = new Vector4;
				from_string<float>((*currentMaterial->specular)[0], tokens[1], 0.0f, dec);
				from_string<float>((*currentMaterial->specular)[1], tokens[2], 0.0f, dec);
				from_string<float>((*currentMaterial->specular)[2], tokens[3], 0.0f, dec);
				if(tokens.size() == 5)
				{
					(*currentMaterial->specular)[3] = 1.0f;
					from_string<float>(currentMaterial->exponent, tokens[4], 10.0f, dec);
				}
				else
				{
					from_string<float>((*currentMaterial->specular)[3], tokens[4], 1.0f, dec);
					from_string<float>(currentMaterial->exponent, tokens[5], 10.0f, dec);
				}
			}
			if(tokens.size() >= 4 && tokens[0] == string("emissive"))
			{
				//tokens[1-3] should contain 3 floats
				currentMaterial->hasEmissive = true;
				currentMaterial->emissive = new Vector4;
				from_string<float>((*currentMaterial->emissive)[0], tokens[1], 0.0f, dec);
				from_string<float>((*currentMaterial->emissive)[1], tokens[2], 0.0f, dec);
				from_string<float>((*currentMaterial->emissive)[2], tokens[3], 0.0f, dec);
				if(tokens.size() == 4)
					(*currentMaterial->emissive)[3] = 1.0f;
				else
					from_string<float>((*currentMaterial->emissive)[3], tokens[4], 1.0f, dec);
			}
			if(braceCounter == 4 && tokens.size() >= 2 && tokens[0] == string("texture"))
			{
				// tokens[1] contains filename
				currentMaterial->hasTexture = true;
				string texturefilename = "textures/" + tokens[1];
				for(unsigned int i=2; i<tokens.size(); i++)
					texturefilename += " " + tokens[i];
				currentMaterial->textureFilename = texturefilename;
			}
		}
	}
}

OgreScene* loadOgreScene(const char* sceneFilename)
{
	if(!fileExists(string(sceneFilename)))
		return NULL;

	OgreScene* scene = new OgreScene;
	cout << "Loading Ogre Scene from " << sceneFilename << "." << endl;

	string scenePath(sceneFilename);
	size_t lastFileSeperator = scenePath.find_last_of("\\/");
	if(lastFileSeperator == scenePath.npos)
		lastFileSeperator = -1;
	scenePath = scenePath.substr(0,lastFileSeperator+1);

	string materialFilename(sceneFilename);
	size_t lastDot = materialFilename.rfind(".");	
	if(lastDot == materialFilename.npos)
		lastDot = materialFilename.size();
	materialFilename = materialFilename.substr(0, lastDot) + ".material";

	loadMaterials(scene, materialFilename);

	XMLNode root = XMLNode::parseFile(sceneFilename,"scene", NULL);

	from_string<float>(scene->unitsPerMeter, root.getAttribute("unitsPerMeter"), 1.0f, dec);

	scene->formatVersion =		(root.isAttributeSet("formatVersion")	? string(root.getAttribute("formatVersion"))								: string());
	scene->upAxis =				(root.isAttributeSet("upAxis")			? (string(root.getAttribute("upAxis")) == string("z") ? Z_AXIS : Y_AXIS)	: Y_AXIS);
	scene->minOgreVersion =		(root.isAttributeSet("minOgreVersion")	? string(root.getAttribute("minOgreVersion"))								: string());
	scene->ogreMaxVersion =		(root.isAttributeSet("ogreMaxVersion")	? string(root.getAttribute("ogreMaxVersion"))								: string());
	scene->author =				(root.isAttributeSet("author")			? string(root.getAttribute("author"))										: string());
	scene->application =		(root.isAttributeSet("application")		? string(root.getAttribute("application"))									: string());

	if(root.nChildNode("nodes"))
	{
		XMLNode nodesNode = root.getChildNode("nodes");
		int numNodes = nodesNode.nChildNode("node");
		int nodeIterator = 0;
		for(int nodeIndex=0; nodeIndex<numNodes; nodeIndex++)
		{
			XMLNode nodeNode = nodesNode.getChildNode("node",&nodeIterator);
			OgreNode* node = loadOgreNode(nodeNode, scenePath, scene);
			if(node)
				scene->nodes.push_back(node);
		}
	}

	return scene;
}

void postProcessOgre(OgreScene* scene)
{
	cout << "Processing Materials\r";
	// process materials (add default colors for missing colors)
	for(map<string, OgreMaterial*>::const_iterator it = scene->materials.begin(); it != scene->materials.end(); ++it)
	{
		OgreMaterial* mat = it->second;
		if(!mat->ambient)	mat->ambient = new Vector4(0.6f, 0.6f, 0.6f, 1.0f);
		if(!mat->diffuse)	mat->diffuse = new Vector4(0.6f, 0.6f, 0.6f, 1.0f);
	}

	// process meshes and submeshes (calculate bounding box & sphere)
	for(map<string, OgreMesh*>::const_iterator it = scene->meshes.begin(); it != scene->meshes.end(); ++it)
	{
		OgreMesh* mesh = it->second;
		Miniball<3> mb_mesh;
		int numSubmeshes = mesh->submeshes.size();
		for(int submeshIndex=0; submeshIndex<numSubmeshes; submeshIndex++)
		{
			OgreSubMesh* submesh = mesh->submeshes[submeshIndex];
			submesh->boundingBoxMin = new Vector3;
			submesh->boundingBoxMax = new Vector3;
			Miniball<3> mb_submesh;
			int numFaces = submesh->faces.size();
			for(int faceIndex=0; faceIndex<numFaces; faceIndex++)
			{
				OgreFace* face = submesh->faces[faceIndex];

				Point<3> v1;
				v1[0] = (*face->v1->position)[0];
				v1[1] = (*face->v1->position)[1];
				v1[2] = (*face->v1->position)[2];
				mb_submesh.check_in(v1);
				mb_mesh.check_in(v1);

				if((*face->v1->position)[0] < (*submesh->boundingBoxMin)[0])
					(*submesh->boundingBoxMin)[0] = (*face->v1->position)[0];
				if((*face->v1->position)[1] < (*submesh->boundingBoxMin)[1])
					(*submesh->boundingBoxMin)[1] = (*face->v1->position)[1];
				if((*face->v1->position)[2] < (*submesh->boundingBoxMin)[2])
					(*submesh->boundingBoxMin)[2] = (*face->v1->position)[2];

				if((*face->v1->position)[0] > (*submesh->boundingBoxMax)[0])
					(*submesh->boundingBoxMax)[0] = (*face->v1->position)[0];
				if((*face->v1->position)[1] > (*submesh->boundingBoxMax)[1])
					(*submesh->boundingBoxMax)[1] = (*face->v1->position)[1];
				if((*face->v1->position)[2] > (*submesh->boundingBoxMax)[2])
					(*submesh->boundingBoxMax)[2] = (*face->v1->position)[2];

				Point<3> v2;
				v2[0] = (*face->v2->position)[0];
				v2[1] = (*face->v2->position)[1];
				v2[2] = (*face->v2->position)[2];
				mb_submesh.check_in(v2);
				mb_mesh.check_in(v2);

				if((*face->v2->position)[0] < (*submesh->boundingBoxMin)[0])
					(*submesh->boundingBoxMin)[0] = (*face->v2->position)[0];
				if((*face->v2->position)[1] < (*submesh->boundingBoxMin)[1])
					(*submesh->boundingBoxMin)[1] = (*face->v2->position)[1];
				if((*face->v2->position)[2] < (*submesh->boundingBoxMin)[2])
					(*submesh->boundingBoxMin)[2] = (*face->v2->position)[2];

				if((*face->v2->position)[0] > (*submesh->boundingBoxMax)[0])
					(*submesh->boundingBoxMax)[0] = (*face->v2->position)[0];
				if((*face->v2->position)[1] > (*submesh->boundingBoxMax)[1])
					(*submesh->boundingBoxMax)[1] = (*face->v2->position)[1];
				if((*face->v2->position)[2] > (*submesh->boundingBoxMax)[2])
					(*submesh->boundingBoxMax)[2] = (*face->v2->position)[2];

				Point<3> v3;
				v3[0] = (*face->v3->position)[0];
				v3[1] = (*face->v3->position)[1];
				v3[2] = (*face->v3->position)[2];
				mb_submesh.check_in(v3);
				mb_mesh.check_in(v3);

				if((*face->v3->position)[0] < (*submesh->boundingBoxMin)[0])
					(*submesh->boundingBoxMin)[0] = (*face->v3->position)[0];
				if((*face->v3->position)[1] < (*submesh->boundingBoxMin)[1])
					(*submesh->boundingBoxMin)[1] = (*face->v3->position)[1];
				if((*face->v3->position)[2] < (*submesh->boundingBoxMin)[2])
					(*submesh->boundingBoxMin)[2] = (*face->v3->position)[2];

				if((*face->v3->position)[0] > (*submesh->boundingBoxMax)[0])
					(*submesh->boundingBoxMax)[0] = (*face->v3->position)[0];
				if((*face->v3->position)[1] > (*submesh->boundingBoxMax)[1])
					(*submesh->boundingBoxMax)[1] = (*face->v3->position)[1];
				if((*face->v3->position)[2] > (*submesh->boundingBoxMax)[2])
					(*submesh->boundingBoxMax)[2] = (*face->v3->position)[2];
			}
			
			cout << "Calculating BoundingSphere for " << mesh->name.c_str() << "::" << submesh->name.c_str() << ".\r";
			mb_submesh.build();

			Point<3> center = mb_submesh.center();
			submesh->boundingSphereCenter = new Vector3;
			(*submesh->boundingSphereCenter)[0] = (float)center[0];
			(*submesh->boundingSphereCenter)[1] = (float)center[1];
			(*submesh->boundingSphereCenter)[2] = (float)center[2];
			submesh->boundingSphereRadius = (float)sqrtl(mb_submesh.squared_radius());
		}

		cout << "Calculating BoundingSphere for " << mesh->name.c_str() << ".\r";
		mb_mesh.build();
		
		Point<3> center = mb_mesh.center();
		mesh->boundingSphereCenter = new Vector3;
		(*mesh->boundingSphereCenter)[0] = (float)center[0];
		(*mesh->boundingSphereCenter)[1] = (float)center[1];
		(*mesh->boundingSphereCenter)[2] = (float)center[2];
		mesh->boundingSphereRadius = (float)sqrtl(mb_mesh.squared_radius());
	}
	cout << "                                                                                                    \r";
}

void writeHeader(ogzstream& oStream)
{
	char magic[13] = {'p', '0', '5', '3', '1', 'd', '0', 'n', 'm', '0', 'd', '3', 'l'};
	oStream.write(reinterpret_cast<char*>(magic), 13*sizeof(char));
	int version = 2;
	oStream.write(reinterpret_cast<char*>(&version), sizeof(int));
}

void writeMaterials(OgreScene* scene, ogzstream& oStream)
{
	int numMaterials = (int) scene->materials.size();

	cout << "Writing " << numMaterials << " Materials." << endl;

	oStream.write(reinterpret_cast<char*>(&numMaterials), sizeof(int));
	for(map<string, OgreMaterial*>::const_iterator it = scene->materials.begin(); it != scene->materials.end(); ++it)
	{
		OgreMaterial* mat = it->second;

		int sizeofName = (int) mat->name.size();
		oStream.write(reinterpret_cast<char*>(&sizeofName), sizeof(int));
		oStream.write(reinterpret_cast<const char*>(mat->name.c_str()), sizeofName*sizeof(char));
		oStream.write(reinterpret_cast<char*>(mat->ambient->getData()), 4*sizeof(float));
		oStream.write(reinterpret_cast<char*>(mat->diffuse->getData()), 4*sizeof(float));
		
		bool hasSpecular = mat->specular != NULL;
		oStream.write(reinterpret_cast<char*>(&hasSpecular), sizeof(bool));
		if(hasSpecular)
		{
			oStream.write(reinterpret_cast<char*>(mat->specular->getData()), 4*sizeof(float));
			oStream.write(reinterpret_cast<char*>(&mat->exponent), sizeof(float));
		}
		
		bool hasEmissive = mat->emissive != NULL;
		oStream.write(reinterpret_cast<char*>(&hasEmissive), sizeof(bool));
		if(hasEmissive)
			oStream.write(reinterpret_cast<char*>(mat->emissive->getData()), 4*sizeof(float));

		if(mat->hasTexture)
		{
			int nrOfTextures = 1;
			oStream.write(reinterpret_cast<char*>(&nrOfTextures), sizeof(int));
			int sizeOfTextureFilename = (int) mat->textureFilename.size();
			oStream.write(reinterpret_cast<char*>(&sizeOfTextureFilename), sizeof(int));
			oStream.write(reinterpret_cast<const char*>(mat->textureFilename.c_str()), sizeOfTextureFilename*sizeof(char));
		}
		else
		{
			int nrOfTextures = 0;
			oStream.write(reinterpret_cast<char*>(&nrOfTextures), sizeof(int));
		}
	}
}

void writeGeometries(OgreScene* scene, ogzstream& oStream)
{
	map<string,OgreSubMesh*> submeshes;

	for(map<string, OgreMesh*>::const_iterator it = scene->meshes.begin(); it != scene->meshes.end(); ++it)
	{
		OgreMesh* mesh = it->second;
		for(unsigned int submeshIndex=0; submeshIndex<mesh->submeshes.size(); submeshIndex++)
		{
			OgreSubMesh* submesh = mesh->submeshes[submeshIndex];
			string meshName = mesh->meshFilename.substr(mesh->meshFilename.find_last_of("/\\")+1);
			meshName = meshName.substr(0,meshName.size()-9);
			submeshes.insert(pair<string,OgreSubMesh*>(meshName + string("_") + submesh->name, submesh));
		}
	}

	int numSubmeshes = (int) submeshes.size();

	cout << "Writing " << numSubmeshes << " Geometries." << endl;

	oStream.write(reinterpret_cast<char*>(&numSubmeshes), sizeof(int));

	for(map<string, OgreSubMesh*>::const_iterator it = submeshes.begin(); it != submeshes.end(); ++it)
	{
		string name = it->first;
		OgreSubMesh* submesh = it->second;

		int sizeofGeometryName = (int) it->first.size();
		oStream.write(reinterpret_cast<char*>(&sizeofGeometryName), sizeof(int));
		oStream.write(reinterpret_cast<const char*>(name.c_str()), sizeofGeometryName*sizeof(char));
		oStream.write(reinterpret_cast<char*>(submesh->boundingBoxMin->getData()), 3*sizeof(float));
		oStream.write(reinterpret_cast<char*>(submesh->boundingBoxMax->getData()), 3*sizeof(float));
		oStream.write(reinterpret_cast<char*>(submesh->boundingSphereCenter->getData()), 3*sizeof(float));
		oStream.write(reinterpret_cast<char*>(&submesh->boundingSphereRadius), sizeof(float));

		int numFaces = (int) submesh->faces.size();
		int numVertices = numFaces*3;
		oStream.write(reinterpret_cast<char*>(&numVertices), sizeof(int));
		
		for(int i=0; i<numFaces; i++)
		{
			oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v1->position->getData()), 3*sizeof(float));
			oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v2->position->getData()), 3*sizeof(float));
			oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v3->position->getData()), 3*sizeof(float));
		}

		for(int i=0; i<numFaces; i++)
		{
			oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v1->normal->getData()), 3*sizeof(float));
			oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v2->normal->getData()), 3*sizeof(float));
			oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v3->normal->getData()), 3*sizeof(float));
		}

		oStream.write(reinterpret_cast<char*>(&submesh->hasTangents), sizeof(bool));

		if(submesh->hasTangents)
		{
			for(int i=0; i<numFaces; i++)
			{
				oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v1->tangent->getData()), 3*sizeof(float));
				oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v2->tangent->getData()), 3*sizeof(float));
				oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v3->tangent->getData()), 3*sizeof(float));
			}
		}

		oStream.write(reinterpret_cast<char*>(&submesh->hasBinormals), sizeof(bool));

		if(submesh->hasBinormals)
		{
			for(int i=0; i<numFaces; i++)
			{
				oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v1->binormal->getData()), 3*sizeof(float));
				oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v2->binormal->getData()), 3*sizeof(float));
				oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v3->binormal->getData()), 3*sizeof(float));
			}
		}

		int numTexSets = (int) submesh->textureCoordinates.size();
		oStream.write(reinterpret_cast<char*>(&numTexSets), sizeof(int));
		if(numTexSets > 0)
		{
			oStream.write(reinterpret_cast<char*>(&submesh->textureCoordinates[0]), numTexSets*sizeof(int));
			for(int i=0; i<numFaces; i++)
			{
				for(int j=0; j<numTexSets; j++)
					oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v1->texcoords[j]->getData()), submesh->textureCoordinates[j]*sizeof(float));
				for(int j=0; j<numTexSets; j++)
					oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v2->texcoords[j]->getData()), submesh->textureCoordinates[j]*sizeof(float));
				for(int j=0; j<numTexSets; j++)
					oStream.write(reinterpret_cast<char*>(submesh->faces[i]->v3->texcoords[j]->getData()), submesh->textureCoordinates[j]*sizeof(float));
			}
		}
	}
}

void writeModels(OgreScene* scene, ogzstream& oStream)
{
	int numModels = (int) scene->meshes.size();
	
	cout << "Writing " << numModels << " Models." << endl;

	oStream.write(reinterpret_cast<char*>(&numModels), sizeof(int));
	for(map<string, OgreMesh*>::const_iterator it = scene->meshes.begin(); it != scene->meshes.end(); ++it)
	{
		OgreMesh* mesh = it->second;
		int sizeofModelName = (int) mesh->name.size();
		oStream.write(reinterpret_cast<char*>(&sizeofModelName), sizeof(int));
		oStream.write(reinterpret_cast<const char*>(mesh->name.c_str()), sizeofModelName*sizeof(char));
		oStream.write(reinterpret_cast<char*>(mesh->boundingSphereCenter->getData()), 3*sizeof(float));
		oStream.write(reinterpret_cast<char*>(&mesh->boundingSphereRadius), sizeof(float));
		int numGeoms = (int) mesh->submeshes.size();
		oStream.write(reinterpret_cast<char*>(&numGeoms), sizeof(int));
		for(int i=0; i<numGeoms; i++)
		{
			OgreSubMesh* submesh = mesh->submeshes[i];
			string meshName = mesh->meshFilename.substr(mesh->meshFilename.find_last_of("/\\")+1);
			meshName = meshName.substr(0,meshName.size()-9);
			meshName += string("_") + submesh->name;
			int sizeofGeometryName = (int) meshName.size();
			oStream.write(reinterpret_cast<char*>(&sizeofGeometryName), sizeof(int));
			oStream.write(reinterpret_cast<const char*>(meshName.c_str()), sizeofGeometryName*sizeof(char));
			int sizeofMaterialName = (int) submesh->material->name.size();
			oStream.write(reinterpret_cast<char*>(&sizeofMaterialName), sizeof(int));
			oStream.write(reinterpret_cast<const char*>(submesh->material->name.c_str()), sizeofMaterialName*sizeof(char));
		}
	}
}

void writeSceneEntities(OgreScene* scene, ogzstream& oStream)
{
	int numSceneEntities = (int) scene->nodes.size();
	
	cout << "Writing " << numSceneEntities << " SceneEntities." << endl;

	oStream.write(reinterpret_cast<char*>(&numSceneEntities), sizeof(int));
	for(int nodeIndex=0; nodeIndex<numSceneEntities; nodeIndex++)
	{
		OgreNode* node = scene->nodes[nodeIndex];
		int sizeofSceneEntityName = (int)node->name.size();
		oStream.write(reinterpret_cast<char*>(&sizeofSceneEntityName), sizeof(int));
		oStream.write(reinterpret_cast<const char*>(node->name.c_str()), sizeofSceneEntityName*sizeof(char));

		// calculate transformations
		Matrix44 translation;	translation.setPosition(*node->position);
		Matrix44 scale;			scale.setScale(*node->scale);
		Matrix44 rotation;		rotation.setRotationFromQuaternion(node->qx, node->qy, node->qz, node->qw);

		Matrix44 transform = translation*rotation*scale;
		oStream.write(reinterpret_cast<char*>(transform.getData()), 16*sizeof(float));

		int numModels = (int) node->meshes.size();
		oStream.write(reinterpret_cast<char*>(&numModels), sizeof(int));
		
		for(int meshIndex=0; meshIndex<numModels; meshIndex++)
		{
			OgreMesh* mesh = node->meshes[meshIndex];
			Matrix44 transform;
			
			oStream.write(reinterpret_cast<char*>(transform.getData()), 16*sizeof(float));
			int sizeofModelName = (int) mesh->name.size();
			oStream.write(reinterpret_cast<char*>(&sizeofModelName), sizeof(int));
			oStream.write(reinterpret_cast<const char*>(mesh->name.c_str()), sizeofModelName*sizeof(char));
		}
	}
}

bool writePoseidonScene(OgreScene* scene, string filename)
{
	ogzstream oStream(filename.c_str());

	cout << "Writing PoseidonScene to " << filename.c_str() << "." << endl;

	writeHeader(oStream);

	writeMaterials(scene, oStream);

	writeGeometries(scene, oStream);

	writeModels(scene, oStream);

	writeSceneEntities(scene, oStream);

	oStream.close();
	return true;
}

int main(int argc, char** argv)
{

#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

	if(argc >= 2)
	{
		char* sceneFilename = argv[1];
		
		timeBeginPeriod(1);
		long ogreLoadingTime = timeGetTime();
		timeEndPeriod(1);
		OgreScene* scene = loadOgreScene(sceneFilename);
		timeBeginPeriod(1);
		ogreLoadingTime = timeGetTime() - ogreLoadingTime;
		timeEndPeriod(1);
		
		if(scene)
		{
			cout << ">> Successfully loaded Ogre Scene in " << ogreLoadingTime << " ms." << endl;
			cout << ">> Post-Processing Ogre Scene." << endl;
			postProcessOgre(scene);
			
			// generate filename
			string outputFilename(sceneFilename);
			size_t lastDot = outputFilename.rfind(".");	
			if(lastDot == outputFilename.npos)
				lastDot = outputFilename.size();
			outputFilename = outputFilename.substr(0, lastDot) + ".pos.gz";

			cout << ">> Writing PoseidonScene." << endl;
			timeBeginPeriod(1);
			long poseidonWritingTime = timeGetTime();
			timeEndPeriod(1);
			bool poseidonSceneWritingSuccess = writePoseidonScene(scene, outputFilename);
			timeBeginPeriod(1);
			poseidonWritingTime = timeGetTime() - poseidonWritingTime;
			timeEndPeriod(1);

			if(poseidonSceneWritingSuccess)
				cout << ">> Successfully wrote PoseidonScene in " << poseidonWritingTime << " ms." << endl;
			else
				cout << ">> Error writing PoseidonScene." << endl;
			
			delete scene;
		}
		else
			cout << ">> Error loading Ogre Scene." << endl;
	}
	else
		cout << ">> Drag and drop Ogre Scene onto .exe!" << endl;
	
	cout << ">> Press Enter to Exit." << endl;
	getchar();
}