#pragma once

#include "SceneObject.h"

#include "..\Utilities\Stack.h"

//a treenode can have 3 states of visibility
/*enum VISIBLETYPE 
{
	VT_NONE = 0,
	VT_FULL,
	VT_PARTLY
};

template <const unsigned N> //N = partitioning 2 = BSP/kd, 4 = Quad, 8 = Oct
class SpacialPartitiningTreeNode : public ICullAble
{
public:
	VISIBLETYPE Visibility;
	SpacialPartitiningTreeNode<N>* Childs[N];
	List<IRenderAble*> Objects;
	
	
};*/

class SpatialPartitioningTreeNode
{
public:
	DynamicStack<SpatialPartitioningTreeNode*> m_Childs;
	LList<SceneObject> m_Objects;
	AABB m_Bound;
	OBS  m_FastBound;

	SpatialPartitioningTreeNode(const unsigned childs) : m_Childs(childs)
	{
	//	m_Visibility = VT_NONE;
		m_Bound.SetVolume(DXVector3(FLT_MAX, FLT_MAX, FLT_MAX), -DXVector3(FLT_MAX, FLT_MAX, FLT_MAX));
	}

	virtual ~SpatialPartitioningTreeNode(void) {}

	//CONTAINMENTTESTRESULT IsVisible(const Frustum& f) { return Cont_AABB_In_Frustum(m_Bound, f); }
	CONTAINMENTTESTRESULT IsVisible(const Frustum& f) { return Cont_OBS_In_Frustum(m_FastBound, f); }

	void UpdateBound(void) //sum child and object bounds
	{
		m_Bound.SetVolume(DXVector3(FLT_MAX, FLT_MAX, FLT_MAX), -DXVector3(FLT_MAX, FLT_MAX, FLT_MAX));

		for (unsigned i = 0; i < m_Childs.GetElementCount(); i++)
		{
			if (m_Childs[i])
			{
				m_Childs[i]->UpdateBound();

				m_Bound.Unify(m_Childs[i]->m_Bound);
			}
		}

		FOREACH(obj, m_Objects, SceneObject)
		{
			m_Bound.Unify(obj->GetInfo()->GetBound().BuildAABB());
		}

		m_FastBound.Center = m_Bound.GetCenter();
		m_FastBound.Radius = m_Bound.GetE().Length();
	}
};

class SpatialPartitioningTree 
{
protected:
	SpatialPartitioningTreeNode*	m_pRoot;

	void TraverseVisibleObjectsInternal(SpatialPartitioningTreeNode* const pNode, const Frustum& f, void(*todo)(SceneObject* const pObj, void* pData), void *pData)
	{
		if (pNode)
		{
			CONTAINMENTTESTRESULT vt = pNode->IsVisible(f);
			if (vt == CTR_CONTAINS)
			{
				TraverseTreeInternal(pNode, todo, pData);
			}
			else if (vt == CTR_INTERSECT)
			{
				FOREACH(obj, pNode->m_Objects, SceneObject)
				{
					if (CTR_DISJOINT != Cont_OBS_In_Frustum(obj->GetInfo()->GetBound(), f))
					{
						todo(obj->GetInfo(), pData);
					}
				}
			
				for (unsigned i = 0; i < pNode->m_Childs.GetElementCount(); i++)
				{
					TraverseVisibleObjectsInternal(pNode->m_Childs[i], f, todo, pData);
				}
			}
		}
	}

	void TraverseTreeInternal(SpatialPartitioningTreeNode* const pNode, void(*todo)(SceneObject* const pObj, void* pData), void *pData)
	{
		if (pNode)
		{
			FOREACH(obj, pNode->m_Objects, SceneObject)
			{
				todo(obj->GetInfo(), pData);
			}
		
			for (unsigned i = 0; i < pNode->m_Childs.GetElementCount(); i++)
			{
				TraverseTreeInternal(pNode->m_Childs[i], todo, pData);
			}
		}
	}

	void DeleteNode(SpatialPartitioningTreeNode* const pNode)
	{
		if(pNode)
		{
			for (unsigned i = 0; i < pNode->m_Childs.GetElementCount(); i++)
			{
				DeleteNode(pNode->m_Childs[i]);
			}
			delete pNode;
		}
	}

public:
	SpatialPartitioningTree(void) { m_pRoot = NULL; }
	virtual ~SpatialPartitioningTree(void) {} //{ DeleteNode(m_pRoot); }

	void TraverseVisibleObjects(const Frustum& f, void(*todo)(SceneObject* const pObj, void* pData), void *pData) { TraverseVisibleObjectsInternal(m_pRoot, f, todo, pData); }
	void TraverseTree(void(*todo)(SceneObject* const pObj, void* pData), void *pData) { TraverseTreeInternal(m_pRoot, todo, pData); }

	void SetRoot(SpatialPartitioningTreeNode* const root) { m_pRoot = root; }
	SpatialPartitioningTreeNode* GetRoot(void) const { return m_pRoot; }

	virtual void AddObject(SceneObject* const obj) { };
	virtual void ObjectMoved(SceneObject* const obj) { };
	virtual void RemoveObject(SceneObject* const obj) { };
};

