#pragma once

#include "..\Utilities\XString.h"
#include "devicestatemanager.h"
#include "renderlist.h"
#include "bound.h"
#include <d3d9.h>
#include <d3dx9mesh.h>
#include <mmsystem.h>

class DXMaterial;

#define MAXSVVERTICE 8096

class DXFUNCTIONS_API CShadowVolume
{
    DXVector3 *m_pVertices; 
    DWORD       m_dwNumVertices;

	void AddEdge( WORD* pEdges, DWORD& dwNumEdges, WORD v0, WORD v1 );

public:

	CShadowVolume(void);
	~CShadowVolume(void);

    HRESULT BuildFromMesh( LPD3DXMESH pObject, DXVector3 vLight ,bool self=true);
    HRESULT Render( DeviceStateManager*device );
	void Clear(void);
};

class DXFUNCTIONS_API CMesh
{
	LPD3DXMESH			m_pMesh;			// Mesh
	//LPD3DXMESH			m_pSysMesh;			// Mesh in Sysmemory for Restore
	DWORD               m_dwNumMaterials;	// Materials for the mesh
   
	DXMaterial*			m_Materials;

    bool                m_bUseMaterials;
	XString				m_strFileName;
	XString				m_strPath;
	bool				m_bInvalidated;

	// Attribute table stuff
	LPD3DXATTRIBUTERANGE m_pAttributeTable;	// The attribute table
    DWORD                m_NumAttributeGroups;// The number of attribute groups

	VertexDeclaration*	m_VDecl;
	int m_NumBytesPerVertex;

	LPDIRECT3DVERTEXBUFFER9 m_VB;
	LPDIRECT3DINDEXBUFFER9 m_IB;

	OBS m_Bound;

	RenderJobDrawCall*   m_pDrawCalls;

	DeviceStateManager* m_Device;

	bool ReadRawBinary(void);
	bool SaveRawBinary(void) const;

public:
	//CMesh(void);
	CMesh(DeviceStateManager*device,XString FileName,XString Path="");
	void BuildDrawCalls(void);
	RenderJobDrawCall* GetDrawCalls(void) const {return m_pDrawCalls;}
	HRESULT Create(DeviceStateManager*device);
	~CMesh(void);
	HRESULT RestoreDeviceObjects(DeviceStateManager*device);
    HRESULT InvalidateDeviceObjects(void);
	HRESULT Render(DeviceStateManager*device,D3DXMATRIX Position,bool bDrawOpaqueSubsets=TRUE,bool bDrawAlphaSubsets=TRUE );
	HRESULT Render(DeviceStateManager*device,D3DXMATRIX Position,DXMaterial *mat);
	LPD3DXMESH GetMesh(void);
	DXMaterial* GetMaterials(void) const {return m_Materials;}
	DWORD GetMaterialCount(void) const { return m_dwNumMaterials;}
	void DrawSubset(DeviceStateManager*device, const DWORD pAttribID);
	LPD3DXATTRIBUTERANGE GetAttributeTable(void) const {return m_pAttributeTable;}
	VertexDeclaration* GetVertexDeclaration(void) const {return m_VDecl;}
	LPDIRECT3DVERTEXBUFFER9 GetVB(void) const {return m_VB;}
	LPDIRECT3DINDEXBUFFER9 GetIB(void) const {return m_IB;}
	OBS GetBound(void) const { return m_Bound; }
};

#define DEFAULTFRAME	8.512

struct D3DXFRAME_DERIVED: public D3DXFRAME
{
    D3DXMATRIX        CombinedTransformationMatrix;
};

struct D3DXMESHCONTAINER_DERIVED: public D3DXMESHCONTAINER
{
    LPDIRECT3DTEXTURE9*  ppTextures;
};

class DXFUNCTIONS_API CAllocateHierarchy;

class DXFUNCTIONS_API CAniMesh
{
	XString				m_strFileName;
	XString				m_strPath;
	double				m_dTime;
	LPD3DXFRAME			m_pFrameRoot;
	LPD3DXANIMATIONCONTROLLER	m_pAnimController;
	bool        m_bInvalidated;
	CAllocateHierarchy	*m_pAllocHist;

	void DeleteMesh(LPD3DXFRAME pFrameBase);
	void SetAlpha(LPD3DXFRAME pFrameBase,float a);

	HRESULT RenderFrame(DeviceStateManager*device,LPD3DXFRAME pFrame,D3DXMATRIX *Position,bool whithsubframe);

public:
	CAniMesh(void);
	CAniMesh(DeviceStateManager*device,XString FileName,XString Path="");
	HRESULT Create(DeviceStateManager*device);
	HRESULT Update(void);
	HRESULT RenderSingleFrame(DeviceStateManager*device,LPD3DXFRAME pFrame,D3DXMATRIX Position);
	~CAniMesh(void);
	HRESULT RestoreDeviceObjects(DeviceStateManager*device);
    HRESULT InvalidateDeviceObjects(void);
	HRESULT Render(DeviceStateManager*device,D3DXMATRIX Position);
	LPD3DXFRAME getFrame(XString*Name,LPD3DXFRAME startFrame=NULL);//if startframe==NULL startFrame=frameroot
	HRESULT SetFrameTime(double Frame);
	void SetAlphaValue(float a);
	LPD3DXFRAME GetFrameRoot(void);
	void UpdateFrameMatrices(LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix);
};

//-----------------------------------------------------------------------------
// Name: class CAllocateHierarchy
// Desc: Custom version of ID3DXAllocateHierarchy with custom methods to create
//       frames and meshcontainers.
//-----------------------------------------------------------------------------

#ifndef SWIG
class DXFUNCTIONS_API CAllocateHierarchy: public ID3DXAllocateHierarchy
{
public:
    STDMETHOD(CreateFrame)(THIS_ LPCTSTR Name, LPD3DXFRAME *ppNewFrame);
	STDMETHOD(CreateMeshContainer)(LPCSTR Name, const D3DXMESHDATA *pMeshData,
		const D3DXMATERIAL *pMaterials, const D3DXEFFECTINSTANCE *pEffectInstances,
		DWORD NumMaterials, const DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo,
		LPD3DXMESHCONTAINER *ppNewMeshContainer);
    STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree);
    STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase);
	CAllocateHierarchy(CAniMesh *pMesh, XString Path="");

public:
    CAniMesh* m_pMesh;
	XString m_strPath;
};
#endif

//Derived from the default mesh container
struct DXFUNCTIONS_API XMESHCONTAINER: public D3DXMESHCONTAINER
{
	//Mesh variables
	DXMaterial*		 pMaterials;		// Use the DirectX 9 Material type
	
	//Skinned mesh variables
	LPD3DXMESH           pSkinMesh;			// The skin mesh
	LPD3DXMATRIX		 pBoneOffsets;		// The bone matrix Offsets
	LPD3DXMATRIX*		 ppFrameMatrices;	// Pointer to the Frame Matrix

	// Attribute table stuff
	LPD3DXATTRIBUTERANGE pAttributeTable;	// The attribute table
    DWORD               NumAttributeGroups;// The number of attribute groups

	unsigned     		NumTexCoordSets;
	bool				HasNormals;
	bool				HasTangents;

	void*				pUserInfo;

	VertexDeclaration*	VDecl;

	LPDIRECT3DVERTEXBUFFER9 VB;
	LPDIRECT3DINDEXBUFFER9 IB;

	RenderJobDrawCall*   pDrawCalls;

	OBS		BSphere;
	AABB	BBox;

	XString GetName(void) const {return XString(Name);}

	XMESHCONTAINER* GetNextMeshContainer(void) const {return (XMESHCONTAINER*)pNextMeshContainer;}

	DWORD GetFaceCount(void) const {LPD3DXMESH pDrawMesh = (pSkinInfo) ? pSkinMesh: MeshData.pMesh; return pDrawMesh->GetNumFaces();}
	DWORD GetVertexCount(void) const {LPD3DXMESH pDrawMesh = (pSkinInfo) ? pSkinMesh: MeshData.pMesh; return pDrawMesh->GetNumVertices();}
	DWORD GetMaterialCount(void) const {return NumMaterials;}

	void DrawMeshSubset(DeviceStateManager* device, const DWORD pAttribID);

	DWORD GetUserInfoPtr(void) const { return (DWORD)pUserInfo; }
};

//Derived frame struct so it looks like a heirarchy
struct DXFUNCTIONS_API XMESHFRAME: public D3DXFRAME
{
	DXMatrix matCombined;	//Combined Transformation Matrix

	XMESHCONTAINER* GetMeshContainer(void) const {return (XMESHCONTAINER*)pMeshContainer;}

	XString GetName(void) const {return XString(Name);}

	const DXMatrix& GetTransformationMatrix(void) const { return *((const DXMatrix*)&TransformationMatrix); }

	XMESHFRAME* GetFrameSibling(void) const {return (XMESHFRAME*)pFrameSibling;}
	XMESHFRAME* GetFrameFirstChild(void) const {return (XMESHFRAME*)pFrameFirstChild;}
};

#ifndef SWIG
class DXFUNCTIONS_API XMeshAllocateHierarchy: public ID3DXAllocateHierarchy
{
	DeviceStateManager* m_Device;
public:
	XMeshAllocateHierarchy(DeviceStateManager* const device);
	~XMeshAllocateHierarchy(){}

	// Create a frame
    //1. The name of the frame
	//2. The output new frame
    STDMETHOD(CreateFrame)(THIS_ LPCTSTR Name, LPD3DXFRAME *ppNewFrame);

	// Create a Mesh Container
	//1. Name of the Mesh
	//2. The mesh Data
	//3. that materials of the mesh
	//4. the effects on the mesh
	//5. the number of meterials in the mesh
	//6. the adjacency array for the mesh
	//7. the skin information for the mesh
	//8. the output mesh container
	STDMETHOD(CreateMeshContainer)(LPCSTR Name, const D3DXMESHDATA *pMeshData,
		const D3DXMATERIAL *pMaterials, const D3DXEFFECTINSTANCE *pEffectInstances,
		DWORD NumMaterials, const DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo,
		LPD3DXMESHCONTAINER *ppNewMeshContainer);

	// Destroy a frame
	//1. The frame to delete
    STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree);

	// Destroy a mesh container
	//1. The container to destroy
    STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase);
};
#endif

/*
class XMaterial
{
	DXColor m_Ambient;
	DXColor m_Diffuse;
	DXColor m_Specular;
	DXColor m_Emissive;
	DXTexture* m_Texture;
	DXTexture* m_Normal;
	DXTexture* m_Specular;
	DXTexture* m_Height;
	float m_SpecularExp;
};*/

class DXFUNCTIONS_API XMesh
{
	XString		m_FileName;
	XString		m_FilePath;

	RenderJobDrawCall** m_pDrawCallIndex;
	XMESHFRAME*		m_pFrameRoot;
	XMESHFRAME**	m_pFrameHierarchyArray;
	LPD3DXANIMATIONCONTROLLER	m_pAnimController;
	DXMatrix*		m_pBoneMatrices;
	UINT			m_uMaxBones;
	
	XMeshAllocateHierarchy	*m_pAllocHist;

	DWORD	m_AnimationSetCount;
	DeviceStateManager* m_Device;

	DWORD	m_CurrentAnimation;

	unsigned m_FrameCount;
	unsigned m_SubsetCount;

	OBS		m_BSphere;
	AABB	m_BBox;
	
	void LoadXFile(void);

	void InitFrameHieraryArray(XMESHFRAME* const pFrame, int& iframe);
	void UpdateBound(XMESHFRAME* const pFrame);
	void UpdateFrameMatrices(XMESHFRAME* const pFrame, DXMatrix* const pParentMatrix);
	void SetupHierarchy(XMESHFRAME* const pFrame, DXMatrix* const pParentMatrix);
	void BuildDrawCallIndex(XMESHFRAME* const pFrame, unsigned& idc);

public:
	XMesh(DeviceStateManager* const device, const XString& pathfn);
	~XMesh(void);

	void Update(float dElapsedTime);

	DWORD GetAnimation(void) const {return m_CurrentAnimation;}
	void SetAnimation(const DWORD anim);

	unsigned GetFrameCount(void) const { return m_FrameCount; }
	unsigned GetSubsetCount(void) const { return m_SubsetCount; }
	XMESHFRAME* GetFrameRoot(void) const {return m_pFrameRoot;}
	XMESHFRAME* GetFrame(const unsigned iframe) const { if (iframe >= 0 && iframe < m_FrameCount) return m_pFrameHierarchyArray[iframe]; return NULL;}
	int GetFrameIndex(const XMESHFRAME* pFrame) const { if (m_pFrameHierarchyArray) { for (unsigned i = 0; i < m_FrameCount; i++) if (m_pFrameHierarchyArray[i] == pFrame) return i; } return -1; }

	void DrawFrame(XMESHFRAME* pFrame);

	XString GetFileName(void) const {return m_FileName;}
	XString GetFilePath(void) const {return m_FilePath;}

	OBS GetBoundingSphere(void) const {return m_BSphere;}
	AABB GetBoundingBox(void) const {return m_BBox;}

	RenderJobDrawCall* GetDrawCall(const unsigned iSubset) { if (iSubset <= m_SubsetCount) return m_pDrawCallIndex[iSubset]; return NULL; }
};
