#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif 

#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

#include "..\dxfunctions\dllinterface.h"
#include "..\dxfunctions\d3dapp.h"
#include "..\dxfunctions\difunctions.h"
#include "..\dxfunctions\font.h"
#include "..\dxfunctions\camera.h"
#include "..\dxfunctions\renderlist.h"
#include "..\Utilities\stack.h"
#include "..\Utilities\list.h"

#include "..\gamegui\menumanager.h"
class MainMenu;

#include "SceneObject.h"
#include "ShadowMapping.h"
#include "Terrain.h"
#include "MeshObject.h"
#include "AirPlane.h"
#include "ShadowRenderList.h"
#include "DeferredShadowTechnique.h"

#define JITTERLIMIT 300
#define CAMPOSITIONS 10
#define BENCHCAMPOSSTAY 3
#define STATICOBJECTS 520
#define DYNAMICOBJECTS 50
#define EIFELS 10

//#define STATICOBJECTS 2500
//#define DYNAMICOBJECTS 250

enum BENCHTYPE
{	
	BT_NONE = 0,
	BT_PERFORMANCE,
	BT_CONVERGENCE
};

enum SHMETHOD
{
	SM_COMMON = 0,
	SM_DEFERRED,
	SM_CONFIDENCE,
	SM_ZTILED,
	SM_SSTILED,
	SM_SMTILED,
	SM_COUNT
};

enum VISTYPE
{
	VIS_NONE = 0,
	VIS_SAMPLE,
	VIS_PASS,
	VIS_FINALCONF,
	VIS_OPTCONF,
	VIS_ID,
	VIS_SSAO,
	VIS_REQRES,
	VIS_COUNT
};

enum DSWORLDPOSTYPE
{
	WPT_DEPTH = 0,
	WPT_LINEARDEPTH,
	WPT_WORLDPOS
};


struct STConfig
{
	SHMETHOD	Method;

	bool		DrawObjects;
	bool		UpdateDynamicObjects;

	bool		PCFPreTest;

	bool		ErodeOneLess;
	int			CloseSize;
		
	//float		ConfT;
	unsigned	MaxJitter;
	
	//unsigned	DrawT;
	//unsigned	DrawTPasses;
	float		ConstBias;
	float		SlopeBias;

	float		LambertScale;

	unsigned		ShadowMapSize;
	bool			UseLispSM;

	bool			UseIDBuffer;
	bool			UsePrimID;

	bool			UseSSAO;
	bool			SMFiltering;
	//SMJITTERTYPE	JitterType;

	DSWORLDPOSTYPE  WorldPosType;
};

class DeferredShadowConfidence;
class DeferredShadowSSTiled;
class DeferredShadowSMTiled;
class DeferredShadowZTiled;

//#define WRITEWORLD

class ShadowApp: public D3DApp
{
protected:

	friend DeferredShadowConfidence;
	friend DeferredShadowSSTiled;
	friend DeferredShadowSMTiled;
	friend DeferredShadowZTiled;
	friend MainMenu;
	friend ShadowScene;

	CDIFunctions m_DI;
	DXVector2	m_MouseDownPos;
	HWND		m_hWndMain;
	HINSTANCE	m_hInst;
	Font*		m_Font;
	//Camera		m_Cam;
	//Camera		m_DebugCam;
	bool		m_UseDebugCam;

	//DXVector3	m_SunDir;
	//DXColor		m_SunColor;
	//DXColor		m_AmbientColor;
	//float		m_LightAngX, m_LightAngZ;

	STConfig	m_Config;

	MenuManager*m_pMenuManager;

	DeferredShadowTechnique* m_Techniques[SM_COUNT];

	bool		m_ShowSM;
	bool		m_bDrawWireframe;
	bool		m_bShowMenu;

	bool		m_CaputreNextFrame;
	bool		m_SelectBench;

	BENCHTYPE	m_BenchType;

	VISTYPE		m_VisType;

	double		m_UpdateTime;
	double		m_RenderTime;

	//performance
	unsigned	m_BenchStartFrames;
	unsigned	m_CamStartFrames;
	unsigned	m_SumFrames;
	unsigned	m_CamFrames[CAMPOSITIONS];
	LONGLONG	m_BenchStart;

	//convergence
	bool		m_RenderRef;
	unsigned	m_MaxJitterRestore;
	unsigned	m_iPixelDiff;
	unsigned	m_Passes[100];
	unsigned	m_PixelDiff[100];
	unsigned	m_FPS[100];

	ViewPortRenderTexture* m_DSShadowRef;
	ViewPortRenderTexture* m_DSShadowRefDiff;

	unsigned	m_BenchCamPos;
	unsigned	m_LastCamPos;

	DWORD		m_LastPixelDrawed;
	int			m_PassesDone;
	
	unsigned	m_PixelSet[JITTERLIMIT+1];

	/*CMesh*		m_Tiger;
	CMesh*		m_Car;
	CMesh*		m_Arrow;
	CMesh*		m_Eifel;
	CMesh*		m_Oster;

	CMesh*		m_Plane;
	CMesh*		m_Jet;
	CMesh*		m_Heli;*/
	
	ShadowMapping* m_SM;

	DXVector3 m_SSAOSampleRays[32];
	
	ViewPortRenderTexture* m_DSWorld;
	ViewPortRenderTexture* m_DSDepth;
	ViewPortRenderTexture* m_DSNormal;
	ViewPortRenderTexture* m_DSDiffuse;
	ViewPortRenderTexture* m_OptConf;
	ViewPortRenderTexture* m_DSID;
	ViewPortRenderTexture* m_DSSSAO;
	//DepthStencilBuffer* m_DSDepthStencil;
	ShaderEffect* m_FXDS;
	ShaderEffect* m_FXOptConf;
	ShaderEffect* m_FXSSAO;
	ShaderEffect* m_FXMat;

	DXTexture* m_SSAONoiseNormal;

	float m_SMOptConfSize;	//scalefactor for confidence, needed when shadowmap gets focused
	
	VertexBuffer*			m_VBSSFrag; //all screen space fragments

	ViewPortRenderTexture* m_DSShadow[2];
	//RenderTarget* m_DSPass;
	ViewPortRenderTexture* m_DSSampleVis;

	VertexBuffer*	m_VBFSQuad;
	VertexDeclaration* m_VDeclFSQuad;
	DynamicVB* m_DynVB;

	//Terrain*	m_Terrain;

	//LList<AirPlane> m_AirPlanes;
	//LList<Object> m_Objects;

	int m_iScene;
	ShadowScene* m_pScene[2];

	//float m_ShadowFar;
	//DXVector3 m_ShadowFarPoint;
	//DXVector3 m_ShadowFarPlaneNormal;

	//AABB m_SceneBound;//bound of the entire scene
	//AABB m_VisibleBound;//bound of everything visible
	//OBB m_ShadowFrustum;//simple bound of light view
	//BoundingMesh* m_VisibleMesh;
	//D3DXPLANE m_AntiLightPlanes[5+6];//frustrum planes at on the oposite side of the light, can be up to 5; + 6 planes from ShadowFrustum
	//unsigned m_NumAntiLightPlanes;

	//ShadowRenderList m_RL;
	//ShadowRenderList m_RLShadow;

	//void UpdateShadowFarPlane(BoundingMesh* const b);

	void WlogCamPos(void);

	//void RenderShadowMap(void);

	//void RenderSceneDS(void);

	unsigned ShadowDiff(RenderTarget* rtsh, RenderTarget* rtref);

	//void MoveCam(void);

	//void UpdateShadowBound(void);

	void PromptShaderErrors(void);

	//void RenderOptConf(void);

	void DrawSampleVis(void);

	void SetupSSAO(void);
	void RenderSSAO(void);

	void RenderScene(void);
	void UpdateScene(void);
	void Restore(void);
	void Invalidate(void);
	void OnCreateDevice(void);

	void ClearFloat(const DXColor& color);

public:
	ShadowApp(void);
	~ShadowApp(void);

	
	void RenderSceneDS(void);

	void SetActive(const bool act);
	HRESULT Init(HINSTANCE hInstance);

	void DrawFrustum(const Camera& c, const D3DXCOLOR color);
	void DrawBoundingMesh(const BoundingMesh &m, const D3DXCOLOR color);
	void DrawBoundingBox(const OBB& box);

	//void ResetCam(void);

	const Camera& GetActiveCam(void) const;
	ShadowScene* GetActiveScene(void) const { return m_pScene[m_iScene]; }
	//const Camera* GetCam(void) const {return &m_Cam;}

	//DXColor GetSunColor(void) const {return m_SunColor;}
	//DXColor GetAmbientColor(void) const {return m_AmbientColor;}
	//DXVector3 GetSunDir(void) const {return m_SunDir;}

	//Terrain* GetTerrain(void) const {return m_Terrain;}
	ShadowMapping* GetShadowMapping(void) const {return m_SM;}
	bool UseDeferred(void) const {return m_Config.Method != SM_COMMON;}
	
	/*void SetCamPos(const unsigned iCamPos)
	{
		GetActiveCam()->SetPosition(m_CamPos[iCamPos]); 
		GetActiveCam()->SetLookAt(m_CamPos[iCamPos] + m_CamDir[iCamPos], m_CamUp[iCamPos]); 
		//m_Cam.SetPosition(m_CamPos[iCamPos]); 
		//m_Cam.SetLookAt(m_CamPos[iCamPos] + m_CamDir[iCamPos], m_CamUp[iCamPos]); 
		m_LightAngX = m_CamDayTime[iCamPos];
		m_LightAngZ = 0;
	}*/

	//RenderList* GetRenderList(void) {return &m_RL;}
	//RenderList* GetShadowRenderList(void) {return &m_RLShadow;}

	/*void ExpandVisibleBound(const AABB& box) { m_VisibleBound.Unify(box); }
	AABB GetVisibleBound(void) const { return m_VisibleBound; }

	AABB GetSceneBound(void) const { return m_SceneBound; }
	void ExpandSceneBound(const AABB& box) { m_SceneBound.Unify(box); }

	OBB GetShadowFrustum(void) const { return m_ShadowFrustum; }
	D3DXPLANE* GetAntiLightPlanes(void) { return m_AntiLightPlanes; }
	DWORD GetNumAntiLightPlanes(void) const { return m_NumAntiLightPlanes; }*/

	//updates the confidence texture
	void RenderOptConf(const float conf);

	void RenderQuad(const unsigned x, const unsigned y, const unsigned xl, const unsigned yl, ShaderEffect* const fx, const unsigned screenxl, const unsigned screenyl);
	void RenderFSQuad(ShaderEffect* const fx);

	bool GetLispSMEnabled(void) const { return m_Config.UseLispSM; }
	void SetLispSMEnabled(const bool lispsm) { m_Config.UseLispSM = lispsm; m_SM->SetLispSMEnable(lispsm); }

	bool GetDrawObjects(void) const { return m_Config.DrawObjects; }
	void SetDrawObjects(const bool draw) { m_Config.DrawObjects = draw; }

	bool GetUpdateDynamicObjects(void) const { return m_Config.UpdateDynamicObjects; }
	void SetUpdateDynamicObjects(const bool draw) { m_Config.UpdateDynamicObjects = draw; }

	bool GetDrawWireframe(void) const { return m_bDrawWireframe; }
	void SetDrawWireframe(const bool draw) { m_bDrawWireframe = draw; }

	int GetSMSize(void) const { return m_Config.ShadowMapSize; }
	void SetSMSize(const int size) { m_Config.ShadowMapSize = size; m_SM->SetShadowMapSize(size); }

	bool GetUseDebugCam(void) const { return m_UseDebugCam; }
	void SetUseDebugCam(const bool use) { m_UseDebugCam = use; }

	bool GetShowSM(void) const { return m_ShowSM; }
	void SetShowSM(const bool show) { m_ShowSM = show; }

	float GetConstBias(void) const { return m_Config.ConstBias; }
	void SetConstBias(const float bias) { m_Config.ConstBias = bias; }

	float GetSlopeBias(void) const { return m_Config.SlopeBias; }
	void SetSlopeBias(const float bias) { m_Config.SlopeBias = bias; }

	float GetLambertScale(void) const { return m_Config.LambertScale; }
	void SetLambertScale(const float scale) { m_Config.LambertScale = scale; }

	void ReloadShaders(void) { m_Device->GetShaderManager()->ReloadShader(false); PromptShaderErrors(); }

	//SMJITTERTYPE GetJitterType(void) const { return m_Config.JitterType; }
	//void SetJitterType(const SMJITTERTYPE jitter) { m_Config.JitterType = jitter; m_SM->SetJitterType(jitter); }

	MenuManager* GetMenuManager(void) const { return m_pMenuManager; }

	VISTYPE GetVisType(void) const { return m_VisType; }
	void SetVisType(const VISTYPE vt) { m_VisType = vt; }

	CDIFunctions* GetDI(void) { return &m_DI; }

	int GetMaxJitter(void) const { return m_Config.MaxJitter; }

	int GetSceneIndex(void) const { return m_iScene; }
	void SetScene(const int index) { m_iScene = index; }

	SHMETHOD GetShadowMethod(void) const { return m_Config.Method; }
	void SetShadowMethod(const SHMETHOD sm);

	bool GetUseIDBuffer(void) const { return m_Config.UseIDBuffer; }
	void SetUseIDBuffer(const bool useid);

	bool GetUsePrimID(void) const { return m_Config.UsePrimID; }
	void SetUsePrimID(const bool primid);

	DSWORLDPOSTYPE GetWorldPosType(void) const { return m_Config.WorldPosType; }
	void SetWorldPosType(const DSWORLDPOSTYPE wpt);

	bool GetUseSSAO(void) const { return m_Config.UseSSAO; }
	void SetUseSSAO(const bool ssao) { m_Config.UseSSAO = ssao; }

	bool GetSMFiltering(void) const { return m_Config.SMFiltering; }
	void SetSMFiltering(const bool filter);
};
