#pragma once

#include <vector>
#include "Mix/Tool/Win32/Core/Object.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Dynamics{ namespace Design{
	class Part;
}}}}}

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

	class LineHelper;
	class Mesh;
	class Model;

	class Node : public Mix::Tool::Win32::Object
	{
	public:
		enum ATTRIBUTE
		{
			EMPTY	= 0,
			BONE	= 1,
			MESH	= 2,
		};

		struct DRAW_EVENT_ARGS
		{
			Mix::Tool::Win32::Graphics::LineHelper* pLineHelper;

			D3DXVECTOR4 selectedColor;
			float axisScale;

			bool bDrawBone;
			D3DXVECTOR4 boneColor;

			bool bDrawCollision;
			bool bDrawJoint;
			D3DXVECTOR4 dynSensorColor;
			D3DXVECTOR4 dynBodyColor;
			D3DXVECTOR4 dynJointAxisColor;
			D3DXVECTOR4 dynJointBallColor;
			D3DXVECTOR4 dynJointLimitColor;
			float dynJointScale;
		};

	private:
		enum PRIVATE_VALUE
		{
			JACOBI_MAX_LOOP = 100,	//Jacobi法の最大ループ数
		};

		//シェイプコンテキスト構造体
		struct SHAPE_CONTEXT
		{
			//頂点数( Phase2 )
			unsigned int vertexCount;

			//中心座標( Phase2 )
			D3DXVECTOR3 center;

			//分散共分散行列のソース( Phase3 )
			float c00;
			float c11;
			float c22;
			float c01;
			float c02;
			float c12;

			//分散共分散行列( Phase3 )
			D3DXMATRIX vcMat;

			//固有行列( Phase3 )
			D3DXMATRIX eigenMat;

			//OBBを求める際の、最大、最小値( Phase4 )
			D3DXVECTOR3 obbMin;
			D3DXVECTOR3 obbMax;

			//OBB( Phase4 )
			Mix::Tool::Win32::Geometry::OBB obb;
		};

	private:
		Mix::Tool::Win32::Graphics::Model* m_pModel;

		int m_Index;

		std::wstring m_Name;

		Mix::Tool::Win32::Graphics::Node* m_pParent;
		std::vector<Mix::Tool::Win32::Graphics::Node*> m_ChildList;

		Mix::Tool::Win32::Graphics::Node::ATTRIBUTE m_Attr;

		int m_MeshIndex;
		Mix::Tool::Win32::Graphics::Mesh* m_pMesh;

		Mix::Tool::Win32::Dynamics::Design::Part* m_pDynamicsPartDesigner;

		std::vector<D3DXVECTOR3> m_VertexList;
		std::vector<Mix::Tool::Win32::Graphics::BASE_SHAPE> m_BaseShapeList;

		D3DXVECTOR3 m_GeometricS;
		D3DXQUATERNION m_GeometricR;
		D3DXVECTOR3 m_GeometricT;
		D3DXMATRIX m_GeometricMat;

		D3DXVECTOR3 m_DefLocalS;
		D3DXQUATERNION m_DefLocalR;
		D3DXVECTOR3 m_DefLocalT;
		D3DXMATRIX m_DefLocalMat;

		D3DXVECTOR3 m_LocalS;
		D3DXQUATERNION m_LocalR;
		D3DXVECTOR3 m_LocalT;
		D3DXMATRIX m_LocalMat;

		D3DXMATRIX m_WorldMat;

	public:
		Node(	Model* pModel,
				int index,
				const char* pName,
				Node* pParent,
				const D3DXMATRIX geoMat,
				const D3DXVECTOR3& localS,
				const D3DXQUATERNION& localR,
				const D3DXVECTOR3& localT );

		Node(	Model* pModel,
				int index,
				const wchar_t* pName,
				Node* pParent,
				const D3DXMATRIX geoMat,
				const D3DXVECTOR3& localS,
				const D3DXQUATERNION& localR,
				const D3DXVECTOR3& localT );

		virtual ~Node( void );

		void SetEmpty( void );
		void SetMesh( int index, Mix::Tool::Win32::Graphics::Mesh* pPtr );
		void SetBone( void );

		void AddBaseShape( const wchar_t* pName, const std::vector<D3DXVECTOR3>& vertices );
		void FinalizeBaseShape( void );

		int GetBaseShapeCount( void ) const;
		Mix::Tool::Win32::Graphics::BASE_SHAPE* GetBaseShapePtr( int index );

		Mix::Tool::Win32::Geometry::OBB GetObb( int baseShapeIndex ); //ベースシェイプのOBB
		Mix::Tool::Win32::Geometry::OBB GetObb( int depth, bool bMeshOnly ); //ジオメトリック行列は適用せず
		Mix::Tool::Win32::Geometry::OBB GetObb( const D3DXMATRIX& mat ); //ジオメトリック行列を適用したベースシェイプ

	public:
		Mix::Tool::Win32::Graphics::Model* GetModelPtr() const;

		int GetIndex( void ) const;

		const wchar_t* GetName( void ) const;

		Mix::Tool::Win32::Graphics::Node* GetParentPtr( void ) const;

		int GetChildCount( void ) const;
		Mix::Tool::Win32::Graphics::Node* GetChildPtr( int index ) const;

		Mix::Tool::Win32::Graphics::Node::ATTRIBUTE GetAttribute( void ) const;

		int GetMeshIndex( void ) const;
		Mix::Tool::Win32::Graphics::Mesh* GetMeshPtr( void ) const;

		void SetDynamicsPartDesignerPtr( Mix::Tool::Win32::Dynamics::Design::Part* pPartDesigner );
		Mix::Tool::Win32::Dynamics::Design::Part* GetDynamicsPartDesignerPtr( void ) const;

		const D3DXVECTOR3& GetGeometricScaling( void ) const;
		const D3DXQUATERNION& GetGeometricRotation( void ) const;
		const D3DXVECTOR3& GetGeometricTranslation( void ) const;
		const D3DXMATRIX& GetGeometricMatrix( void ) const;

		const D3DXVECTOR3& GetDefLocalScaling( void ) const;
		const D3DXQUATERNION& GetDefLocalRotation( void ) const;
		const D3DXVECTOR3& GetDefLocalTranslation( void ) const;
		const D3DXMATRIX& GetDefLocalTransform( void ) const;

		const D3DXVECTOR3& GetLocalScaling( void ) const;
		void SetLocalScaling( const D3DXVECTOR3& scaling );

		const D3DXQUATERNION& GetLocalRotation( void ) const;
		void SetLocalRotation( const D3DXQUATERNION& rotation );

		const D3DXVECTOR3& GetLocalTranslation( void ) const;
		void SetLocalTranslation( const D3DXVECTOR3& translation );

		void SetLocalTransform( const D3DXVECTOR3& scaling, const D3DXQUATERNION& rotation, const D3DXVECTOR3& translation );
		void SetLocalTransform( const D3DXVECTOR3& scaling, const D3DXQUATERNION& rotation, const D3DXVECTOR3& translation, float ratio );
		const D3DXMATRIX& GetLocalMatrix( void ) const;

		const D3DXMATRIX& GetWorldMatrix( void ) const;

#if 0
		void Reset( const D3DXMATRIX& parentWorldMat );
		void ResetWorldMatrix( const D3DXMATRIX& parentWorldMat );
#endif

		void Refresh( const D3DXMATRIX& parentWorldMat );
		void RefreshWorldMatrix( const D3DXMATRIX& parentWorldMat );
		void RefreshMesh( void );

		void Draw( const Node::DRAW_EVENT_ARGS& args );

	private:
		static D3DXMATRIX ComputeTransform( const D3DXVECTOR3& scaling, const D3DXQUATERNION& rotation, const D3DXVECTOR3& translation );

		static void ComputeBaseShape_ApplyGeometricMatrix( const D3DXMATRIX& parentMat, Mix::Tool::Win32::Graphics::Node* pNode, D3DXVECTOR3& aabbMin, D3DXVECTOR3& aabbMax );

		D3DXMATRIX BaseShape_GetScalingMatrix( void );

		static void BaseShape_Node_Append( void ( *pFunc )( const std::vector<D3DXVECTOR3>&, Node::SHAPE_CONTEXT& ), Node* pNode, int maxDepth, Node::SHAPE_CONTEXT& ctx, bool bMeshOnly );
		static void BaseShape_Node_Append( void ( *pFunc )( const std::vector<D3DXVECTOR3>&, Node::SHAPE_CONTEXT& ), const D3DXMATRIX& mat, Node* pNode, Node::SHAPE_CONTEXT& ctx );
		static void BaseShape_Node_Append( void ( *pFunc )( const std::vector<D3DXVECTOR3>&, Node::SHAPE_CONTEXT& ), const D3DXMATRIX& mat, Node* pNode, int maxDepth, Node::SHAPE_CONTEXT& ctx, bool bMeshOnly, int depth, std::vector<D3DXVECTOR3>& work );

		static void BaseShape_Phase1_Initialize( Node::SHAPE_CONTEXT& ctx );
		static void BaseShape_Phase2_Center_Append( const std::vector<D3DXVECTOR3>& vertices, Node::SHAPE_CONTEXT& ctx );
		static void BaseShape_Phase2_Center_Finish( Node::SHAPE_CONTEXT& ctx );
		static void BaseShape_Phase3_Matrix_Append( const std::vector<D3DXVECTOR3>& vertices, Node::SHAPE_CONTEXT& ctx );
		static void BaseShape_Phase3_Matrix_Finish( Node::SHAPE_CONTEXT& ctx );
		static void BaseShape_Phase4_OBB_Append( const std::vector<D3DXVECTOR3>& vertices, Node::SHAPE_CONTEXT& ctx );
		static void BaseShape_Phase4_OBB_Finish( Node::SHAPE_CONTEXT& ctx );

		static float Jacobi_GetMax( const D3DXMATRIX& mat, int& p, int& q );

	public:
		virtual Mix::Tool::Win32::Object::TYPE GetType( void ) const;
	};

}}}}
