
// zlib open source license
//
// Copyright (c) 2010 to 2013 David Forsgren Piuva
// 
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// 
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 
//    1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 
//    2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 
//    3. This notice may not be removed or altered from any source
//    distribution.

#include "../MessageQueue.h"

#define Try_Start_Model __try
#define Try_Else_Model __except(Filter_Model(GetExceptionCode(), GetExceptionInformation()))

struct Model_Struct; // Forward declare

struct VertexStructure {
	DVector3 Pos;
	DVector4 TexCoord;
	DVector4 Color;
	DVector3 Normal;
	float Selected;
	DVector4 A;
	DVector4 B;
	DVector4 BoneData;
};

const VertexStructure DefaultVertexStructure =
	{DVector3(0.0f,0.0f,0.0f), // Pos
	DVector4(0.0f,0.0f,0.0f,0.0f), // Texcoord
	DVector4(1.0f,1.0f,1.0f,1.0f), // Color
	DVector3(0.0f,0.0f,0.0f), // Normal
	0.0f, // Selected
	DVector4(0.0f,0.0f,0.0f,0.0f), // A
	DVector4(0.0f,0.0f,0.0f,0.0f), // B
	DVector4(0.0f,0.0f,0.0f,-1.0f)}; // BoneData

struct Part_Type {
	VertexStructure*		VertexBuffer; // The array to edit
	int						AllocatedVertices;
	int						UsedVertices;
	ID3D11Buffer*			D3DVertexBuffer; // The vertex buffer in Direct3D
	wchar_t*				PartName;
	Texture_Struct*			pTexture[NumberOfTextureChannels];
	int						iTextureOverride[NumberOfTextureChannels];
	Shader_Struct*			pShaders[NumberOfShaderChannels];
	bool					NeedGeometryUpdate;
	int						MinDetailLevel;
	int						MaxDetailLevel;
};

struct Bone_Type {
	wchar_t*	BoneName; // The name of this bone
	int			ParentIndex; // The index of the parent bone who's end this should start with
	float		Length; // The distance between the start and the end
	DVector3	ObjectSpacePos; // The bone's real position in object space
	DVector3	ParentSpacePos; // The relative position to it's parent used to align the bones after rotating
	DVector3	YAxis; // The up or back side
	DVector3	ZAxis; // From start to end
	DVector3	UserData; // The user's data in the padding
};

struct DirPoint_Type {
	DVector3	Pos; // The position of the corner
	DVector3	Dir; // The direction of the corner used for tolerance compensation
};

struct Shape_Type {
	int						ShapeType; // See GlobalConstants.h for the "ShapeType_*" constants
	
	// Flags
	int						CollisionType; // 0 is non collisional, 1 is collisional, every thing else is for the user to decide but usually non collisional in the physics engine

	// For basic shapes
	DVector3				Pos;
	DVector3				XAxis;
	DVector3				YAxis;
	DVector3				ZAxis;
	float					Radius;
	float					HalfWidth;
	float					HalfHeight;
	float					HalfDepth;
	
	// For convex hulls
	DirPoint_Type*			Points;
	int						NumberOfPoints;
	int						AllocatedPoints;
	
	// For the user
	wchar_t*				ShapeName;
};

struct ModelPacket {
	int						FilterType;
	int						CullingMethod;
	float					BoundMultiplier;
	boolean					NeedNewBounds;
	boolean					NeedAnyUpdate;
	wchar_t					FileName[260];
};

class ModelClass {
private:
	// Constant
	UINT					VertexStructureSize;
	UINT					NoOffset;
	
	// Collection of bones
	Bone_Type*				Bones;
	int						NumberOfBones;
	
	// Part methods
	VertexStructure*		OldBuffer;
	void					UpdatePartsGeometry(int PartIndex);
	void					SetPartSize(int PartIndex, int newSize);
	void						SetPartSizeAux(int PartIndex, int newSize);
	void						SetPartSizeAux2(int PartIndex, int newSize);
	void					ReleasePart(int PartIndex);
	int						AddTriangle(int PartIndex); // Returns the triangle index
	void					AllocateParts(int newSize);
	void					ReallocateParts(int newSize);
	void					DeallocateCollections(void);
	
	// Bone methods
	void ReleaseBone(int BoneIndex);
	
	// Trap low level errors
	int Filter_Model(unsigned int code, struct _EXCEPTION_POINTERS *ep);
	
	// Modified
	void PartHasBeenModified(int PartIndex);
	void NeedNewBounds(void);
	
public:
	// Collection of parts
	Part_Type*				Parts;				// The array
	int						NumberOfParts;		// Used space
	int						AllocatedParts;		// Available space

	// Collection of shapes
	
	Shape_Type*				Shapes;				// The array
	int						NumberOfShapes;		// Used space
	int						AllocatedShapes;	// Available space

	// Shape methods
	DirPoint_Type*				OldPointBuffer;
	void					SetPointArraySizeAux2(int ShapeIndex, int newSize);
	void					SetPointArraySizeAux(int ShapeIndex, int newSize);
	void					SetPointArraySize(int ShapeIndex, int newSize);

	// Class lifetime
	void Init(ID3D11Device* new_pd3dDevice, ID3D11DeviceContext* new_pImmediateContext, MessageQueue* new_MQ);
	void Terminate();
	
	// Persistent properties
	ModelPacket ModelProperties;
	
	// Read only
	DVector3				ObjectSpaceMin;
	DVector3				ObjectSpaceMax;
	DVector3				ObjectSpaceCenter;
	float					Radius;
	
	// Call pointers
	MessageQueue* MQ;
	ID3D11Device* m_pd3dDevice;
	ID3D11DeviceContext* m_pImmediateContext;
	
	// Safe public methods
	bool					IsPartVisible(int DetailLevel_Int, int PartIndex);
	void					Render( int ShaderChannel, Shader_Struct* DefaultShader, Texture_Struct* DefaultTexture, SurfaceCombo OverrideSurfaces[16], Texture_Struct* LightProjectionAtlas, int DetailLevel_Int, Shader_Struct* ShaderOverride, int Part, bool IsShadow);
	void					MeasureBounds(void);
	int						GetNumberOfParts(void);
	int						CreateNewPart( wchar_t* strPartName, int PreallocatedTriangleCount ); // Returns the part index
	wchar_t*					GetPartName(int PartIndex);
	void						SetPartName(int PartIndex, wchar_t* Name);
	void						SetPartTexture( int PartIndex, int TextureChannel, Texture_Struct* Texture );
	Texture_Struct*				GetPartTexture(int PartIndex, int TextureChannel);
	void						SetPartTextureOverride( int PartIndex, int TextureChannel, int TextureOverride );
	int							GetPartTextureOverride(int PartIndex, int TextureChannel);
	void						SetPartMinDetailLevel( int PartIndex, int DetailLevel );
	int							GetPartMinDetailLevel(int PartIndex);
	void						SetPartMaxDetailLevel( int PartIndex, int DetailLevel );
	int							GetPartMaxDetailLevel(int PartIndex);
	void						SetPartShader( int PartIndex, int ShaderChannel, Shader_Struct* Shader );
	Shader_Struct*				GetPartShader(int PartIndex, int ShaderChannel);
	int							GetTriangleCountFromPart(int PartIndex);
	int							InsertTriangleToPart( int PartIndex ); // Returns the triangle index
	void						DeleteTriangle(int PartIndex, int TriangleIndex);
	void						DeleteTriangle_PreserveOrder(int PartIndex, int TriangleIndex);
	void						FlipTriangle(int PartIndex, int TriangleIndex);
	void						SwapTriangleRenderingOrder(int PartIndex, int TriangleIndexA, int TriangleIndexB);
	void							Vertice_SetPos( int PartIndex, int TriangleIndex, int VerticeIndex, DVector3 Pos );
	void							Vertice_SetTexCoord( int PartIndex, int TriangleIndex, int VerticeIndex, DVector4 TexCoord );
	void							Vertice_SetColor( int PartIndex, int TriangleIndex, int VerticeIndex, DVector4 Color );
	void							Vertice_SetNormal( int PartIndex, int TriangleIndex, int VerticeIndex, DVector3 Normal );
	void							Vertice_SetA( int PartIndex, int TriangleIndex, int VerticeIndex, DVector4 A );
	void							Vertice_SetB( int PartIndex, int TriangleIndex, int VerticeIndex, DVector4 B );
	void							Vertice_SetBoneData( int PartIndex, int TriangleIndex, int VerticeIndex, DVector4 B );
	void							Vertice_SetPos_X( int PartIndex, int TriangleIndex, int VerticeIndex, float Value ); // For file loading
	void							Vertice_SetPos_Y( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetPos_Z( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetTexCoord_U1( int PartIndex, int TriangleIndex, int VerticeIndex, float Value ); // For file loading
	void							Vertice_SetTexCoord_V1( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetTexCoord_U2( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetTexCoord_V2( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetColor_R( int PartIndex, int TriangleIndex, int VerticeIndex, float Value ); // For file loading
	void							Vertice_SetColor_G( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetColor_B( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetColor_A( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetNormal_X( int PartIndex, int TriangleIndex, int VerticeIndex, float Value ); // For file loading
	void							Vertice_SetNormal_Y( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetNormal_Z( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetSelected( int PartIndex, int TriangleIndex, int VerticeIndex, float Selected );
	void							Vertice_SetA_X( int PartIndex, int TriangleIndex, int VerticeIndex, float Value ); // For file loading
	void							Vertice_SetA_Y( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetA_Z( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetA_W( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetB_X( int PartIndex, int TriangleIndex, int VerticeIndex, float Value ); // For file loading
	void							Vertice_SetB_Y( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetB_Z( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetB_W( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetBoneData_X( int PartIndex, int TriangleIndex, int VerticeIndex, float Value ); // For file loading
	void							Vertice_SetBoneData_Y( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetBoneData_Z( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	void							Vertice_SetBoneData_W( int PartIndex, int TriangleIndex, int VerticeIndex, float Value );
	DVector3						Vertice_GetPos( int PartIndex, int TriangleIndex, int VerticeIndex );
	DVector4						Vertice_GetTexCoord( int PartIndex, int TriangleIndex, int VerticeIndex );
	DVector4						Vertice_GetColor( int PartIndex, int TriangleIndex, int VerticeIndex );
	DVector3						Vertice_GetNormal( int PartIndex, int TriangleIndex, int VerticeIndex );
	float							Vertice_GetSelected( int PartIndex, int TriangleIndex, int VerticeIndex );
	DVector4						Vertice_GetA( int PartIndex, int TriangleIndex, int VerticeIndex );
	DVector4						Vertice_GetB( int PartIndex, int TriangleIndex, int VerticeIndex );
	DVector4						Vertice_GetBoneData( int PartIndex, int TriangleIndex, int VerticeIndex );
	void							Vertice_SetAll( int PartIndex, int TriangleIndex, int VerticeIndex,VertexStructure VertexData );
	VertexStructure					Vertice_GetAll( int PartIndex, int TriangleIndex, int VerticeIndex );
	void					DeletePart( int PartIndex );
	void					DeletePart_PreserveOrder( int PartIndex );
	void					SwapPartRenderingOrder( int PartIndexA, int PartIndexB );
	void					UpdatePartsGeometryIfNeeded(int PartIndex);
	void					UpdateEverythingIfNeeded(void); // Call before rendering or getting bounds
	wchar_t*				GetFilterName(void);
	void					SetFilterUsingName(wchar_t* FilterName,wchar_t* MethodName);
	wchar_t*				GetCullingName(void);
	void					SetCullingUsingName(wchar_t* CullingName,wchar_t* MethodName);
	int						GetNumberOfBones(void);
	int						CreateNewBone(wchar_t* strBoneName); // Returns the bone index
	wchar_t*					GetBoneName(int BoneIndex);
	void						SetBoneName(int BoneIndex, wchar_t* Name);
	void						Bone_SetParentIndex(int BoneIndex, int ParentIndex);
	int							Bone_GetParentIndex(int BoneIndex);
	void						Bone_SetLength(int BoneIndex, float Length);
	float						Bone_GetLength(int BoneIndex);
	void						Bone_SetObjectSpacePos(int BoneIndex, DVector3 ObjectSpacePos);
	void						Bone_SetObjectSpacePosX(int BoneIndex, float ObjectSpacePosX); // For file loading
	void						Bone_SetObjectSpacePosY(int BoneIndex, float ObjectSpacePosY);
	void						Bone_SetObjectSpacePosZ(int BoneIndex, float ObjectSpacePosZ);
	DVector3					Bone_GetObjectSpacePos(int BoneIndex);
	void						Bone_SetParentSpacePos(int BoneIndex, DVector3 ParentSpacePos);
	void						Bone_SetParentSpacePosX(int BoneIndex, float ParentSpacePosX); // For file loading
	void						Bone_SetParentSpacePosY(int BoneIndex, float ParentSpacePosY);
	void						Bone_SetParentSpacePosZ(int BoneIndex, float ParentSpacePosZ);
	DVector3					Bone_GetParentSpacePos(int BoneIndex);
	void						Bone_SetYAxis(int BoneIndex, DVector3 YAxis);
	void						Bone_SetYAxisX(int BoneIndex, float YAxisX); // For file loading
	void						Bone_SetYAxisY(int BoneIndex, float YAxisY);
	void						Bone_SetYAxisZ(int BoneIndex, float YAxisZ);
	DVector3					Bone_GetYAxis(int BoneIndex);
	void						Bone_SetZAxis(int BoneIndex, DVector3 ZAxis);
	void						Bone_SetZAxisX(int BoneIndex, float ZAxisX); // For file loading
	void						Bone_SetZAxisY(int BoneIndex, float ZAxisY);
	void						Bone_SetZAxisZ(int BoneIndex, float ZAxisZ);
	DVector3					Bone_GetZAxis(int BoneIndex);
	void						Bone_SetUserData(int BoneIndex, DVector3 ZAxis);
	void						Bone_SetUserDataX(int BoneIndex, float UserDataX); // For file loading
	void						Bone_SetUserDataY(int BoneIndex, float UserDataY);
	void						Bone_SetUserDataZ(int BoneIndex, float UserDataZ);
	DVector3					Bone_GetUserData(int BoneIndex);
	SingleBone					Bone_GetBone(int BoneIndex);
	void					DeleteBone(int BoneIndex);
	void					DeleteBone_PreserveOrder(int BoneIndex);
	void					SwapBoneHierarchyOrder( int BoneIndexA, int BoneIndexB );
	int						CreateNewShape(wchar_t* strShapeName, int ShapeType);
	void						ReallocateShapes(int newSize);
	void						ReleaseShape(int ShapeIndex);
	void						DeleteShape(int ShapeIndex);
	void						DeleteShape_PreserveOrder(int ShapeIndex);
	wchar_t*					GetShapeName(int ShapeIndex);
	void						SetShapeName(int ShapeIndex, wchar_t* Name);
	int							GetNumberOfShapes(void);
	int							Shape_GetNumberOfPoints(int ShapeIndex);
	void						Shape_SetShapeType(int ShapeIndex, int ShapeType);
	int							Shape_GetShapeType(int ShapeIndex);
	void						Shape_SetCollisionType(int ShapeIndex, int CollisionType);
	int							Shape_GetCollisionType(int ShapeIndex);
	void						Shape_SetRadius(int ShapeIndex, float Radius);
	float						Shape_GetRadius(int ShapeIndex);
	void						Shape_SetHalfWidth(int ShapeIndex, float HalfWidth);
	float						Shape_GetHalfWidth(int ShapeIndex);
	void						Shape_SetHalfHeight(int ShapeIndex, float HalfHeight);
	float						Shape_GetHalfHeight(int ShapeIndex);
	void						Shape_SetHalfDepth(int ShapeIndex, float HalfDepth);
	float						Shape_GetHalfDepth(int ShapeIndex);
	void						Shape_SetPos(int ShapeIndex, DVector3 Pos);
	void						Shape_SetPosX(int ShapeIndex, float PosX); // For file loading
	void						Shape_SetPosY(int ShapeIndex, float PosY);
	void						Shape_SetPosZ(int ShapeIndex, float PosZ);
	DVector3					Shape_GetPos(int ShapeIndex);
	void						Shape_SetXAxis(int ShapeIndex, DVector3 XAxis);
	void						Shape_SetXAxisX(int ShapeIndex, float XAxisX); // For file loading
	void						Shape_SetXAxisY(int ShapeIndex, float XAxisY);
	void						Shape_SetXAxisZ(int ShapeIndex, float XAxisZ);
	DVector3					Shape_GetXAxis(int ShapeIndex);
	void						Shape_SetYAxis(int ShapeIndex, DVector3 YAxis);
	void						Shape_SetYAxisX(int ShapeIndex, float YAxisX); // For file loading
	void						Shape_SetYAxisY(int ShapeIndex, float YAxisY);
	void						Shape_SetYAxisZ(int ShapeIndex, float YAxisZ);
	DVector3					Shape_GetYAxis(int ShapeIndex);
	void						Shape_SetZAxis(int ShapeIndex, DVector3 ZAxis);
	void						Shape_SetZAxisX(int ShapeIndex, float ZAxisX); // For file loading
	void						Shape_SetZAxisY(int ShapeIndex, float ZAxisY);
	void						Shape_SetZAxisZ(int ShapeIndex, float ZAxisZ);
	DVector3					Shape_GetZAxis(int ShapeIndex);
	DVector3					Shape_GetPoint(int ShapeIndex, int PointIndex);
	DVector3					Shape_GetPointDir(int ShapeIndex, int PointIndex);
	int							Shape_InsertPointToShape( int ShapeIndex, DVector3 Pos); // Returns the point index
	void						Shape_SetPoint(int ShapeIndex, int PointIndex, DVector3 Point);
	void						Shape_SetPointX(int ShapeIndex, int PointIndex, float PointX); // For file loading
	void						Shape_SetPointY(int ShapeIndex, int PointIndex, float PointY);
	void						Shape_SetPointZ(int ShapeIndex, int PointIndex, float PointZ);
	void						Shape_SetPointDir(int ShapeIndex, int PointIndex, DVector3 Dir);
	void						Shape_SetPointDirX(int ShapeIndex, int PointIndex, float DirX);
	void						Shape_SetPointDirY(int ShapeIndex, int PointIndex, float DirY);
	void						Shape_SetPointDirZ(int ShapeIndex, int PointIndex, float DirZ);
	void						Shape_DeletePoint(int ShapeIndex, int PointIndex);
	void						Shape_DeletePoint_PreserveOrder(int ShapeIndex, int PointIndex);
	void						SwapShapeOrder( int ShapeIndexA, int ShapeIndexB );
	void						SwapPointOrder( int ShapeIndex, int PointIndexA, int PointIndexB );
};
