#include <iostream>
#include <sys/stat.h>
#include "gzstream.h"

using namespace std;

void readMaterials_v1(igzstream& iStream)
{
	int numMaterials;
	iStream.read(reinterpret_cast<char*>(&numMaterials), sizeof(int));
	cout << numMaterials << " Materials:" << endl;
	
	for(int i=0; i<numMaterials; i++)
	{
		int sizeofName;
		iStream.read(reinterpret_cast<char*>(&sizeofName), sizeof(int));
		char* name = new char[sizeofName+1];
		name[sizeofName] = 0;
		iStream.read(name, sizeofName);
		cout << "\tName: " << name << endl;
		delete[] name;
		float color[4];
		iStream.read(reinterpret_cast<char*>(color), 4*sizeof(float));
		cout << "\tAmbient: (" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << ")" << endl;
		iStream.read(reinterpret_cast<char*>(color), 4*sizeof(float));
		cout << "\tDiffuse: (" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << ")" << endl;
		iStream.read(reinterpret_cast<char*>(color), 4*sizeof(float));
		cout << "\tSpecular: (" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << ")" << endl;
		iStream.read(reinterpret_cast<char*>(color), 4*sizeof(float));
		cout << "\tEmissive: (" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << ")" << endl;
		float ks;
		iStream.read(reinterpret_cast<char*>(&ks), sizeof(float));
		cout << "\tKs: " << ks << endl;
		int numTextures;
		iStream.read(reinterpret_cast<char*>(&numTextures), sizeof(int));
		cout << "\tTextures: " << numTextures << endl;
		for(int j=0; j<numTextures; j++)
		{
			int sizeofTextureFilename;
			iStream.read(reinterpret_cast<char*>(&sizeofTextureFilename), sizeof(int));
			char* textureFilename = new char[sizeofTextureFilename+1];
			textureFilename[sizeofTextureFilename] = 0;
			iStream.read(textureFilename, sizeofTextureFilename);
			cout << "\t\t" << j << ": " << textureFilename << endl;
			delete[] textureFilename;
		}
		cout << endl;
	}
}

void readGeometries_v1(igzstream& iStream)
{
	int numGeometries;
	iStream.read(reinterpret_cast<char*>(&numGeometries), sizeof(int));
	cout << numGeometries << " Geometries:" << endl;
	for(int i=0; i<numGeometries; i++)
	{
		int sizeofGeometryName;
		iStream.read(reinterpret_cast<char*>(&sizeofGeometryName), sizeof(int));
		char* geometryName = new char[sizeofGeometryName+1];
		geometryName[sizeofGeometryName] = 0;
		iStream.read(geometryName, sizeofGeometryName);
		cout << "\tName: " << geometryName << endl;
		delete[] geometryName;

		float vector3[3];
		iStream.read(reinterpret_cast<char*>(vector3), 3*sizeof(float));
		cout << "\tBoundingBox Min: (" << vector3[0] << ", " << vector3[1] << ", " << vector3[2] << ")" << endl;
		iStream.read(reinterpret_cast<char*>(vector3), 3*sizeof(float));
		cout << "\tBoundingBox Max: (" << vector3[0] << ", " << vector3[1] << ", " << vector3[2] << ")" << endl;
		iStream.read(reinterpret_cast<char*>(vector3), 3*sizeof(float));
		cout << "\tBoundingSphere Center: (" << vector3[0] << ", " << vector3[1] << ", " << vector3[2] << ")" << endl;
		float bSphereRadius;
		iStream.read(reinterpret_cast<char*>(&bSphereRadius), sizeof(float));
		cout << "\tBoundingSphere Radius: " << bSphereRadius << endl;

		int numVertices;
		iStream.read(reinterpret_cast<char*>(&numVertices), sizeof(int));
		cout << "\tVertices: " << numVertices << endl;

		cout << "\t\t<Omitting display of " << numVertices << " Positions>" << endl;
		for(int j=0; j<numVertices; j++)
			iStream.read(reinterpret_cast<char*>(vector3), 3*sizeof(float));

		cout << "\t\t<Omitting display of " << numVertices << " Normals>" << endl;
		for(int j=0; j<numVertices; j++)
			iStream.read(reinterpret_cast<char*>(vector3), 3*sizeof(float));

		bool hasTangents;
		iStream.read(reinterpret_cast<char*>(&hasTangents), sizeof(bool));
		if(hasTangents)
		{
			cout << "\t\t<Omitting display of " << numVertices << " Tangents>" << endl;
			for(int j=0; j<numVertices; j++)
				iStream.read(reinterpret_cast<char*>(vector3), 3*sizeof(float));
		}
		else
			cout << "\t\tNo Tangents" << endl;

		bool hasBinormals;
		iStream.read(reinterpret_cast<char*>(&hasBinormals), sizeof(bool));
		if(hasBinormals)
		{
			cout << "\t\t<Omitting display of " << numVertices << " Binormals>" << endl;
			for(int j=0; j<numVertices; j++)
				iStream.read(reinterpret_cast<char*>(vector3), 3*sizeof(float));
		}
		else
			cout << "\t\tNo Binormals" << endl;

		int numTexSets;
		iStream.read(reinterpret_cast<char*>(&numTexSets), sizeof(int));
		cout << "\t\tTexture Coordinate Sets: " << numTexSets << endl;

		if(numTexSets > 0)
		{
			int* dimensions = new int[numTexSets];
	
			for(int j=0; j<numTexSets; j++)
			{
				iStream.read(reinterpret_cast<char*>(&dimensions[j]), sizeof(int));
				cout << "\t\t\tSet " << j << ": Dimension: " << dimensions[j] << endl;
			}
	
			cout << "\t\t\t<Omitting display of " << numVertices << " Texcoords with " << numTexSets << " Texcoord set(s) of the above dimensions." << endl;
	
			for(int j=0; j<numVertices; j++)
			{
				for(int k=0; k<numTexSets; k++)
				{
					for(int l=0; l<dimensions[k]; l++)
					{
						float f;
						iStream.read(reinterpret_cast<char*>(&f), sizeof(float));
					}
				}
			}
	
			delete[] dimensions;
		}

		cout << endl;
	}
}

void readModels_v1(igzstream& iStream)
{
	int numModels;
	iStream.read(reinterpret_cast<char*>(&numModels), sizeof(int));
	cout << numModels << " Models:" << endl;
	for(int i=0; i<numModels; i++)
	{
		int sizeofModelName;
		iStream.read(reinterpret_cast<char*>(&sizeofModelName), sizeof(int));
		char* modelName = new char[sizeofModelName+1];
		modelName[sizeofModelName] = 0;
		iStream.read(modelName, sizeofModelName);
		cout << "\tName: " << modelName << endl;
		delete[] modelName;

		float vector3[3];
		iStream.read(reinterpret_cast<char*>(vector3), 3*sizeof(float));
		cout << "\tBoundingSphere Center: (" << vector3[0] << ", " << vector3[1] << ", " << vector3[2] << ")" << endl;
		float bSphereRadius;
		iStream.read(reinterpret_cast<char*>(&bSphereRadius), sizeof(float));
		cout << "\tBoundingSphere Radius: " << bSphereRadius << endl;

		int numGeoms;
		iStream.read(reinterpret_cast<char*>(&numGeoms), sizeof(int));
		cout << "\tGeometries: " << numGeoms << endl;
		for(int j=0; j<numGeoms; j++)
		{
			int sizeofGeometryName;
			iStream.read(reinterpret_cast<char*>(&sizeofGeometryName), sizeof(int));
			char* geometryName = new char[sizeofGeometryName+1];
			geometryName[sizeofGeometryName] = 0;
			iStream.read(geometryName, sizeofGeometryName);
			cout << "\t\tGeometry " << j << ": " << geometryName << endl;
			delete[] geometryName;
			
			int sizeofMaterialName;
			iStream.read(reinterpret_cast<char*>(&sizeofMaterialName), sizeof(int));
			char* materialName = new char[sizeofMaterialName+1];
			materialName[sizeofMaterialName] = 0;
			iStream.read(materialName, sizeofMaterialName);
			cout << "\t\tMaterial " << j << ": " << materialName << endl;
			delete[] materialName;
		}
		
		cout << endl;
	}
}

void readSceneEntities_v1(igzstream& iStream)
{
	int numSceneEntities;
	iStream.read(reinterpret_cast<char*>(&numSceneEntities), sizeof(int));
	cout << numSceneEntities << " Scene Entities:" << endl;
	for(int i=0; i<numSceneEntities; i++)
	{
		int sizeofSceneEntityName;
		iStream.read(reinterpret_cast<char*>(&sizeofSceneEntityName), sizeof(int));
		char* sceneEntityName = new char[sizeofSceneEntityName+1];
		sceneEntityName[sizeofSceneEntityName] = 0;
		iStream.read(sceneEntityName, sizeofSceneEntityName);
		cout << "\tName: " << sceneEntityName << endl;
		delete[] sceneEntityName;

		float transformation[16];
		iStream.read(reinterpret_cast<char*>(transformation), 16*sizeof(float));
		cout << "\tTransformation:\n"
			<< "\t\t" << transformation[ 0] << "\t" << transformation[ 4] << "\t" << transformation[ 8] << "\t" << transformation[12] << "\n"
			<< "\t\t" << transformation[ 1] << "\t" << transformation[ 5] << "\t" << transformation[ 9] << "\t" << transformation[13] << "\n"
			<< "\t\t" << transformation[ 2] << "\t" << transformation[ 6] << "\t" << transformation[10] << "\t" << transformation[14] << "\n"
			<< "\t\t" << transformation[ 3] << "\t" << transformation[ 7] << "\t" << transformation[11] << "\t" << transformation[15] << endl;

		int numModels;
		iStream.read(reinterpret_cast<char*>(&numModels), sizeof(int));
		cout << "\tModels: " << numModels << endl;

		for(int j=0; j<numModels; j++)
		{
			iStream.read(reinterpret_cast<char*>(transformation), 16*sizeof(float));
			cout << "\t\tModel transformation:\n"
				<< "\t\t\t" << transformation[ 0] << "\t" << transformation[ 4] << "\t" << transformation[ 8] << "\t" << transformation[12] << "\n"
				<< "\t\t\t" << transformation[ 1] << "\t" << transformation[ 5] << "\t" << transformation[ 9] << "\t" << transformation[13] << "\n"
				<< "\t\t\t" << transformation[ 2] << "\t" << transformation[ 6] << "\t" << transformation[10] << "\t" << transformation[14] << "\n"
				<< "\t\t\t" << transformation[ 3] << "\t" << transformation[ 7] << "\t" << transformation[11] << "\t" << transformation[15] << endl;

			int sizeofModelName;
			iStream.read(reinterpret_cast<char*>(&sizeofModelName), sizeof(int));
			char* modelName = new char[sizeofModelName+1];
			modelName[sizeofModelName] = 0;
			iStream.read(modelName, sizeofModelName);
			cout << "\t\tModel name: " << modelName << endl;
			delete[] modelName;
		}

		cout << endl;
	}
}

void readMaterials_v2(igzstream& iStream)
{
	int numMaterials;
	iStream.read(reinterpret_cast<char*>(&numMaterials), sizeof(int));
	cout << numMaterials << " Materials:" << endl;
	
	for(int i=0; i<numMaterials; i++)
	{
		int sizeofName;
		iStream.read(reinterpret_cast<char*>(&sizeofName), sizeof(int));
		char* name = new char[sizeofName+1];
		name[sizeofName] = 0;
		iStream.read(name, sizeofName);
		cout << "\tName: " << name << endl;
		delete[] name;
		float color[4];
		iStream.read(reinterpret_cast<char*>(color), 4*sizeof(float));
		cout << "\tAmbient: (" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << ")" << endl;
		iStream.read(reinterpret_cast<char*>(color), 4*sizeof(float));
		cout << "\tDiffuse: (" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << ")" << endl;

		bool hasSpecular;
		iStream.read(reinterpret_cast<char*>(&hasSpecular), sizeof(bool));
		if(hasSpecular)
		{
			iStream.read(reinterpret_cast<char*>(color), 4*sizeof(float));
			cout << "\tSpecular: (" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << ")" << endl;

			float exponent;
			iStream.read(reinterpret_cast<char*>(&exponent), sizeof(float));
			cout << "\tExponent: " << exponent << endl;
		}
		else
		{
			cout << "\tNo specular." << endl;
			cout << "\tNo exponent." << endl;
		}

		bool hasEmissive;
		iStream.read(reinterpret_cast<char*>(&hasEmissive), sizeof(bool));
		if(hasEmissive)
		{
			iStream.read(reinterpret_cast<char*>(color), 4*sizeof(float));
			cout << "\tEmissive: (" << color[0] << ", " << color[1] << ", " << color[2] << ", " << color[3] << ")" << endl;
		}
		else
		{
			cout << "\tNo emissive." << endl;
		}

		int numTextures;
		iStream.read(reinterpret_cast<char*>(&numTextures), sizeof(int));
		cout << "\tTextures: " << numTextures << endl;
		for(int j=0; j<numTextures; j++)
		{
			int sizeofTextureFilename;
			iStream.read(reinterpret_cast<char*>(&sizeofTextureFilename), sizeof(int));
			char* textureFilename = new char[sizeofTextureFilename+1];
			textureFilename[sizeofTextureFilename] = 0;
			iStream.read(textureFilename, sizeofTextureFilename);
			cout << "\t\t" << j << ": " << textureFilename << endl;
			delete[] textureFilename;
		}
		cout << endl;
	}
}

void readGeometries_v2(igzstream& iStream)
{
	// nothing changed here from v1 to v2
	readGeometries_v1(iStream);
}

void readModels_v2(igzstream& iStream)
{
	// nothing changed here from v1 to v2
	readModels_v1(iStream);
}

void readSceneEntities_v2(igzstream& iStream)
{
	// nothing changed here from v1 to v2
	readSceneEntities_v1(iStream);
}

bool read(igzstream& iStream)
{
	char magicReference[13] = {'p', '0', '5', '3', '1', 'd', '0', 'n', 'm', '0', 'd', '3', 'l'};
	char magic[13];
	iStream.read(magic, 13*sizeof(char));
	if(memcmp(magic, magicReference, 13*sizeof(char)) != 0)
	{
		cerr << "Invalid Header found!" << endl;
		return false;
	}

	int version;
	iStream.read(reinterpret_cast<char*>(&version), sizeof(int));
	cout << "Found PoseidonScene version " << version << "." << endl;

	switch(version)
	{
	case 1:
		readMaterials_v1(iStream);
		readGeometries_v1(iStream);
		readModels_v1(iStream);
		readSceneEntities_v1(iStream);
		break;
	case 2:
		readMaterials_v2(iStream);
		readGeometries_v2(iStream);
		readModels_v2(iStream);
		readSceneEntities_v2(iStream);
	}
	return true;
}

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);
}

int main(int argc, char** argv)
{
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

	if(argc < 2)
	{
		cout << "Drag and drop poseidon scene onto .exe!" << endl;
		return -1;
	}

	char* sceneFilename = argv[1];

	if(fileExists(string(sceneFilename)))
	{
		igzstream iStream(sceneFilename);
		read(iStream);
		iStream.close();
	}
	else
		cout << "File '" << sceneFilename << "' not found" << endl;

	cout << "Press Enter to Exit!" << endl;
	getchar();
	return 0;
}