/*
---------------------------------------------------------------------------
Direct3D AssimpRenderer by ponx
---------------------------------------------------------------------------

This code is heavily based on the "AssimpView" project of the Open Asset Import Library (ASSIMP).
http://assimp.sourceforge.net

/*
---------------------------------------------------------------------------
Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------------

Copyright (c) 2006-2008, ASSIMP Development Team

All rights reserved.

Redistribution and use of this software in source and binary forms, 
with or without modification, are permitted provided that the following 
conditions are met:

* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.

* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.

* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/




#pragma once

class SceneAnimator;

#include <stdlib.h>
#include <aiScene.h>

#define NO_OF_DIRECTIONAL_LIGHTS	1

namespace boost
{
	namespace serialization
	{
		class access;
	}
}

namespace AssimpRendererD3d
{
	class AssetHelper;
}

//{
	/// This class is the Direct3D representation of a single 3D Asset that was imported using ASSIMP.
	/** Instances of this class are created when you use AssimpRenderer's functions for importing or saving Assets.
	*	The class comes with some setters to alter the lighting parameters of the Asset.
	*/
class AssimpRendererD3d::AssetHelper
	{
		friend class boost::serialization::access;
		friend class AssimpRenderer;
		friend class CMaterialManager;
		friend class CMeshRenderer;

	public:
			bool SetAmbientLight(D3DCOLOR ambientLight);
			bool SetDirectionalLight(unsigned int lightIndex, D3DCOLOR lightColor, const aiVector3D& lightDir);
			bool SetDirectionalLightDirection(unsigned int lightIndex, const aiVector3D& lightDir);
			bool SetDirectionalLightColor(unsigned int lightIndex, D3DCOLOR lightColor);
			bool SetSpecularShininessModificator(float modificator);
			bool SetSpecularStrengthModificator(float modificator);

			bool operator == (const AssetHelper& otherAsset);
			bool operator != (const AssetHelper& otherAsset);

	private:	
			enum 
			{
				// the original normal set will be used
				ORIGINAL = 0x0u,

				// a smoothed normal set will be used
				SMOOTH = 0x1u,

				// a hard normal set will be used
				HARD = 0x2u,
			};

			// default constructor
			AssetHelper()
			{
				mAnimator = NULL;
				apcMeshes = NULL;
				pcScene = NULL;
				numMeshes = 0;

				// PNX:
				lightDirs[0] = aiVector3D(1.0f, 1.0f, 1.0f);
				lightDirs[1] = aiVector3D(1.0f, 1.0f, 1.0f);
				lightColors[0] = D3DCOLOR(D3DCOLOR_XRGB(255, 255, 255));
				lightColors[1] = D3DCOLOR(D3DCOLOR_XRGB(255, 255, 255));
				lightDirIntensity = 1.0f;
				lightAmbient =	D3DCOLOR_XRGB(127, 127, 127);
				lightShininessModificator = 1.0f;
				lightSpecularStrengthModificator = 1.0f;
			}

			template<class Archive>
			void serialize(Archive & ar, const unsigned int version);

			//---------------------------------------------------------------
			// default vertex data structure
			// (even if tangents, bitangents or normals aren't
			// required by the shader they will be committed to the GPU)
			//---------------------------------------------------------------
			struct Vertex
			{
				aiVector3D vPosition;
				aiVector3D vNormal;

				D3DCOLOR dColorDiffuse;
				aiVector3D vTangent;
				aiVector3D vBitangent;
				aiVector2D vTextureUV;
				aiVector2D vTextureUV2;
				unsigned char mBoneIndices[4];
				unsigned char mBoneWeights[4]; // last Weight not used, calculated inside the vertex shader

				/** Returns the vertex declaration elements to create a declaration from. */
				static D3DVERTEXELEMENT9* GetDeclarationElements() 
				{
					static D3DVERTEXELEMENT9 decl[] =
					{
						{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
						{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
						{ 0, 24, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
						{ 0, 28, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 },
						{ 0, 40, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0 },
						{ 0, 52, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
						{ 0, 60, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
						{ 0, 68, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDINDICES, 0 },
						{ 0, 72, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0 },
						D3DDECL_END()
					};

					return decl;
				}

				Vertex ()
					: 
					vPosition       (0.0f, 0.0f, 0.0f),
					vNormal         (0.0f, 0.0f, 0.0f),
					dColorDiffuse   (0),
					vTangent        (0.0f, 0.0f, 0.0f),
					vBitangent      (0.0f, 0.0f, 0.0f),
					vTextureUV      (0.0f, 0.0f),
					vTextureUV2     (0.0f, 0.0f)
					{}

					template<class Archive>
					void serialize(Archive & ar, const unsigned int version);
			};


			//---------------------------------------------------------------
			// Helper class to store GPU related resources created for
			// a given aiMesh
			//---------------------------------------------------------------
			class MeshHelper
				{
				public:

					MeshHelper ()
						: 
						piVB				(NULL),
						piIB				(NULL),
						vertexBuffer        (NULL),				
						indexBuffer         (NULL),
						piEffect			(NULL),
						piDiffuseTexture	(NULL),
						piDiffuseTexture2	(NULL),
						piSpecularTexture	(NULL),
						piAmbientTexture	(NULL),
						piNormalTexture		(NULL),
						piEmissiveTexture	(NULL),
						piOpacityTexture	(NULL),
						piShininessTexture	(NULL),
						pvOriginalNormals	(NULL),
						bSharedFX           (false),
						numVertices         (0),
						primitiveTypes		(0),
						bHM					(false)
						{}

					~MeshHelper ()
						{
						// NOTE: This is done in DeleteAssetData()
						// TODO: Make this a proper d'tor
						}

					template<class Archive>
					void serialize(Archive & ar, const unsigned int version);

					bool operator == (const AssetHelper::MeshHelper& otherMesh);
					bool operator != (const AssetHelper::MeshHelper& otherMesh);

					// shading mode to use. Either Lambert or otherwise phong
					// will be used in every case
					aiShadingMode eShadingMode;

					// vertex buffer
					IDirect3DVertexBuffer9* piVB;

					// index buffer. For partially transparent meshes
					// created with dynamic usage to be able to update
					// the buffer contents quickly
					IDirect3DIndexBuffer9* piIB;               

					// PNX: copy of the vertexBuffer piVB, used for serialization
					AssetHelper::Vertex* vertexBuffer;  

					// PNX: copy of the indexBuffer piIB, used for serialization
					unsigned int* indexBuffer;


					// shader to be used
					ID3DXEffect* piEffect;
					bool bSharedFX;


					// PNX: damit ich später LoadTexture() ohne das aiMaterial Object aufrufen kann,
					// werden auch im MeshHelper die pfade zu den texturen gespeichert
					aiString texturePathDiffuse;
					aiString texturePathDiffuse2;
					aiString texturePathSpecular;
					aiString texturePathAmbient;
					aiString texturePathEmissive;
					aiString texturePathNormal;
					aiString texturePathOpacity;
					aiString texturePathShininess;

					// PNX: das flag wird gesetzt, wenn statt einer NormalMap eine HeightMap benutzt wird
					bool bHM; 

					//PNX: übernommen aus dem aiMesh
					/** The number of primitives (triangles, polygons, lines) in this  mesh. 
					* This is also the size of the mFaces array 
					*/
					unsigned int numFaces;

					// material textures
					IDirect3DTexture9* piDiffuseTexture;
					IDirect3DTexture9* piDiffuseTexture2;
					IDirect3DTexture9* piSpecularTexture;
					IDirect3DTexture9* piAmbientTexture;
					IDirect3DTexture9* piEmissiveTexture;
					IDirect3DTexture9* piNormalTexture;
					IDirect3DTexture9* piOpacityTexture;
					IDirect3DTexture9* piShininessTexture;

					// material colors
					D3DXVECTOR4 vDiffuseColor;
					D3DXVECTOR4 vSpecularColor;
					D3DXVECTOR4 vAmbientColor;
					D3DXVECTOR4 vEmissiveColor;

					// opacity for the material
					float fOpacity;

					// shininess for the material
					float fShininess;

					// strength of the specular highlight
					float fSpecularStrength;

					// Stores a pointer to the original normal set of the asset
					aiVector3D* pvOriginalNormals;

					// the number of vertices in the mesh
					unsigned int numVertices;

					unsigned int indexBufferLength;

					// PNX: übermommen aus aiMesh
					unsigned int primitiveTypes;
				
				};  // class MeshHelper

		// One instance per aiMesh in the globally loaded asset
		MeshHelper** apcMeshes;

		// Scene wrapper instance
		aiScene* pcScene;

		// Animation player to animate the scene if necessary
		SceneAnimator* mAnimator;

		// PNX: filename of the asset
		aiString g_szFileName;

		//PNX: hatte ich aus der aiScene hier mit reingenommen, weil ich dachte, ich müsste die pcScene dann nicht serialisieren. 
		// lass ich aber erstmal hier drin, weil es so irgendwie übersichtlicher ist.
		unsigned int numMeshes;	

		// PNX: 
		aiVector3D lightDirs[NO_OF_DIRECTIONAL_LIGHTS];					// directional light
		D3DCOLOR   lightColors[NO_OF_DIRECTIONAL_LIGHTS];				// directional light colors
		float	   lightDirIntensity;									// intensity of all directional lights
		D3DCOLOR   lightAmbient;

		float		lightShininessModificator;								// modify the shininess of the whole asset
		float		lightSpecularStrengthModificator;						// modify the specular strength of the whole asset

	}; // class AssetHelper



//} // namespace


namespace AssimpRendererD3d
{
	template<class Archive>
	void AssetHelper::serialize(Archive & ar, const unsigned int version)
	{
		// 			DxWrapper* dxw = DxWrapper::Instance();
		// 			dxw->LogWrite("serializing assethelper");


		ar & numMeshes;

		//if (!assetHelper.pcScene)
		//	assetHelper.pcScene = new aiScene();

		ar & pcScene;

		if (!apcMeshes)
		{
			apcMeshes = new AssimpRendererD3d::AssetHelper::MeshHelper*[numMeshes];
			for (unsigned int i=0; i<numMeshes; ++i)
			{
				apcMeshes[i] = 0;
			}
		}

		for (unsigned int i=0; i<numMeshes; ++i)
		{
			if (apcMeshes[i] == 0)
				apcMeshes[i] = new AssimpRendererD3d::AssetHelper::MeshHelper();

			ar & *apcMeshes[i];
		}

	}

	template<class Archive>
	void AssetHelper::Vertex::serialize(Archive & ar, const unsigned int version)
	{
		ar & vPosition;
		ar & vNormal;

		ar & dColorDiffuse;
		ar & vTangent;
		ar & vBitangent;
		ar & vTextureUV;
		ar & vTextureUV2;

		ar & mBoneIndices[0];
		ar & mBoneIndices[1];
		ar & mBoneIndices[2];
		ar & mBoneIndices[3];

		ar & mBoneWeights[0]; // last Weight not used, calculated inside the vertex shader
		ar & mBoneWeights[1];
		ar & mBoneWeights[2];
		ar & mBoneWeights[3];                
	}

	// 			DxWrapper* dxw = DxWrapper::Instance();
	// 			dxw->LogWrite("serializing MeshHelper");
	template<class Archive>
	void AssetHelper::MeshHelper::serialize(Archive & ar, const unsigned int version)
	{

		ar & texturePathDiffuse;
		ar & texturePathDiffuse2;
		ar & texturePathSpecular;
		ar & texturePathAmbient;
		ar & texturePathEmissive;
		ar & texturePathNormal;
		ar & texturePathOpacity;
		ar & texturePathShininess;

		ar & bHM;
		ar & fShininess;
		ar & fSpecularStrength;
		ar & fOpacity;
		ar & bSharedFX;
		ar & eShadingMode;

		ar & vDiffuseColor;
		ar & vSpecularColor;
		ar & vEmissiveColor;
		ar & vAmbientColor;

		ar & numFaces;
		ar & numVertices;
		ar & primitiveTypes;

		// create the vertexBuffer upon deserialization
		if (vertexBuffer == 0)
			vertexBuffer = new AssimpRendererD3d::AssetHelper::Vertex[numVertices];

		// serialize the vertexBuffer
		AssimpRendererD3d::AssetHelper::Vertex* pVertex =  vertexBuffer;
		for (unsigned int i=0; i < numVertices; ++i)
		{
			ar & *pVertex;
			pVertex++;
		}

		// (de)serialize the indexBuffer
		ar & indexBufferLength;

		if (indexBuffer == 0)
		{
			indexBuffer = new unsigned int[indexBufferLength];
		}
		unsigned int* pIndex = indexBuffer;
		for (unsigned int i=0; i < indexBufferLength; ++i)
		{
			ar & *pIndex;
			pIndex++;
		}
	}

}