#pragma once

#include <list>
#include <vector>

#include "Mix/Tool/Win32/Core/File/OutputStream.h"
#include "Mix/Tool/Win32/Core/Graphics/Mesh.h"
#include "Mix/Tool/Win32/Core/Graphics/ObjectModel.h"

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

	class VertexBuffer;
	class IndexBuffer;
	class Node;

	class OptimizeMesh : public Mix::Tool::Win32::Graphics::Mesh
	{
	public:
		enum VALUE
		{
			BLOCK_MAX		= 256,		//最大ブロック数( 無限ループ回避 )
			BLOCK_BONE_MAX	= 20,		//1回の描画で使用できるボーンの数( 全体で使用できるボーン数ではないことに注意！ )
		};

		//ビルドコンフィグ
		struct BUILD_CONFIG
		{
			Mix::Tool::Win32::Graphics::MESH_BUILD_TYPE type;	//ビルドタイプ
			bool bSmooting;										//スムージングを行う
			float smootingAngle;								//スムージング角度( COS )
			void* pUserPtr;										//ユーザーポインタ
		};

		//ボーン構造体
		struct BONE
		{
			std::wstring name; //名前
			Mix::Tool::Win32::Graphics::Node* pLinkNode;	//リンクしているノードのポインタ
			D3DXMATRIX offsetMat; //ボーンを所持している場合は、オフセット行列。そうでない場合は 単位行列
			D3DXMATRIX worldMat; //ワールド行列( オフセット行列 * ノード行列 )
		};

		//頂点構造体
		struct VERTEX
		{
			D3DXVECTOR3 pos;
			D3DXVECTOR4 color;
			D3DXVECTOR3 normal;
			D3DXVECTOR3 tangent;
			D3DXVECTOR3 binormal;
			D3DXVECTOR2 tex;
			unsigned char indices[Mix::Tool::Win32::Graphics::MAX_VERTEX_WEIGHT];
			float weights[Mix::Tool::Win32::Graphics::MAX_VERTEX_WEIGHT];
		};

		//描画のソート上方構造体
		struct DRAW_SORT_INFO
		{
			int boneIndex;		//影響を受けるボーンの番号( ボーンを所持していない場合は -1 )
			D3DXVECTOR3 pos;	//ソート用の座標
		};

		//描画構造体
		struct DRAW
		{
			unsigned int boneOffset;				//描画に使用するボーン行列リスト上の開始位置
			unsigned int boneCount;					//描画に使用するボーンの数
			unsigned int vertexStart;				//描画に使用する頂点のリスト上の開始位置
			unsigned int vertexCount;				//描画に使用する頂点の数
			unsigned int indexStart;				//描画に使用するインデックスのリスト上の開始位置
			unsigned int indexCount;				//描画に使用するインデックスの数
			OptimizeMesh::DRAW_SORT_INFO sortInfo;	//描画のソート情報( 半透明用 )
		};

		//サブセット構造体
		struct SUBSET
		{
			unsigned int materialIndex;					//マテリアルインデックス

			OptimizeMesh::DRAW draw;					//一括での描画
			std::vector<OptimizeMesh::DRAW> drawList;	//ポリゴン単位での描画リスト

			bool operator()( const OptimizeMesh::SUBSET& l, const OptimizeMesh::SUBSET& r )
			{
				return ( l.materialIndex < r.materialIndex );
			}
		};

	protected:
		//最適化 : 各種値
		enum O_VALUE
		{
			O_BONE_POLYGON_KEY_MAX = 12,	//ボーンポリゴンのキー最大数
		};

		//最適化 : ボーンポリゴン上方構造体
		struct O_BONE_POLYGON_INFO
		{
			unsigned int index;
			unsigned int keyCount;
			float key;

			bool operator()( const O_BONE_POLYGON_INFO& l, const O_BONE_POLYGON_INFO& r )
			{
				return ( l.key > r.key );
			}
		};

		//最適化 : ボーンポリゴンキー構造体
		struct O_BONE_POLYGON_KEY
		{
			unsigned int index;
			float value;

			O_BONE_POLYGON_KEY( unsigned int _index, float _value )
			{
				index = _index;
				value = _value;
			}
		};

		//最適化 : ボーンポリゴン構造体
		struct O_BONE_POLYGON
		{
			unsigned int keyCount;
			unsigned int keys[OptimizeMesh::O_BONE_POLYGON_KEY_MAX];

			const Mix::Tool::Win32::Graphics::POLYGON* pData;

			bool operator()( const O_BONE_POLYGON& l, const O_BONE_POLYGON& r )
			{
				unsigned int count = ( l.keyCount > r.keyCount )? l.keyCount : r.keyCount;
				unsigned int index = 0;

				while( index < OptimizeMesh::O_BONE_POLYGON_KEY_MAX )
				{
					unsigned int lk = l.keys[index];
					unsigned int rk = r.keys[index];

					if( lk < rk )
					{
						return true;
					}
					else if( lk > rk )
					{
						return false;
					}
					else if( lk == rk )
					{
						index++;
					}
				}

				return ( l.keyCount < r.keyCount );
			}
		};

		//最適化 : フェイス構造体
		struct O_FACE
		{
			unsigned int indexOffset;		//インデックス配列のオフセット
			unsigned int indices[3];		//頂点座標の参照インデックス配列

			O_FACE( unsigned int i0, unsigned int i1, unsigned int i2 )
			{
				indices[0] = i0;
				indices[1] = i1;
				indices[2] = i2;

				indexOffset = 0;
			}
		};

		//最適化 : マテリアルブロック構造体
		struct O_MATERIAL_BLOCK
		{
			unsigned int boneCount;
			unsigned int boneOffset;
			std::vector<unsigned int> boneRefTable;
			std::vector<unsigned int> boneList;

			std::vector<Mix::Tool::Win32::Graphics::VERTEX> vertexList;
			std::vector<unsigned int> vertexOptTable;

			std::vector<Mix::Tool::Win32::Graphics::POLYGON> polygonList;
			std::vector<OptimizeMesh::O_FACE> faceList;

			unsigned int indexStart;
			unsigned int indexCount;
		};

		//最適化 : マテリアル構造体
		struct O_MATERIAL
		{
			unsigned int index;	//マテリアルのインデックス
			std::vector<OptimizeMesh::O_MATERIAL_BLOCK> blockList;
			unsigned int polygonStart;
			unsigned int polygonCount;
		};

		//インデックス情報構造体(最適化用)
		struct O_INDEX_INFO
		{
			bool bAssign;
			unsigned int value;

			O_INDEX_INFO( void )
			{
				bAssign = false;
				value = 0;
			}
		};

	private:
		//MESH : マテリアルヘッダ構造体
		struct MESH_MATERIAL_DESC
		{
			unsigned int slotIndex;
			unsigned int opSubsetNum;
			unsigned int tlSubsetNum;
			unsigned int tiSubsetNum;
		};

		//MESH : シンプル不透明サブセット
		struct MESH_SIMPLE_OPACITY_SUBSET
		{
			unsigned int vertexStart;
			unsigned int vertexNum;
			unsigned int indexStart;
			unsigned int indexNum;
		};

		//MESH : ブレンド不透明サブセット
		struct MESH_BLEND_OPACITY_SUBSET
		{
			unsigned int boneOffset;
			unsigned int boneNum;
			unsigned int vertexStart;
			unsigned int vertexNum;
			unsigned int indexStart;
			unsigned int indexNum;
		};

		//MESH : シンプル半透明サブセット
		struct MESH_SIMPLE_TRANSPARENCY_SUBSET
		{
			unsigned int vertexStart;
			unsigned int vertexNum;
			unsigned int indexStart;
			unsigned int indexNum;
			float sortKey[3];
			unsigned int reserve;
		};

		//MESH : ブレンド半透明サブセット
		struct MESH_BLEND_TRANSPARENCY_SUBSET
		{
			unsigned int boneOffset;
			unsigned int boneNum;
			unsigned int affectBoneIndex;
			unsigned int vertexStart;
			unsigned int vertexNum;
			unsigned int indexStart;
			unsigned int indexNum;
			float sortKey[3];
		};

	public:
		Mix::Tool::Win32::Graphics::DrawObject* m_pDrawObject;
		Mix::Tool::Win32::Graphics::ObjectModel* m_pObjectModel;

		Mix::Tool::Win32::Graphics::Node* m_pLinkNode;

		std::vector<Mix::Tool::Win32::Graphics::POLYGON> m_PolygonList;

		Mix::Tool::Win32::Graphics::VERTEX_TYPE m_VertexType;
		Mix::Tool::Win32::Graphics::INDEX_TYPE m_IndexType;
		std::vector<OptimizeMesh::VERTEX> m_VertexList;
		std::vector<unsigned int> m_IndexList;
		std::vector<OptimizeMesh::SUBSET> m_SubsetList;

		Mix::Tool::Win32::Graphics::VertexBuffer* m_pVertexBuffer;
		Mix::Tool::Win32::Graphics::IndexBuffer* m_pIndexBuffer;

	private:
		bool Build_Direct( OptimizeMesh::BUILD_CONFIG& cfg );
		bool Build_Optimize( OptimizeMesh::BUILD_CONFIG& cfg );

		bool Build_ProcessBefore( OptimizeMesh::BUILD_CONFIG& cfg, std::vector<OptimizeMesh::O_MATERIAL>& oMaterialList );
		bool Build_ProcessAfter( OptimizeMesh::BUILD_CONFIG& cfg, const std::vector<OptimizeMesh::O_MATERIAL>& oMaterialList );

	public:
		OptimizeMesh( Mix::Tool::Win32::Graphics::DrawObject* pDrawObject, Mix::Tool::Win32::Graphics::Node* pLinkNode );
		virtual ~OptimizeMesh( void );

		//描画オブジェクトの取得
		Mix::Tool::Win32::Graphics::DrawObject* GetDrawObjectPtr( void ) const;

		//リンクノードの取得
		Mix::Tool::Win32::Graphics::Node* GetLinkNodePtr( void ) const;

		//ボーン関係
		virtual bool GetBoneAvaliable( void ) const;
		virtual unsigned int AddBone( const char* pName, const D3DXMATRIX& offsetMat );
		virtual unsigned int AddBone( const wchar_t* pName, const D3DXMATRIX& offsetMat );
		virtual unsigned int GetBoneCount( void ) const;
		virtual const D3DXMATRIX* GetBoneMatList( void ) const;
		virtual const OptimizeMesh::BONE* GetBoneByIndex( unsigned int index ) const;
		virtual const OptimizeMesh::BONE* GetBoneByName( const wchar_t* pName ) const;

		//ポリゴン関係
		void ReservePolygonList( unsigned int count );
		void AddPolygon( const Mix::Tool::Win32::Graphics::POLYGON& polygon );
		bool SetPolygonMaterial( int start, int count, int material );
		void TransformPolygons( const D3DXMATRIX& mat );
		const std::vector<Mix::Tool::Win32::Graphics::POLYGON>& GetPolygonList( void ) const;

		//ビルド
		bool Build( OptimizeMesh::BUILD_CONFIG& cfg );

		//バーテックス
		Mix::Tool::Win32::Graphics::VERTEX_TYPE GetVertexType( void ) const;
		const std::vector<OptimizeMesh::VERTEX>& GetVertexList( void ) const;

		//インデックス
		Mix::Tool::Win32::Graphics::INDEX_TYPE GetIndexType( void ) const;
		const std::vector<unsigned int>& GetIndexList( void ) const;

		//サブセット
		const std::vector<OptimizeMesh::SUBSET>& GetSubsetList( void ) const;

		// 処理内容 :
		//   中間データ( バーテックスリスト、インデックスリスト、サブセットリスト )の破棄
		//   描画リソース( バーテックスバッファ、インデックスバッファ、不透明サブセット、半透明サブセット )の破棄
		//
		// 補足 :
		//   カレントスレッドで呼び出す必要あり
		void Release( void );

		// 処理内容 :
		//   描画バッファのリフレッシュ
		//
		// 補足 :
		//   「最適化後」「マテリアルシェーダー更新後」に呼び出す必要あり( RefreshBuffer RefreshSubset )
		//   カレントスレッドで呼び出す必要あり
		bool RefreshBuffer( void );
		bool RefreshBuffer( unsigned int materialSlotIndex );

	protected:
		//ビルド前イベント
		virtual bool OnBeforeBuild( OptimizeMesh::BUILD_CONFIG& cfg ) { return true; }
		//ビルド後イベント
		virtual bool OnAfterBuild( OptimizeMesh::BUILD_CONFIG& cfg, const std::vector<OptimizeMesh::O_MATERIAL>& oMaterialList ) { return true; }

		//マテリアルの書き込み
		bool WriteSimpleMaterial( Mix::Tool::Win32::File::OutputStream& output );
		bool WriteBlendMaterial( Mix::Tool::Win32::File::OutputStream& output );

		//バッファの書き込み
		bool WriteVertexBuffer( Mix::Tool::Win32::File::OutputStream& output );
		bool WriteIndexBuffer( Mix::Tool::Win32::File::OutputStream& output );

	private:
		//カラーの描画ハンドラ
		void OnDrawColor(	const D3DXMATRIX& viewMat,
							const D3DXMATRIX& worldMat,
							std::vector<Mix::Tool::Win32::Graphics::OPACITY_SUBSET>& opacitySubsets,
							std::vector<Mix::Tool::Win32::Graphics::TRANSPARENCY_SUBSET>& transparencySubsets,
							std::vector<Mix::Tool::Win32::Graphics::REFRACT_SUBSET>& refractSubsets );

		//セレクトマップの描画ハンドラ
		void OnDrawSelectMap(	const D3DXMATRIX& viewMat,
								const D3DXMATRIX& worldMat,
								std::vector<Mix::Tool::Win32::Graphics::OPACITY_SUBSET>& subsets );

		//セレクトカラーのレンダリングイベント
		void OnDrawSelectColor(	const D3DXMATRIX& viewMat,
								const D3DXMATRIX& worldMat,
								std::vector<Mix::Tool::Win32::Graphics::TRANSPARENCY_SUBSET>& subsets );

		//ソート情報の作成
		OptimizeMesh::DRAW_SORT_INFO CreateSortInfo( bool bBoneEnabled, const OptimizeMesh::O_FACE* faceList, unsigned int faceCount );
		OptimizeMesh::DRAW_SORT_INFO CreateSortInfo( unsigned int boneCount, unsigned int vertexStart, unsigned int vertexCount );

		//頂点を書き込む
		static bool WriteVertex( const D3DVERTEXELEMENT9* pElements, const OptimizeMesh::VERTEX* pSrc, unsigned char* pDst, unsigned int stride, unsigned int count );

	public:
		virtual unsigned int GetPolygonCount( void ) const;
		virtual unsigned int GetVertexCount( void ) const;
		virtual unsigned int GetIndexCount( void ) const;
		virtual unsigned int GetSubsetCount( void ) const;
		virtual unsigned int GetMaterialSlotIndexBySubsetIndex( unsigned int subsetIndex ) const;

	public:
		MIX_OBJECT_TYPE( Mix::Tool::Win32::Object::GRAPHICS__OPTIMIZE_MESH, Mix::Tool::Win32::Graphics::Mesh );

		friend class DrawObject;
		friend class Node;
		friend class ObjectModel;
		friend class OctreeModel;
		friend class MapModel;
	};

}}}}
