#pragma once

#include <string>
#include <list>
#include <vector>
#include <map>

#include "Mix/Tool/Win32/Core/Graphics/FBX.h"
#include "Mix/Tool/Win32/Core/Graphics/Model.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Graphics{

	class BlankMesh;
	class Material;
	class MapMesh;
	class MapViewMesh;
	class Node;
	class MapCollision;

	//! @class OctreeModel
	//! @brief ８分木モデルクラス
	class OctreeModel : public Mix::Tool::Win32::Graphics::Model
	{
	public:
		//! @struct IMPORT_CONFIG
		//! @brief インポート設定構造体
		struct IMPORT_CONFIG
		{
			const wchar_t* pFilePath;	//!< ファイルパス

			Mix::Tool::Win32::Graphics::Model::IMPORT_OPTION_TYPE opt;	//!< オプション

			float scaling;				//!< スケール
			D3DXVECTOR3 rotation;		//!< 回転
			D3DXVECTOR3 translation;	//!< 移動

			D3DXVECTOR3 cellSize;		//!< 格子サイズ
			float cellError;			//!< 格子サイズの誤差の許容範囲

			bool bSmooting;				//!< スムージングを有効にする場合は true
			float smootingAngle;		//!< スムージング角度( DEG )

			Mix::Tool::Win32::Graphics::MESH_BUILD_TYPE meshBuildType;	//!< メッシュのビルドタイプ

			//! @brief コンストラクタ
			IMPORT_CONFIG( void )
			{
				pFilePath = NULL;

				opt = Mix::Tool::Win32::Graphics::Model::IO_FBX_MAYA;

				scaling = 1.0f;
				rotation = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
				translation = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );

				cellSize = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
				cellError = 0.001f;	//1.0fを1mとして考えて、初期値を1mmとする

				bSmooting = false;
				smootingAngle = 45.0f;

				meshBuildType = Mix::Tool::Win32::Graphics::MESH_BUILD_OPTIMIZE;
			}
		};

	protected:
		typedef std::vector<Mix::Tool::Win32::Graphics::MapMesh*> MeshList;
		typedef std::vector<Mix::Tool::Win32::Graphics::MapViewMesh*> ViewMeshList;
		typedef std::vector<Mix::Tool::Win32::Graphics::Node*> NodeList;

		//八分木ノード構造体
		struct OCT_NODE
		{
			//分割オーダー
			enum SPLITORDER
			{
				SO_YZ = 0,	//左右
				SO_XZ = 1,	//上下
				SO_XY = 2,	//前後
			};

			//自身の番号
			int number;

			//親ノード
			OCT_NODE* pParent;

			//子ノード
			std::vector<OCT_NODE*> childs;

			//メッシュ番号( 保存用 )
			int meshNumber;

			//メッシュ( ファイル )
			Mix::Tool::Win32::Graphics::MapMesh* pMesh;

			//メッシュ( エディット )
			OctreeModel::MeshList editMeshes;

			//ノードを囲むAABB
			Mix::Tool::Win32::Geometry::AABB aabb;

			//分割オーダー
			SPLITORDER splitOrder[3];
			int splitOrderCount;

			OCT_NODE( void )
			{
				pParent = NULL;
				number = -1;
				meshNumber = -1;
				pMesh = NULL;
				aabb.min = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
				aabb.max = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
				splitOrderCount = 0;
			}

			OCT_NODE( OCT_NODE* p0, const Mix::Tool::Win32::Geometry::AABB& p1 )
			{
				pParent = p0;
				if( pParent != NULL )
				{
					pParent->childs.push_back( this );
				}

				number = -1;
				meshNumber = -1;
				pMesh = NULL;
				aabb = p1;
				splitOrderCount = 0;
			}
		};

		typedef std::vector<OctreeModel::OCT_NODE*> OctNodeList;

	protected:
		//全般
		std::wstring m_Title;
		bool m_bRenderingOnly;

		//名前
		std::wstring m_Name;

		//保存用メッシュ
		OctreeModel::MeshList m_FileMeshList;
		OctreeModel::OctNodeList m_OctNodeList;

		//モデルエディターのアウトライナー用メッシュ
		OctreeModel::ViewMeshList m_ViewMeshList;
		OctreeModel::NodeList m_NodeList;

		//モデルディターの描画、選択用メッシュ
		OctreeModel::MeshList m_EditMeshList;
		unsigned int m_SelectedMeshKey;

	private:
		bool Optimize( const Mix::Tool::Win32::Graphics::OctreeModel::IMPORT_CONFIG& cfg, const OctreeModel::MeshList& meshes );
		void Draw( const D3DXMATRIX& worldMat, const OctreeModel::OCT_NODE* pNode, DrawObject::DRAW_EVENT_ARGS& args );
		void ReleaseModel( void );

		Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* Octree_Create( const D3DXVECTOR3& cellSize, const float cellError, Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pParentNode, const Mix::Tool::Win32::Geometry::AABB& aabb );
		void Octree_InsertPolygon( unsigned viewMeshIndex, Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pNode, const Mix::Tool::Win32::Graphics::POLYGON& polygon );
		bool Octree_Optimize1( Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pNode );
		void Octree_Optimize2( Mix::Tool::Win32::Graphics::OctreeModel::OCT_NODE* pNode );
		void Octree_Finalize( std::vector<OctreeModel::OCT_NODE*>& nodeList, OctreeModel::OCT_NODE* pNode );
		void Octree_Release( OctreeModel::OCT_NODE* pNode );

		bool FBX_LoadNode( const OctreeModel::IMPORT_CONFIG& cfg, OctreeModel::MeshList& meshes, Mix::Tool::Win32::Graphics::FBX* pFbx, FbxNode* pFbxNode, Mix::Tool::Win32::Graphics::Node* pParentNode );

		static void ReleaseMeshes( OctreeModel::MeshList& meshes );

	public:
		//! @brief コンストラクタ
		OctreeModel( const wchar_t* pTitle, bool bRenderingOnly );
		//! @brief デストラクタ
		virtual ~OctreeModel( void );

		//! @brief インポート
		//! @param[in] cfg インポート設定構造体
		//! @param[in, out] pBlankMesh ブランクメッシュクラスのポインタ( 全てのメッシュを結合したもの )
		//! @return 成功した場合は true を返します
		bool Import( const OctreeModel::IMPORT_CONFIG& cfg, Mix::Tool::Win32::Graphics::BlankMesh* pBlankMesh = NULL );
		//! @brief インポートの完了処理
		//! @return 成功した場合は true を返します
		bool ImportFinalize( void );

	protected:
		//! @brief インポートを開始する際に呼び出されます
		//! @param[in] cfg インポート設定
		virtual void OnPreImport( const OctreeModel::IMPORT_CONFIG& cfg ) {}

	public:
		//! @brief 名前を取得します
		//! @return 名前を返します
		virtual const wchar_t* GetName( void ) const;
		//! @brief ルートノードを取得します
		//! @return ルートノードのポインタを返します
		virtual Mix::Tool::Win32::Graphics::Node* GetRootNode( void );
		//! @brief 選択するメッシュのキーを設定します
		//! @param[in] key 選択するメッシュのキー
		//! @note メッシュのキーはカメラから取得することができます
		virtual void SelectedMeshByKey( unsigned int key );
		//! @brief 選択するノードを設定します
		//! @param[in] pNode 選択するノード
		virtual void SelectedNode( Mix::Tool::Win32::Graphics::Node* pNode );
		//! @brief 保存します
		//! @return 常に false を返します
		virtual bool SaveModel( void );

	protected:
		virtual void OnDraw( DrawObject::DRAW_EVENT_ARGS& args );
		virtual void OnRefreshMesh( unsigned int materialSlotIndex );

	public:
		MIX_OBJECT_TYPE( Mix::Tool::Win32::Object::GRAPHICS__OCTREE_MODEL, Mix::Tool::Win32::Graphics::Model );

		friend class Manager;
		friend class MapModel;
	};

}}}}

