#pragma once

#include "d3dx9.h"
#include "..\DXFunctions\dllinterface.h"
#include "..\DXFunctions\DeviceStateManager.h"
#include "..\DXFunctions\Bound.h"
#include "..\DXFunctions\Camera.h"
#include "..\DXFunctions\RenderList.h"

#define MAXJITTER 320

enum SMJITTERTYPE
{
	SMJT_NONE = 0,
	SMJT_RANDOM,
	SMJT_HALTON,
	SMJT_ROTATION,
	SMJT_LISPSMN,
	SMJT_COUNT
};

class ShadowMapping
{
	DeviceStateManager* m_Device;

	bool mUseLispSM;
	DXMatrix mMatSM;
	DXMatrix mMatLightView;
	DXMatrix mMatLightProj;
	DXMatrix mMatSMTex;

	float mCosGamma;

	float m_N;

	float mDepthBias;
	float mSlopeBias;

	bool			m_UseIDBuffer;
	bool			m_UseNVShadowMap;
	RenderTexture*	m_ShadowMap;
	DepthStencilTexture* m_NVShadowMap;
	float			m_ShadowMapSize;

	float m_SMWorldSize;

	SMJITTERTYPE	m_JitterType;
	float			m_Jitter[MAXJITTER*2];

	SHADERPARAMETER m_ParamSMMatrix;
	SHADERPARAMETER m_ParamSMSize;
	TEXTUREPARAMETER m_TexParamSM;
	TEXTUREPARAMETER m_TexParamID;
	
	BoundingMesh* mB;
	BoundingMesh* mLVS;	
	DXVector3 mLightDir;
	Camera mCam;
	Camera m_CamLispSM;
	
	DXVector3 GetZ0_ls(const DXMatrix& matLight, const DXVector3& e, const float B_lsZmax, const DXVector3& camDir) const;
	float GetN(const DXMatrix& matLight, const AABB& obbB_ls) const;
	DXVector3 GetNearCameraPointE(void) const ;
	DXVector3 GetProjViewDir_ls(const DXMatrix& lv) const;

	void UpdateB(const AABB &obbScene);
	DXMatrix CalcLispSMMtx(const DXMatrix& matLight, const float nscale);

public:
	ShadowMapping(DeviceStateManager* const device);
	~ShadowMapping(void);

	void SetLispSMEnable(const bool ena) {mUseLispSM = ena;}
	bool GetLispSMEnable(void) const {return mUseLispSM;}

	void Update(const AABB &aabbScene, const Camera &cam, const DXVector3 &lightDir);

	bool GetUseNVShadowMap(void) const { return m_NVShadowMap != NULL; }

	const DXMatrix& GetSMMatrix(void) const {return mMatSM;}
	const DXMatrix& GetSMTexMatrix(void) const {return mMatSMTex;}
	const DXMatrix& GetLightView(void) const {return mMatLightView;}
	const DXMatrix& GetLightProj(void) const {return mMatLightProj;}
	void Jitter(const int Iteration);

	void StartShadowMapRendering(void) { m_ShadowMap->StartRender(0); if (m_NVShadowMap) m_NVShadowMap->StartRender(); }
	void EndShadowMapRendering(void){ m_ShadowMap->EndRender(); if (m_NVShadowMap) m_NVShadowMap->EndRender(); }
	DXTexture* GetShadowMap(void) const { return m_NVShadowMap ? m_NVShadowMap->GetSurface() : (DXTexture*)(m_ShadowMap->GetSurface()); }
	DXTexture* GetIDBuffer(void) const { return (DXTexture*)(m_ShadowMap->GetSurface()); }
	float GetShadowMapSize(void) const {return m_ShadowMapSize;}
	void SetShadowMapSize(const unsigned size);

	void SetJitterType(const SMJITTERTYPE type);
	SMJITTERTYPE GetJitterType(void) const {return m_JitterType;}

	SHADERPARAMETER* GetParamSMMatrix(void) { return &m_ParamSMMatrix; }
	SHADERPARAMETER* GetParamSMSize(void) { return &m_ParamSMSize; }
	TEXTUREPARAMETER* GetTexParamSM(void) { return &m_TexParamSM; }
	TEXTUREPARAMETER* GetTexParamID(void) { return &m_TexParamID; }

	float GetUsedN(void) const { return m_N; }

	BoundingMesh* GetB(void) { return mB; }
	float GetSMWorldSize(void) const { return m_SMWorldSize; }

	void SetIDBufferEnable(const bool ena);
	bool GetIDBufferEnable(void) const { return m_UseIDBuffer; }

	void SetLightViewProj(const DXMatrix& view, const DXMatrix& proj);
};
