#pragma once

#include "Mix/Geometry/AABB.h"
#include "Mix/Geometry/Sphere.h"
#include "Mix/Geometry/Frustum.h"

#include "Mix/Private/Container.h"
#include "Mix/Private/Stack.h"
#include "Mix/Private/Scene/Common/OctreeTypes.h"
#include "Mix/Private/Scene/Common/OctreeView.h"
#include "Mix/Private/Scene/Common/OctreeNode.h"
#include "Mix/Private/Scene/Common/OctreeObject.h"

namespace Mix{ namespace Scene{ namespace Common{

	class PointLight;
	class SpotLight;
	class WaterPool;
	class Planter;
	class ActorModel;

	class Octree
	{
	public:
		enum PUBLIC_VALUE
		{
			MAX_LEVEL = 7,
		};

	private:
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, Mix::Scene::Common::OCTREE_VIEW_DATA> ViewDataStack;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, Mix::Scene::Common::OctreeView*> ViewList;
		typedef Mix::ContainerT<Mix::Scene::Common::ScatterParticleUnitObject*, Mix::Memory::SECTION_SCENE> ScatterParticleUnitObjectList;

#ifdef _DEBUG
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, Mix::Scene::Common::LocalLightObject*> Debug_LocalLightObjectList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, Mix::Scene::Common::WaterPoolObject*> Debug_WaterPoolObjectList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, Mix::Scene::Common::PlanterObject*> Debug_PlanterObjectList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, Mix::Scene::Common::LeavingParticleObject*> Debug_LeavingParticleObjectList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, Mix::Scene::Common::ScatterParticleUnitObject*> Debug_SPUnitObjectList;
		typedef Mix::STL::Vector<Mix::Memory::SECTION_SCENE, Mix::Scene::Common::ActorModelObject*> Debug_ActorModelObjectList;
#endif //_DEBUG

	private:
		Boolean m_bInit;

		Mix::String m_Name;

		Mix::Scene::Common::IllegalOctreeNode* m_pIllegalNode;
		Mix::Scene::Common::OctreeNode** m_ppNodes;
		UInt32 m_NodeMax;
		UInt32 m_NodeNum;
		UInt32 m_Pows[Octree::MAX_LEVEL + 1];
		UInt32 m_Level;
		Mix::Geometry::AABB m_Bounds;
		Mix::Vector3 m_BoundsMin;
		Mix::Vector3 m_BoundsMax;
		Mix::Vector3 m_Unit;
		Mix::Vector3 m_InvUnit;

		Mix::Scene::Common::OctreeNode** m_ppRefreshNodes;
		UInt32 m_RefreshNodeNum;

		Octree::ViewDataStack m_ViewDataStack;

		Octree::ViewList m_ViewList;
		Octree::ScatterParticleUnitObjectList m_SPUnitObjStock;

		Mix::Scene::Common::OctreeView* m_pCurView;
		UInt32 m_CurBits[Mix::Scene::Common::OCTREE_VIEW_TYPE_MAX];
		UInt32 m_CurMask;
		Mix::ContainerT<Mix::Scene::Common::LeavingParticleObject*, Mix::Memory::SECTION_SCENE>* m_pCurValidLeavingParticleObjects;
		Mix::ContainerT<Mix::Scene::Common::ActorModelObject*, Mix::Memory::SECTION_SCENE>* m_pCurValidActorModelObjects;

		Mix::ContainerT<Mix::Scene::Common::LocalLightObject*, Mix::Memory::SECTION_SCENE> m_ValidLocalLightObjects;
		Mix::ContainerT<Mix::Scene::Common::WaterPoolObject*, Mix::Memory::SECTION_SCENE> m_ValidWaterPoolObjects;
		Mix::ContainerT<Mix::Scene::Common::PlanterObject*, Mix::Memory::SECTION_SCENE> m_ValidPlanterObjects;
		Mix::ContainerT<Mix::Scene::Common::ScatterParticleUnitObject*, Mix::Memory::SECTION_SCENE> m_ValidScatterParticleUnitObjects;
		Mix::ContainerT<Mix::Scene::Common::ActorModelObject*, Mix::Memory::SECTION_SCENE> m_TempValidActorModelObjects;
		Mix::ContainerT<Mix::Scene::Common::ActorModelObject*, Mix::Memory::SECTION_SCENE> m_TempValidShadowActorModelObjects;

#ifdef _DEBUG
		UInt32* m_pDebVisibleNodes;
		UInt32 m_DebVisibleNodeCount;

		Octree::Debug_LocalLightObjectList m_DebLocalLightObjects;
		Octree::Debug_WaterPoolObjectList m_DebWaterPoolObjects;
		Octree::Debug_PlanterObjectList m_DebPlanterObjects;
		Octree::Debug_LeavingParticleObjectList m_DebLeavingParticleObjects;
		Octree::Debug_SPUnitObjectList m_DebSPUnitObjects;
		Octree::Debug_ActorModelObjectList m_DebActorModelObjects;
#endif //_DEBUG

	public:
		Octree( const wchar_t* pName );
		~Octree( void );

		Boolean NeedsInitialize( const Mix::Vector3& aabbMin, const Mix::Vector3& aabbMax, UInt32 level ) const;

		Boolean Initialize( const Mix::Vector3& aabbMin, const Mix::Vector3& aabbMax, UInt32 level );
		void Release( void );

		UInt32 GetNodeMax( void ) const;
		UInt32 GetNodeNum( void ) const;

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

		const Mix::Vector3& GetUnit( void ) const;

		//ビューの作成 : ビット数の関係で最大16個まで作成可能
		UInt32 GetViewCount( void ) const;
		Mix::Scene::Common::OctreeView* AddView( void );

		//オブジェクトの追加
		Mix::Scene::Common::LocalLightObject* AddLocalLight( Mix::Scene::Common::PointLight* pPointLight );
		Mix::Scene::Common::LocalLightObject* AddLocalLight( Mix::Scene::Common::SpotLight* pSpotLight );
		Mix::Scene::Common::WaterPoolObject* AddWaterPool( Mix::Scene::Common::WaterPool* pWaterPool );
		Mix::Scene::Common::PlanterObject* AddPlanter( Mix::Scene::Common::Planter* pPlanter );
		Mix::Scene::Common::LeavingParticleObject* AddLeavingParticle( Mix::Scene::Common::LeavingParticle* pParticle );
		Mix::Scene::Common::ScatterParticleUnitObject* AddScatterParticleUnit( Mix::Scene::Common::ScatterParticleUnit* pParticleUnit );
		Mix::Scene::Common::ActorModelObject* AddActorModel( Mix::Scene::Common::ActorModel* pActorModel );

		//リフレッシュ : レンダラー単位で呼び出される
		void Refresh( void );

		//レンダー : ビュー単位で呼び出される
		void RenderBegin( void );
		void RenderColor( const Mix::Geometry::Frustum& frustum );
		void RenderShadow( const Mix::Geometry::Sphere& bounds );
		void RenderEnd( void );

		//現在設定されているビューでオブジェクトを有効化 : ビュー単位で呼び出す
		void ValidView( Mix::Scene::Common::OCTREE_VIEW_TYPE type );

		//フィニッシュ : レンダラー単位で呼び出される
		void Finish( void );

		//有効なオブジェクトリストの取得
		const Mix::ContainerT<Mix::Scene::Common::LocalLightObject*, Mix::Memory::SECTION_SCENE>& GetValidLocalLightObjects( void ) const;
		const Mix::ContainerT<Mix::Scene::Common::WaterPoolObject*, Mix::Memory::SECTION_SCENE>& GetValidWaterPoolObjects( void ) const;
		const Mix::ContainerT<Mix::Scene::Common::PlanterObject*, Mix::Memory::SECTION_SCENE>& GetValidPlanterObjects( void ) const;
		const Mix::ContainerT<Mix::Scene::Common::LeavingParticleObject*, Mix::Memory::SECTION_SCENE>& GetValidLeavingParticleObjects( void ) const;
		const Mix::ContainerT<Mix::Scene::Common::ScatterParticleUnitObject*, Mix::Memory::SECTION_SCENE>& GetValidScatterParticleUnitObjects( void ) const;
		const Mix::ContainerT<Mix::Scene::Common::ActorModelObject*, Mix::Memory::SECTION_SCENE>& GetValidActorModelObjects( void ) const;
		const Mix::ContainerT<Mix::Scene::Common::ActorModelObject*, Mix::Memory::SECTION_SCENE>& GetValidShadowActorModelObjects( void ) const;

#ifdef _DEBUG
		template<typename T>
		static Boolean Debug_ContainsObject( Mix::STL::Vector<Mix::Memory::SECTION_SCENE, T*>& list, const void* pInterface )
		{
			Mix::STL::Vector<Mix::Memory::SECTION_SCENE, T*>::iterator it_begin = list.begin();
			Mix::STL::Vector<Mix::Memory::SECTION_SCENE, T*>::iterator it_end = list.end();
			Mix::STL::Vector<Mix::Memory::SECTION_SCENE, T*>::iterator it;

			for( it = it_begin; it != it_end; ++it )
			{
				T* pObj = ( *it );

				if( pObj->Debug_GetInternalPtr() == pInterface )
				{
					return True;
				}
			}

			return False;
		}

		UInt32 Debug_GetIllegalLocalLightNum( void ) const;
		UInt32 Debug_GetIllegalWaterPoolNum( void ) const;
		UInt32 Debug_GetIllegalPlanterNum( void ) const;
		UInt32 Debug_GetIllegalLeavingParticleNum( void ) const;
		UInt32 Debug_GetIllegalScatterParticleUnitNum( void ) const;
		UInt32 Debug_GetIllegalActorModelNum( void ) const;

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

	private:
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void SwitchView( Mix::Scene::Common::OctreeView* pView );
		void DestroyView( Mix::Scene::Common::OctreeView* pView );

		////////////////////////////////////////////////////////////////////////////////////////////////////

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

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

		////////////////////////////////////////////////////////////////////////////////////////////////////

		Boolean RefreshNode( Mix::Scene::Common::OctreeNode* pNode );
		void RefreshParentNode( Mix::Scene::Common::OctreeNode* pChildNode );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		void RenderColor( UInt32 nodeIndex, const Mix::Geometry::Frustum& frustum, UInt32 bit );
		void RenderShadow( UInt32 nodeIndex, const Mix::Geometry::Sphere& bounds, UInt32 bit );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		void InvalidObject( Mix::Scene::Common::OCTREE_VIEW_TYPE viewType, Mix::Scene::Common::LeavingParticleObject* pObj );
		void InvalidObject( Mix::Scene::Common::OCTREE_VIEW_TYPE viewType, Mix::Scene::Common::ActorModelObject* pObj );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		Mix::Scene::Common::OctreeNode* GetNode( UInt32 index );

		Boolean GetChildBounds( Mix::Scene::Common::OctreeNode* pNode, Mix::Geometry::AABB& bounds );

		UInt32 GetMortonCode( const Mix::Vector3& abbMin, const Mix::Vector3& aabbMax );
//		UInt32 GetMortonCode( const Mix::Geometry::AABB& bounds );
		UInt32 GetMortonCode( const Mix::Geometry::Sphere& bounds );
		UInt32 GetMortonCode( UInt8 x, UInt8 y, UInt8 z );
		UInt32 BitSeparate( UInt8 n );
		UInt32 GetPointElement( const Mix::Vector3& p );

		static void SortRefreshNodes( Mix::Scene::Common::OctreeNode** list, Int32 first, Int32 last );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		template<typename T>
		static void SortValidObjects( T** list, Int32 first, Int32 last, UInt32 mask )
		{
			const T* pObj = list[( first + last ) >> 1];

			UInt32 sortKey = ( pObj->m_NxtState & mask );

			Int32 i = first;
			Int32 j = last;

			do
			{
				while( ( list[i]->m_NxtState & mask ) < sortKey ) { i++; }
				while( ( list[j]->m_NxtState & mask ) > sortKey ) { j--; }

				if( i <= j )
				{
					MIX_ASSERT( ( i <= last ) && ( j >= first ) );

					T* pTempObj = list[i];

					list[i] = list[j];
					list[j] = pTempObj;

					i++;
					j--;
				}

			}while( i <= j );

			if( first < j ) { Octree::SortValidObjects( list, first, j, mask ); }
			if( i < last ) { Octree::SortValidObjects( list, i, last, mask ); }
		}

		template<typename T>
		static UInt32 AppendValidObjects( Mix::ContainerT<T*, Mix::Memory::SECTION_SCENE>& validList,
			Mix::ContainerT<T*, Mix::Memory::SECTION_SCENE>& visibleList,
			UInt32 start, UInt32 mask, UInt32 bit )
		{
			if( visibleList.GetCount() <= start )
			{
				return start;
			}

			UInt32 nextStart = start;
			T** ppObj = &( visibleList[start] );
			T** ppObjEnd = visibleList.GetEndPtr();

			while( ( ppObj != ppObjEnd ) && ( ( ( *ppObj )->m_NxtState & mask ) == bit ) )
			{
				validList.Add( *ppObj );

				ppObj++;
				nextStart++;
			}

			return nextStart;
		}

		template<typename T>
		static void AppendValidObjects( Mix::ContainerT<T*,
			Mix::Memory::SECTION_SCENE>& colValidList,
			Mix::ContainerT<T*, Mix::Memory::SECTION_SCENE>& sdwValidList,
			Mix::ContainerT<T*, Mix::Memory::SECTION_SCENE>& visibleList,
			UInt32 start, UInt32 mask )
		{
#ifdef NDEBUG
			( void )mask;
#endif //NDEBUG

			if( visibleList.GetCount() <= start )
			{
				return;
			}

			T** ppObj = &( visibleList[start] );
			T** ppObjEnd = visibleList.GetEndPtr();

			while( ppObj != ppObjEnd )
			{
				T* pObj = *ppObj;

				MIX_ASSERT( ( pObj->m_NxtState & mask ) == mask );

				colValidList.Add( pObj );
				sdwValidList.Add( pObj );

				ppObj++;
			}
		}

		template<typename T>
		static void DestroyObjectFromView( Octree::ViewList& viewList, T* pObj )
		{
			size_t viewCount = viewList.size();

			if( viewCount == 0 )
			{
				return;
			}

			Mix::Scene::Common::OctreeView** ppView = &( viewList[0] );
			Mix::Scene::Common::OctreeView** ppViewEnd = ppView + viewCount;

			while( ppView != ppViewEnd )
			{
				( *ppView )->DestroyObject( pObj );
				ppView++;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////

		friend class ActorModelObject;
		friend class LeavingParticleObject;
		friend class OctreeView;
		friend class OctreeNode;
		friend class DefaultOctreeNode;

	private:
		static const UInt32 V_LL_OBJECTS__DEF_SIZE;
		static const UInt32 V_LL_OBJECTS__RESIZE_STEP;

		static const UInt32 V_WP_OBJECTS__DEF_SIZE;
		static const UInt32 V_WP_OBJECTS__RESIZE_STEP;

		static const UInt32 V_PT_OBJECTS__DEF_SIZE;
		static const UInt32 V_PT_OBJECTS__RESIZE_STEP;

		static const UInt32 V_SPU_OBJECTS__DEF_SIZE;
		static const UInt32 V_SPU_OBJECTS__DEF_RESIZE_STEP;

		static const UInt32 V_AM_OBJECTS__DEF_SIZE;
		static const UInt32 V_AM_OBJECTS__RESIZE_STEP;
	};

}}}
