#pragma once

#include "Mix/Geometry/AABB.h"
#include "Mix/Geometry/Frustum.h"
#include "Mix/Private/Scene/Common/RendererTypes.h"
#include "Mix/Private/Scene/Common/OctreeObject.h"

namespace Mix{ namespace Scene{ namespace Common{

	class Octree;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ベース
	////////////////////////////////////////////////////////////////////////////////////////////////////

	class OctreeNode
	{
	protected:
		enum FLAG
		{
			CHANGED_OBJECTS		= 0x01,	//オブジェクトが変更された( AttachObject DetachObject )
			REQ_REFRESH_BOUNDS	= 0x02,	//境界のリフレッシュ要求
		};

		template<typename T>
		struct OBJECT_LIST
		{
			T* pTop;
			T* pBottom;
			UInt32 count;

			OBJECT_LIST( void )
			{
				pTop = NULL;
				pBottom = NULL;
				count = 0;
			}
		};

	protected:
		Mix::Scene::Common::Octree* m_pOwner;
		UInt32 m_Index;
		UInt8 m_Flags;

		OctreeNode::OBJECT_LIST<Mix::Scene::Common::LocalLightObject> m_LocalLightObjects;
		OctreeNode::OBJECT_LIST<Mix::Scene::Common::WaterPoolObject> m_WaterPoolObjects;
		OctreeNode::OBJECT_LIST<Mix::Scene::Common::PlanterObject> m_PlanterObjects;
		OctreeNode::OBJECT_LIST<Mix::Scene::Common::LeavingParticleObject> m_LeavingParticleObjects;
		OctreeNode::OBJECT_LIST<Mix::Scene::Common::ScatterParticleUnitObject> m_ScatterParticleUnitObjects;
		OctreeNode::OBJECT_LIST<Mix::Scene::Common::ActorModelObject> m_ActorModelObjects;

	public:
		OctreeNode( Mix::Scene::Common::Octree* pOwner, UInt32 index );
		virtual ~OctreeNode( void );

		UInt32 GetIndex( void ) const;

		Mix::Scene::Common::LocalLightObject* GetLocalLightObjects( void ) const;
		Mix::Scene::Common::WaterPoolObject* GetWaterPoolObjects( void ) const;
		Mix::Scene::Common::PlanterObject* GetPlanterObjects( void ) const;
		Mix::Scene::Common::LeavingParticleObject* GetLeavingParticleObjects( void ) const;
		Mix::Scene::Common::ScatterParticleUnitObject* GetScatterParticleUnitObjects( void ) const;
		Mix::Scene::Common::ActorModelObject* GetActorModelObjects( void ) const;

		virtual void AttachObject( Mix::Scene::Common::LocalLightObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::WaterPoolObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::PlanterObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::LeavingParticleObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::ScatterParticleUnitObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::ActorModelObject* pObj );

		virtual void DetachObject( Mix::Scene::Common::LocalLightObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::WaterPoolObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::PlanterObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::LeavingParticleObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::ScatterParticleUnitObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::ActorModelObject* pObj );

		void DestroyObject( Mix::Scene::Common::LocalLightObject* pObj );
		void DestroyObject( Mix::Scene::Common::WaterPoolObject* pObj );
		void DestroyObject( Mix::Scene::Common::PlanterObject* pObj );
		void DestroyObject( Mix::Scene::Common::LeavingParticleObject* pObj );
		void DestroyObject( Mix::Scene::Common::ScatterParticleUnitObject* pObj );
		void DestroyObject( Mix::Scene::Common::ActorModelObject* pObj );

		virtual UInt8 GetChilds( void ) const = 0;
		virtual void SetChilds( UInt8 childs ) = 0;

		virtual void ReqRefreshBounds( void ) = 0;
		virtual Boolean IsReqRefresh( void ) const = 0;
		virtual void Refresh( void ) = 0;

		virtual const Mix::Geometry::AABB& GetBounds( void ) const = 0;

#ifdef _DEBUG
		virtual void Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, UInt32 flags ) = 0;
#endif //_DEBUG

	private:
		template<typename T>
		static void AddObject( OctreeNode::OBJECT_LIST<T>& list, T* pObj )
		{
			MIX_ASSERT( pObj != NULL );

#ifdef _DEBUG
			const T* pDebObj = list.pTop;
			while( pDebObj != NULL )
			{
				MIX_ASSERT_EX( pDebObj != NULL, L"OctreeNode::AddObject : すでに存在しているオブジェクトを追加しようとしました" );
				pDebObj = pDebObj->m_pNext;
			}
#endif //_DEBUG

			if( list.pTop == NULL )
			{
				list.pTop = list.pBottom = pObj;
				list.pTop->m_pPrev = NULL;
				list.pTop->m_pNext = NULL;
			}
			else
			{
				pObj->m_pPrev = list.pBottom;
				pObj->m_pNext = NULL;
				list.pBottom->m_pNext = pObj;
				list.pBottom = pObj;
			}

			list.count++;
		}

		template<typename T>
		static void RemoveObject( OctreeNode::OBJECT_LIST<T>& list, T* pObj )
		{
			MIX_ASSERT( pObj != NULL );
			MIX_ASSERT( list.count > 0 );

#ifdef _DEBUG
			const T* pDebObj = list.pTop;

			while( pDebObj != NULL )
			{
				if( pDebObj == pObj )
				{
					break;
				}
				else
				{
					pDebObj = pDebObj->m_pNext;
				}
			}

			MIX_ASSERT_EX( pDebObj != NULL, L"OctreeNode::RemoveObject : 存在しないオブジェクトを削除しようとしました" );
#endif //_DEBUG

			if( ( list.pTop == pObj ) &&
				( list.pBottom == pObj ) )
			{
				list.pTop = NULL;
				list.pBottom = NULL;
			}
			else if(	( list.pTop == pObj ) &&
						( list.pBottom != pObj ) )
			{
				list.pTop = list.pTop->m_pNext;
				list.pTop->m_pPrev = NULL;
			}
			else if(	( list.pTop != pObj ) &&
						( list.pBottom == pObj ) )
			{
				list.pBottom = list.pBottom->m_pPrev;
				list.pBottom->m_pNext = NULL;
			}
			else
			{
				pObj->m_pPrev->m_pNext = pObj->m_pNext;
				pObj->m_pNext->m_pPrev = pObj->m_pPrev;
			}

			pObj->m_pPrev = NULL;
			pObj->m_pNext = NULL;

			list.count--;
		}

	protected:
		virtual void RefreshObject( Mix::Scene::Common::LocalLightObject* pObj );
		virtual void RefreshObject( Mix::Scene::Common::WaterPoolObject* pObj );
		virtual void RefreshObject( Mix::Scene::Common::PlanterObject* pObj );
		virtual void RefreshObject( Mix::Scene::Common::LeavingParticleObject* pObj );
		virtual void RefreshObject( Mix::Scene::Common::ScatterParticleUnitObject* pObj );
		virtual void RefreshObject( Mix::Scene::Common::ActorModelObject* pObj );

		friend class OctreeObject;
		friend class PointLightObject;
		friend class SpotLightObject;
		friend class WaterPoolObject;
		friend class PlanterObject;
		friend class LeavingParticleObject;
		friend class ScatterParticleUnitObject;
		friend class ActorModelObject;
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// デフォルト
	////////////////////////////////////////////////////////////////////////////////////////////////////

	class DefaultOctreeNode : public Mix::Scene::Common::OctreeNode
	{
	private:
		UInt32 m_Childs;

		Mix::Geometry::AABB m_ObjBounds;
		Mix::Geometry::AABB m_Bounds;

	public:
		DefaultOctreeNode( Mix::Scene::Common::Octree* pOwner, UInt32 index );
		virtual ~DefaultOctreeNode( void );

	public:
		virtual UInt8 GetChilds( void ) const;
		virtual void SetChilds( UInt8 childs );

		virtual void ReqRefreshBounds( void );
		virtual Boolean IsReqRefresh( void ) const;
		virtual void Refresh( void );

		virtual const Mix::Geometry::AABB& GetBounds( void ) const;

#ifdef _DEBUG
		virtual void Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, UInt32 flags );
#endif //_DEBUG
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// イリーガル
	////////////////////////////////////////////////////////////////////////////////////////////////////

	class IllegalOctreeNode : public Mix::Scene::Common::OctreeNode
	{
	public:
		IllegalOctreeNode( Mix::Scene::Common::Octree* pOwner, UInt32 index );
		virtual ~IllegalOctreeNode( void );

	public:
		virtual void AttachObject( Mix::Scene::Common::LocalLightObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::WaterPoolObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::PlanterObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::LeavingParticleObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::ScatterParticleUnitObject* pObj );
		virtual void AttachObject( Mix::Scene::Common::ActorModelObject* pObj );

		virtual void DetachObject( Mix::Scene::Common::LocalLightObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::WaterPoolObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::PlanterObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::LeavingParticleObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::ScatterParticleUnitObject* pObj );
		virtual void DetachObject( Mix::Scene::Common::ActorModelObject* pObj );

		virtual UInt8 GetChilds( void ) const;
		virtual void SetChilds( UInt8 childs );

		virtual void ReqRefreshBounds( void );
		virtual Boolean IsReqRefresh( void ) const;
		virtual void Refresh( void );

		virtual const Mix::Geometry::AABB& GetBounds( void ) const;

#ifdef _DEBUG
		UInt32 Debug_GetLocalLightNum( void ) const;
		UInt32 Debug_GetWaterPoolNum( void ) const;
		UInt32 Debug_GetPlanterNum( void ) const;
		UInt32 Debug_GetLeavingParticleNum( void ) const;
		UInt32 Debug_GetScatterParticleUnitNum( void ) const;
		UInt32 Debug_GetActorModelNum( void ) const;

		virtual void Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, UInt32 flags );
#endif //_DEBUG

	private:
		static const wchar_t* NOT_IMPL;
		static const Mix::Geometry::AABB DUMMY_BOUNDS;
	};

}}}
