#pragma once

#include "..\DXFunctions\Camera.h"

//#include "DirectionalLight.h"
#include "SceneGraph.h"
#include "SpatialPartitioningTree.h"
#include "ShadowRenderList.h"

#include "ShadowApp.h"

#define CAMPOSITIONS 10

class DirectionalLight;

class ShadowScene : public DXRessource
{
protected:
	friend ShadowApp;

	ShadowApp* m_pApp;

	Camera m_Cam;
	Camera m_DebugCam;

	DXVector3	m_CamPos[CAMPOSITIONS];
	DXVector3	m_CamDir[CAMPOSITIONS];
	DXVector3	m_CamUp[CAMPOSITIONS];
	float		m_CamLightAngX[CAMPOSITIONS];
	float		m_CamLightAngZ[CAMPOSITIONS];

	VertexBuffer* m_pSkyVB;
	IndexBuffer* m_pSkyIB;
	DXCubeTexture* m_pSkyTexture;
	VertexDeclaration* m_pSkyVDecl;
	ShaderEffect* m_pSkyFX;

	DirectionalLight* m_pLight;

	SceneGraph* m_pSG; //all objects to update (dynamic, static if needed)
	SpatialPartitioningTree* m_pSPT; //all objects for culling and rendering (static & dynamic)
	
	LList<SceneObject> m_Objects;

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

	ShadowRenderList	m_RL;
	//DynamicStack<SceneObject*>	m_VisibleObjects;

	AABB m_SceneBound;//bound of the entire scene
	AABB m_VisibleBound;//bound of everything visible
	BoundingMesh* m_VisibleMesh; //exact mesh of visible volume

	//OBB m_ShadowFrustum;//simple bound of light view
	//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;

	//virtual void GatherObjects(const Frustum& f, DynamicStack<IRenderAble*>& objs) {}
	//virtual void UpdateVisibility(void);
	//virtual void UpdateScene(void) {}
	//virtual void RenderScene(void) {}

	/*static void GatherDrawCalls(SpatialPartitioningTreeNode* const pNode, void* pData)
	{
		FOREACH(obj, pNode->m_Objects, SceneObject)
		{
			((ShadowScene*)pData)->ExpandVisibleBound(obj->GetInfo()->GetBoundingVolume().BuildAABB());

			obj->GetInfo()->AddToRenderList(((ShadowScene*)pData)->m_RL);
		}
	}*/

	static void GatherDrawCalls(SceneObject* const pObj, void* pData)
	{
		ShadowScene* scene = (ShadowScene*)pData;

		if (scene->m_pApp->GetDrawObjects() || dynamic_cast<TerrainPatch*>(pObj))
		{
			scene->ExpandVisibleBound(pObj->GetBound().BuildAABB());

			pObj->AddToRenderList(((ShadowScene*)pData)->m_RL);
		}
	}

	static void UpdateTechniqueCB(SceneObject* const pObj, void* pData)
	{
		pObj->UpdateTechnique();
	}	

	void CreateSkyBox(void);
	void RenderSkyBox(const bool ds);
	void RenderSkyBoxDS(void);

	void RenderDebugCam(void);
//	void RenderSceneDS(void);

	virtual void InitCamPositions(void);

public:
	ShadowScene(ShadowApp* const pApp);
	virtual ~ShadowScene(void);

	void Update(void);
	void Render(void);

	virtual void MoveCam(void);

	virtual void ResetCam(void);

	void UpdateVisibleMesh(const Camera& cam);

	ShadowApp* GetShadowApp(void) const { return m_pApp; }

	const AABB& GetVisibleBound(void) const { return m_VisibleBound; }
	const AABB& GetSceneBound(void) const { return m_SceneBound; }
	const BoundingMesh* const GetVisibleMesh(void) const { return m_VisibleMesh; }

	void ExpandVisibleBound(const AABB& box) { m_VisibleBound.Unify(box); }
	void ExpandSceneBound(const AABB& box) { m_SceneBound.Unify(box); }

	void SetCamPos(const unsigned iCamPos)
	{
		Camera* cam = m_pApp->GetUseDebugCam() ? &m_DebugCam : &m_Cam;
		cam->SetPosition(m_CamPos[iCamPos]); 
		cam->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;
		m_LightAngX = m_CamLightAngX[iCamPos];
		m_LightAngZ = m_CamLightAngZ[iCamPos];
	}

	const Camera& GetCamera(void) const { return m_Cam; }
	const Camera& GetDebugCamera(void) const { return m_DebugCam; }

	SceneGraph* GetSceneGraph(void) const { return m_pSG; }
	SpatialPartitioningTree* GetSpatialPartitioningTree(void) const { return m_pSPT; }

	float GetLightAngX(void) const { return m_LightAngX; }
	void SetLightAngX(const float a) { m_LightAngX = a; }
	float GetLightAngZ(void) const { return m_LightAngZ; }
	void SetLightAngZ(const float a) { m_LightAngZ = a; }

	DirectionalLight* GetLight(void) const { return m_pLight; }

	void UpdateTechnique(void);
};
