/*
 * General information:
 * 
 * For using this Library, you'll have to write a child class
 * for each of the following classes:
 * - PosScene
 * - PosSceneEntity
 * - PosModel
 * - PosGeometry
 * - PosMaterial
 * and implement all of their pure virtual methods. After that
 * you can call the 'read' method to read a Scene from a file.
 * Your implementations of the pure virtual methods will get
 * called during the loading process.
 *
 * Pay Attention to the following methods of PosScene:
 * - PosSceneEntity* getNewSceneEntity();
 * - PosModel* getNewModel();
 * - PosGeometry* getNewGeometry();
 * - PosMaterial* getNewMaterial();
 * The PoseidonScene Framework will use this methods to create 
 * new instances of your Subtypes, so nothing else but 
 * 'return new MySubType;' should be done there.
 * 
 * Object Destruction Notes:
 * 
 * The Framework will delete all *primitive* pointers passed to your 
 * implementations of the pure virtual methods immediately after your
 * method returns, e.g. the various float* for colors, vectors and
 * matrices and the occasional int*.
 * That means you'll have to copy these values into your data structures
 * as soon as you get them.
 * The Framework will, however, not delete the complex objects passed to
 * your implementations, e.g. a PosModel*.
 * BEWARE: Models, Geometries and Materials may be passed multiple times to
 * different parents (e.g. *one* PosMaterial object might be shared among 
 * an abitrary numbers of Models - this makes deleting them (and avoiding to
 * double-delete them) quite hard.
 * To help you with this, every Model, Geometry and Material is passed
 * to the PosScene (addModel, addGeometry and addMaterial methods) as well
 * as to their respective parents.
 *
 *
 * Changelog:
 *
 * 2010-11-12:
 *		Code:
 *			Material:
 *				-	Texture Filenames are no longer passed relative paths from the scene location,
 *					instead the scene directory path and the texture path are concat'd and passed as whole.
 *					So if you load the scene using a relative path from your working directory, you'll get
 *					a relative path from your working directory for each texture, if you load the scene
 *					using an absolute path, you'll get an absolute path for each texture as well.
 *
 * 2010-11-10:
 *		File Format:
 *			-	incremented version to 2
 *			-	renamed 'ks' to 'exponent' in the material
 *			-	added hasSpecular and hasEmissive flags in the Material
 *			-	changed the order of the material components a bit (specular and exponent go together)
 *		Code:
 *			Material:
 *				-	added setHasSpecular() and setHasEmissive()
 *				-	merged the setSpecularColor() and setKs() methods to setSpecularColor() with two parameters.
 *				-	renamed 'ks' to 'exponent' (formally in setKs(), now the second parameter of setSpecularColor()
 *
 * 2010-10-27:
 *		-	initial release
 */

/*
 * ----------------------------------------------------------------------------------------------------
 * Version 2:
 * ----------------------------------------------------------------------------------------------------
 *
 * PoseidonScene format (*.pos) layout. The whole thing is usually GZIPed (*.pos.gz).
 * Entities:
 *	 - Material
 *	 	A Material is a set of ambient, diffuse, (optional) emissive and (optional) specular colors (+specular exponent) 
 *		and 0..* texture filenames
 *	 - Geometry
 *	 	A Geometry is a set of Vertices (forming Triangles), along with their normals
 *	 	and (optionally) their tangents, binormals and 0..* texcoords. Each Geometry is identified by a name
 *	 - Model
 *	 	A Model is a set of Geometries, identified by a name, each having it's own Material
 *	 - SceneEntity
 *	 	A SceneEntity is a set of Models, each having it's own Transformation Matrix. Furthermore
 *		the SceneEntity as whole has a Transformation Matrix as well, and is identified by a name
 *
 * #----------
 * # Header
 * #----------
 * p0531d0nm0d3l					char[13]						Magic Bytes
 * version							int								The current Version of this Format (2)
 * 
 * #----------
 * # Materials
 * #----------
 * numMaterials						int								Number of Materials in this file
 * for(numMaterials)
 * {
 *		sizeofName					int								the length of name
 * 		name						char[sizeofName]				Name of this Material (must be unique)
 * 		ambient						float[4]						ambient color
 * 		diffuse						float[4]						diffuse color
 *		hasSpecular					bool							whether this material has a specular color
 *		if(hasSpecular)
 *		{
 * 			specular				float[4]						specular color
 * 			exponent				float							specular reflecion exponent
 *		}
 *		hasEmissive					bool							whether this material has an emissive color
 *		if(hasEmissive)
 *		{
 * 			emissive				float[4]						emissive color
 *		}
 *		numTextures					int								how many textures does this have
 *		for(numTextures)
 *		{
 *			sizeofTextureFilename	int								the length of textureFilename (=0 if no texture)
 * 			textureFilename			char[sizeofTextureFilename]		texture filename
 *		}
 * }
 * 
 * #----------
 * # Geometries
 * #----------
 * numGeometries					int								Number of Geometries in this file
 * for(numGeometries)
 * {
 *		sizeofGeometryName			int								the length of geometryName
 * 		geometryName				char[sizeofGeometryName]		Name of this Geometry (must be unique)
 * 		boundingBoxMin				float[3]						Minimum Point of the bounding box
 * 		boundingBoxMax				float[3]						Maximum Point of the bounding box
 * 		boundingSphereCenter		float[3]						Center of the bounding sphere
 * 		boundingSphereRadius		float							Radius of the bounding sphere
 * 		numVertices					int								Number of Vertices of this Geometry
 * 		for(numVertices)	
 * 		{
 * 			vertex					float[3]						A Vertex
 * 		}
 * 		for(numVertices)	
 * 		{
 * 			normal					float[3]						A Normal
 * 		}
 *		hasTangents					bool							Whether we have Tangents
 *		for(numVertices)
 *		{
 *			tangent					float[3]						A Tangent
 *		}
 *		hasBinormals				bool							Whether we have Binormals
 *		for(numVertices)
 *		{
 *			binormal				float[3]						A Binormal
 *		}
 * 		numTexSets					int								The number of texture coordinate sets
 *		for(numTexSets)
 *		{
 *			dimensions				int								The number of texture coordinates in the current set (usually 2 or 3)
 *		}
 *		for(numVertices)
 *		{
 *			for(numTexSets)
 *			{
 *				texcoord			float[currentDimension]			A Texcoord
 *			}
 *		}
 * }
 * 
 * #----------
 * # Models
 * #----------
 * numModels						int								Number of Models in this file
 * for(numModels)
 * {
 *		sizeofModelName				int								the length of modelName
 * 		modelName					char[sizeofModelName]			Name of this Model (must be unique)
 * 		boundingSphereCenter		float[3]						Center of the bounding sphere
 * 		boundingSphereRadius		float							Radius of the bounding sphere
 * 		numGeoms					int								Number of Geometries in this Model
 * 		for(numGeoms)
 * 		{
 *			sizeofGeometryName		int								the length of geometryName
 * 			geometryName			char[sizeofGeometryName]		A Geometry, identified by its name
 *			sizeofMaterialName		int								the length of materialName
 * 			materialName			char[sizeofMaterialName]		Material of this Geometry, identified by its name
 * 		}
 * }
 * 
 * #----------
 * # SceneEntities
 * #----------
 * numSceneEntities					int								Number of Scene Entities in this file
 * for(numSceneEntities)
 * {
 *		sizeofSceneEntityName		int								the length of SceneEntityName
 * 		sceneEntityName				char[sizeofSceneEntityName]		Name of this SceneEntity (must be unique)
 * 		sceneTransformation			float[16]						Transformation matrix of this SceneEntity (regarding world)
 * 		numModels					int								Number of Models in this SceneEntity
 * 		for(numModels)
 * 		{
 * 			modelTransformation		float[16]						Transformation matrix of this Model (regarding the Scene Entity)
 *			sizeofModelName			int								the length of modelName
 * 			modelName				char[sizeofModelName]			A Model, identified by its name
 * 		}
 * }
 */

/*
 * ----------------------------------------------------------------------------------------------------
 * Version 1:
 * ----------------------------------------------------------------------------------------------------
 *
 * PoseidonScene format (*.pos) layout. The whole thing is usually GZIPed (*.pos.gz).
 * Entities:
 *	 - Material
 *	 	A Material is a set of ambient, diffuse, specular and emissive colors, a ks and 0..* texture filenames
 *	 - Geometry
 *	 	A Geometry is a set of Vertices (forming Triangles), along with their normals
 *	 	and (optionally) their tangents, binormals and 0..* texcoords. Each Geometry is identified by a name
 *	 - Model
 *	 	A Model is a set of Geometries, identified by a name, each having it's own Material
 *	 - SceneEntity
 *	 	A SceneEntity is a set of Models, each having it's own Transformation Matrix. Furthermore
 *		the SceneEntity as whole has a Transformation Matrix as well, and is identified by a name
 *
 * #----------
 * # Header
 * #----------
 * p0531d0nm0d3l					char[13]						Magic Bytes
 * version							int								The current Version of this Format (1)
 * 
 * #----------
 * # Materials
 * #----------
 * numMaterials						int								Number of Materials in this file
 * for(numMaterials)
 * {
 *		sizeofName					int								the length of name
 * 		name						char[sizeofName]				Name of this Material (must be unique)
 * 		ambient						float[4]						ambient color
 * 		diffuse						float[4]						diffuse color
 * 		specular					float[4]						specular color
 * 		emissive					float[4]						emissive color
 * 		ks							float							specular reflecion coefficient
 *		numTextures					int								how many textures does this have
 *		for(numTextures)
 *		{
 *			sizeofTextureFilename	int								the length of textureFilename (=0 if no texture)
 * 			textureFilename			char[sizeofTextureFilename]		texture filename
 *		}
 * }
 * 
 * #----------
 * # Geometries
 * #----------
 * numGeometries					int								Number of Geometries in this file
 * for(numGeometries)
 * {
 *		sizeofGeometryName			int								the length of geometryName
 * 		geometryName				char[sizeofGeometryName]		Name of this Geometry (must be unique)
 * 		boundingBoxMin				float[3]						Minimum Point of the bounding box
 * 		boundingBoxMax				float[3]						Maximum Point of the bounding box
 * 		boundingSphereCenter		float[3]						Center of the bounding sphere
 * 		boundingSphereRadius		float							Radius of the bounding sphere
 * 		numVertices					int								Number of Vertices of this Geometry
 * 		for(numVertices)	
 * 		{
 * 			vertex					float[3]						A Vertex
 * 		}
 * 		for(numVertices)	
 * 		{
 * 			normal					float[3]						A Normal
 * 		}
 *		hasTangents					bool							Whether we have Tangents
 *		for(numVertices)
 *		{
 *			tangent					float[3]						A Tangent
 *		}
 *		hasBinormals				bool							Whether we have Binormals
 *		for(numVertices)
 *		{
 *			binormal				float[3]						A Binormal
 *		}
 * 		numTexSets					int								The number of texture coordinate sets
 *		for(numTexSets)
 *		{
 *			dimensions				int								The number of texture coordinates in the current set (usually 2 or 3)
 *		}
 *		for(numVertices)
 *		{
 *			for(numTexSets)
 *			{
 *				texcoord			float[currentDimension]			A Texcoord
 *			}
 *		}
 * }
 * 
 * #----------
 * # Models
 * #----------
 * numModels						int								Number of Models in this file
 * for(numModels)
 * {
 *		sizeofModelName				int								the length of modelName
 * 		modelName					char[sizeofModelName]			Name of this Model (must be unique)
 * 		boundingSphereCenter		float[3]						Center of the bounding sphere
 * 		boundingSphereRadius		float							Radius of the bounding sphere
 * 		numGeoms					int								Number of Geometries in this Model
 * 		for(numGeoms)
 * 		{
 *			sizeofGeometryName		int								the length of geometryName
 * 			geometryName			char[sizeofGeometryName]		A Geometry, identified by its name
 *			sizeofMaterialName		int								the length of materialName
 * 			materialName			char[sizeofMaterialName]		Material of this Geometry, identified by its name
 * 		}
 * }
 * 
 * #----------
 * # SceneEntities
 * #----------
 * numSceneEntities					int								Number of Scene Entities in this file
 * for(numSceneEntities)
 * {
 *		sizeofSceneEntityName		int								the length of SceneEntityName
 * 		sceneEntityName				char[sizeofSceneEntityName]		Name of this SceneEntity (must be unique)
 * 		sceneTransformation			float[16]						Transformation matrix of this SceneEntity (regarding world)
 * 		numModels					int								Number of Models in this SceneEntity
 * 		for(numModels)
 * 		{
 * 			modelTransformation		float[16]						Transformation matrix of this Model (regarding the Scene Entity)
 *			sizeofModelName			int								the length of modelName
 * 			modelName				char[sizeofModelName]			A Model, identified by its name
 * 		}
 * }
 */

#pragma once
#include <map>
#include "gzstream.h"
#include "PosSceneEntity.h"
#include "PosModel.h"
#include "PosGeometry.h"
#include "PosMaterial.h"

using namespace std;

class PosScene
{
public:
	PosScene(void);

	/*
	 * Call this method to read a Scene from a File
	 * @param filename: the filename of the scene file (relative from working directory)
	 */
	bool read(string filename);

	/*
	 * Adds a Scene Entity to this Scene
	 * @param sceneEntity: the Scene Entity to add
	 */
	virtual void addSceneEntity(PosSceneEntity* sceneEntity) = 0;

	/*
	 * This is called once per model and only meant for collecting all Models
	 * for deleting them (to avoid double deletes)
	 * The Model passed here will be added into the hierachy as well (as a child
	 * of a Scene Entity).
	 * @param model A Model
	 */
	virtual void addModel(PosModel* model) = 0;

	/*
	 * This is called once per Geometry and only meant for collecting all Geometries
	 * for deleting them (to avoid double deletes)
	 * The Geometry passed here will be added into the hierachy as well (as a child
	 * of a Model).
	 * @param geometry A Geometry
	 */
	virtual void addGeometry(PosGeometry* geometry) = 0;

	/*
	 * This is called once per Material and only meant for collecting all Materials
	 * for deleting them (to avoid double deletes)
	 * The Material passed here will be added into the hierachy as well (as a child
	 * of a Model).
	 * @param material A Material
	 */
	virtual void addMaterial(PosMaterial* material) = 0;

	virtual ~PosScene(void);
protected:

	/*
	 * Used to construct a new instance of your subtype of PosSceneEntity
	 * @return a new instance of your subtype of PosSceneEntity
	 */
	virtual PosSceneEntity* getNewSceneEntity() = 0;

	/*
	 * Used to construct a new instance of your subtype of PosModel
	 * @return a new instance of your subtype of PosModel
	 */
	virtual PosModel* getNewModel() = 0;

	/*
	 * Used to construct a new instance of your subtype of PosGeometry
	 * @return a new instance of your subtype of PosGeometry
	 */
	virtual PosGeometry* getNewGeometry() = 0;

	/*
	 * Used to construct a new instance of your subtype of PosMaterial
	 * @return a new instance of your subtype of PosMaterial
	 */
	virtual PosMaterial* getNewMaterial() = 0;

private:
	string sceneDirectory;
	string filename;

	int readHeader(igzstream &iStream);
	
	bool read_v1(igzstream &iStream);
	PosMaterial* readMaterial_v1(igzstream &iStream);
	PosGeometry* readGeometry_v1(igzstream &iStream);
	PosModel* readModel_v1(igzstream &iStream, map<string,PosGeometry*> geometries, map<string,PosMaterial*> materials);
	PosSceneEntity* readSceneEntity_v1(igzstream &iStream, map<string,PosModel*> models);

	bool read_v2(igzstream &iStream);
	PosMaterial* readMaterial_v2(igzstream &iStream);
	PosGeometry* readGeometry_v2(igzstream &iStream);
	PosModel* readModel_v2(igzstream &iStream, map<string,PosGeometry*> geometries, map<string,PosMaterial*> materials);
	PosSceneEntity* readSceneEntity_v2(igzstream &iStream, map<string,PosModel*> models);
};