#include "Mix/Tool/Win32/Core/Dynamics/Design/Actor.h"

#include "Mix/Tool/Win32/Core/Dynamics/World.h"
#include "Mix/Tool/Win32/Core/Dynamics/RigidBody.h"
#include "Mix/Tool/Win32/Core/Dynamics/KinematicCharacter.h"
#include "Mix/Tool/Win32/Core/Dynamics/Design/Part.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Dynamics{ namespace Design{

Actor::Actor( void ) : Basic(),
m_pKinematicCharacter( NULL ),
m_pRoot( NULL ),
m_pCastMotionPart( NULL ),
m_AngularVelocity( 0.0f, 0.0f, 0.0f, 1.0f ),
m_LinearVelocity( 0.0f, 0.0f, 0.0f )
{
	m_pSubject = this; //自身を設定しおく

	m_KCCenterPos = D3DXVECTOR3( 0.0f, Mix::Tool::Win32::Dynamics::KinematicCharacter::DefaultHeight() * 0.5f, 0.0f );
	D3DXMatrixIdentity( &m_KCStoreMat );

	D3DXMatrixIdentity( &m_ReceiveMotionMat );
}

Actor::~Actor( void )
{
	Mix::Tool::Win32::Dynamics::World* pWorld = GetWorldPtr();

	if( ( pWorld != NULL ) &&
		( m_pKinematicCharacter != NULL ) )
	{
		pWorld->RemoveCollisionObject( m_pKinematicCharacter );
	}

	MIX_DELETE( m_pRoot );
}

void Actor::Initialize( Basic::COORDINATE_SYSTEM coordinateSystem,
						const D3DXMATRIX& initalWorldMat,
						const D3DXQUATERNION& defCenterRot,
						const D3DXVECTOR3& defCenterPos )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// パラメータの設定
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	//座標系
	m_CoordinateSystem = coordinateSystem;

	//ワールド行列の初期値
	m_InitalWorldMat = initalWorldMat;

	//デフォルトトセンタートランスフォーム
	m_DefCenterRot = defCenterRot;
	m_DefCenterPos = defCenterPos;

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// センタートランスフォーム
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	ComputeObjectTransform( m_DefCenterRot, m_DefCenterPos, m_DefCenterMat, m_DefRestoreMat );

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// ワールドトランスフォーム
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	SetWorldMatrix( m_InitalWorldMat );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダーの初期の回転と位置を求める( ジョイント用 )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	ComputeInitalRigidBodyTransform( m_DefCenterMat, m_DefInitalColliderRot, m_DefInitalColliderPos );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// キネマティックキャラクターのセンタリングに関するトランスフォームを更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	KC_UpdateCenterTransform();
}

void Actor::SetWorld( Mix::Tool::Win32::Dynamics::World* pWorld )
{
	if( GetWorldPtr() != pWorld )
	{
		OnWorldChanged( Basic::WORLD_CHANGED_EVENT_ARGS( GetWorldPtr(), pWorld ) );
	}
}

Mix::Tool::Win32::Dynamics::Design::Part* Actor::CreateRoot(	const D3DXVECTOR3& localPos,
																const D3DXMATRIX& worldMat,
																const D3DXQUATERNION& centerRot,
																const D3DXVECTOR3& centerPos )
{
	MIX_DELETE( m_pRoot );

	m_pRoot = new Mix::Tool::Win32::Dynamics::Design::Part( this, NULL, GetCoordinateSystem(), localPos, worldMat, centerRot, centerPos );
	if( m_pRoot != NULL )
	{
		m_pRoot->OnWorldChanged( Basic::WORLD_CHANGED_EVENT_ARGS( NULL, GetWorldPtr() ) );
	}

	return m_pRoot;
}

Mix::Tool::Win32::Dynamics::Design::Part* Actor::CreateRoot(	const D3DXVECTOR3& localPos,
																const D3DXQUATERNION& worldRot,
																const D3DXVECTOR3& worldPos,
																const D3DXQUATERNION& centerRot,
																const D3DXVECTOR3& centerPos )
{
	D3DXMATRIX worldMat;

	D3DXMatrixRotationQuaternion( &worldMat, &worldRot );

	worldMat._41 = worldPos.x;
	worldMat._42 = worldPos.y;
	worldMat._43 = worldPos.z;

	return CreateRoot( localPos, worldMat, centerRot, centerPos );
}

Mix::Tool::Win32::Dynamics::Design::Part* Actor::GetRoot( void )
{
	return m_pRoot;
}

Mix::Tool::Win32::Dynamics::KinematicCharacter* Actor::KinematicCharacter_Get( void )
{
	return m_pKinematicCharacter;
}

void Actor::KinematicCharacter_Set( Mix::Tool::Win32::Dynamics::KinematicCharacter* pKinematicCharacter )
{
	if( m_pKinematicCharacter != pKinematicCharacter )
	{
		Mix::Tool::Win32::Dynamics::World* pWorld = GetWorldPtr();

		if( ( pWorld != NULL ) &&
			( m_pKinematicCharacter != NULL ) &&
			( GetMode() != Basic::EDIT ) )
		{
			pWorld->RemoveCollisionObject( m_pKinematicCharacter );
		}

		m_pKinematicCharacter = pKinematicCharacter;

		if( m_pKinematicCharacter != NULL )
		{
			m_pKinematicCharacter->SetWorldPosition( m_KCCenterPos );

			if( ( pWorld != NULL ) &&
				( GetMode() != Basic::EDIT ) )
			{
				pWorld->AddCollisionObject( m_pKinematicCharacter );
			}
		}
	}
}

const D3DXVECTOR3& Actor::KinematicCharacter_GetCenterPosition( void ) const
{
	return m_KCCenterPos;
}

void Actor::KinematicCharacter_SetCenterPosition( const D3DXVECTOR3& pos )
{
	m_KCCenterPos = pos;

	KC_UpdateCenterTransform();

	if( m_pKinematicCharacter != NULL )
	{
		m_pKinematicCharacter->SetWorldPosition( m_KCCenterPos );
	}
}

const D3DXMATRIX& Actor::KinematicCharacter_GetLoadMatrix( void ) const
{
	return m_KCLoadMat;
}

const D3DXMATRIX& Actor::KinematicCharacter_GetStoreMatrix( void ) const
{
	return m_KCStoreMat;
}

const D3DXMATRIX& Actor::GetReceiveMotionMatrix( void ) const
{
	return m_ReceiveMotionMat;
}

void Actor::SetMode( Basic::MODE mode )
{
	if( m_Mode != mode )
	{
		D3DXVECTOR3 impulse;

		if( mode == Basic::RAGDOLL )
		{
			impulse.x = m_WorldMat._41 - m_PreWorldMat._41;
			impulse.y = m_WorldMat._42 - m_PreWorldMat._42;
			impulse.z = m_WorldMat._43 - m_PreWorldMat._43;
		}
		else
		{
			impulse.x = 0.0f;
			impulse.y = 0.0f;
			impulse.z = 0.0f;
		}

		OnModeChanged( Basic::MODE_CHANGED_EVENT_ARGS( m_Mode, mode, impulse ) );

		m_Mode = mode;
	}
}

bool Actor::Update_NeedVelocity( void ) const
{
	if( ( GetMode() != Basic::EDIT ) &&
		( ( Collider_IsDefault() == true ) || ( m_pKinematicCharacter != NULL ) ) )
	{
		return true;
	}

	return false;
}

void Actor::Update_SetVelocity( const D3DXQUATERNION& av, const D3DXVECTOR3& lv )
{
	m_AngularVelocity = av;
	m_LinearVelocity = lv;
}

void Actor::KC_UpdateCenterTransform( void )
{
	D3DXMATRIX tempMat;
	D3DXMATRIX invInitKcMat;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ｢アクター｣ → ｢キネマティックキャラクター｣ への変換行列
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_CoordinateSystem == Basic::LEFT_HANDED )
	{
		//左手座標系
		D3DXMatrixTranslation( &m_KCLoadMat, m_KCCenterPos.x, m_KCCenterPos.y, m_KCCenterPos.z );
	}
	else
	{
		//右手座標系
		D3DXMatrixScaling( &m_KCLoadMat, +1.0f, +1.0f, -1.0f );

		m_KCLoadMat._41 = m_KCCenterPos.x;
		m_KCLoadMat._42 = m_KCCenterPos.y;
		m_KCLoadMat._43 = m_KCCenterPos.z;
		m_KCLoadMat._44 = 1.0f;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ｢キネマティックキャラクター｣ → ｢アクター｣ への変換行列
	////////////////////////////////////////////////////////////////////////////////////////////////////

	D3DXMatrixMultiply( &tempMat, &m_KCLoadMat, &Mix::Tool::Win32::Matrix_ExcludeScaling( m_InitalWorldMat ) );
	D3DXMatrixInverse( &invInitKcMat, NULL, &tempMat );

	D3DXMatrixMultiply( &m_KCStoreMat, &Mix::Tool::Win32::Matrix_ExcludeScaling( m_InitalWorldMat ), &invInitKcMat );
}

void Actor::Reset( void )
{
	ResetWorldMatrix();

	if( m_pRoot != NULL )
	{
		Reset( m_pRoot );
	}
}

void Actor::Update( void )
{
	Basic::MODE mode = GetMode();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 線形速度を設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( mode == Basic::STAND )
	{
		if( m_pKinematicCharacter != NULL )
		{
			//キネマティックキャラクターのほうが優先される
			if( m_pKinematicCharacter->IsGround() == true )
			{
				//地上にいるときのみ速度が設定可能

				if( D3DXQuaternionIsIdentity( &m_AngularVelocity ) == FALSE )
				{
					D3DXQUATERNION worldRot = m_pKinematicCharacter->GetWorldRotation();
					D3DXQUATERNION tempQuat;

					worldRot = *D3DXQuaternionMultiply( &tempQuat, &worldRot, &m_AngularVelocity );
					worldRot = *D3DXQuaternionNormalize( &tempQuat, &worldRot );

					m_pKinematicCharacter->SetWorldRotation( worldRot );
				}

				m_pKinematicCharacter->SetLinearVelocity( m_LinearVelocity );
			}
		}
		else if( Collider_IsDefault() == true )
		{
			Mix::Tool::Win32::Dynamics::RigidBody* pRigidBody = Collider_GetRigidBodyPtr();

			if( D3DXQuaternionIsIdentity( &m_AngularVelocity ) == FALSE )
			{
				pRigidBody->SetAngularVelocity( Mix::Tool::Win32::ToEulerRadianZYX( m_AngularVelocity ) );
			}

			if( D3DXVec3LengthSq( &m_LinearVelocity ) > FLT_EPSILON )
			{
				pRigidBody->SetLinearVelocity( m_LinearVelocity );
			}
		}
		else
		{
			//スタティック and キネマティック状態のコライダーはワールドトランスフォームがそのまま適用される
			;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 速度をリセット
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_AngularVelocity = D3DXQUATERNION( 0.0f, 0.0f, 0.0f, 1.0f );
	m_LinearVelocity = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
}

void Actor::Refresh( void )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// パートの更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pRoot != NULL )
	{
		m_pRoot->OnRefresh();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 自身の更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	OnRefresh();
}

void Actor::Draw(	Mix::Tool::Win32::Graphics::LineHelper* pLineHelper,
					bool bKinematicCharacter,
					bool bSensor,
					bool bBody,
					bool bJoint,
					const D3DXVECTOR4& selectedColor,
					const D3DXVECTOR4& kcColor,
					const D3DXVECTOR4& sensorColor,
					const D3DXVECTOR4& bodyColor,
					const D3DXVECTOR4& jointAxisColor,
					const D3DXVECTOR4& jointBallColor,
					const D3DXVECTOR4& jointLimitColor,
					float jointScale,
					float axisScale,
					bool bDrawPart )
{
	Basic::MODE mode = GetMode();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ベーシック
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Basic::Draw(	pLineHelper,
					bSensor,
					bBody,
					selectedColor,
					sensorColor,
					bodyColor,
					axisScale );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// キネマティックキャラクター
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( bKinematicCharacter == true ) &&
		( ( mode == Basic::EDIT ) || ( mode == Basic::STAND ) ) &&
		( m_pKinematicCharacter != NULL ) )
	{
		m_pKinematicCharacter->Draw( pLineHelper, Mix::Tool::Win32::Lerp_B( kcColor, selectedColor ) );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 子を処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( m_pRoot != NULL ) &&
		( bDrawPart == true ) )
	{
		m_pRoot->Draw(	pLineHelper,
						bSensor,
						bBody,
						bJoint,
						selectedColor,
						sensorColor,
						bodyColor,
						jointAxisColor,
						jointBallColor,
						jointLimitColor,
						jointScale,
						axisScale,
						true );
	}
}

void Actor::SetWorldMatrix( const D3DXMATRIX& worldMat )
{
	Basic::SetWorldMatrix( worldMat );

	if( m_pKinematicCharacter != NULL )
	{
		D3DXMATRIX kcMat;
		D3DXQUATERNION kcRot;
		D3DXVECTOR3 kcPos;

		D3DXMatrixMultiply( &kcMat, &m_KCLoadMat, &m_WorldMat );
		kcMat = Mix::Tool::Win32::Matrix_ExcludeScaling( kcMat );
		Mix::Tool::Win32::Matrix_Decompose( kcMat, NULL, &kcRot, &kcPos );

		m_pKinematicCharacter->SetWorldTransform( kcRot, kcPos );
	}
}

bool Actor::IsWorldMatrixRefreshed( void ) const
{
	Basic::MODE mode = GetMode();

	if( ( mode == Basic::STAND ) &&
		( m_pKinematicCharacter != NULL ) )
	{
		//スタンド状態でキネマティックキャラクターが設定されているとき
		return true;
	}

	if( ( mode == Basic::RAGDOLL ) &&
		( m_pCastMotionPart != NULL ) )
	{
		//ラグドール状態でモーショントランスミッターが存在しているとき
		return true;
	}

	return Basic::IsWorldMatrixRefreshed();
}

bool Actor::IsWorldPositionRefreshed( void ) const
{
	if( Basic::IsWorldPositionRefreshed() == true )
	{
		//ベーシック
		return true;
	}

	if( ( GetMode() == Basic::STAND ) &&
		( m_pKinematicCharacter != NULL ) )
	{
		//スタンド状態で、キネマティックキャラクターが存在しているとき( ボディのステータスがデフォルトではない場合 )
		return true;
	}

	return false;
}

void Actor::OnWorldChanged( const Basic::WORLD_CHANGED_EVENT_ARGS& args )
{
	Basic::OnWorldChanged( args );

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

	if( ( args.pOldWorld != args.pNewWorld ) &&
		( GetMode() != Basic::EDIT ) )
	{
		if( args.pOldWorld != NULL )
		{
			args.pOldWorld->RemoveCollisionObject( m_pKinematicCharacter );
		}

		if( args.pNewWorld != NULL )
		{
			args.pNewWorld->AddCollisionObject( m_pKinematicCharacter );
		}
	}

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

	if( m_pRoot != NULL )
	{
		m_pRoot->OnWorldChanged( args );
	}
}

void Actor::OnModeChanged( const Basic::MODE_CHANGED_EVENT_ARGS& args )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ベーシックの処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Basic::OnModeChanged( args );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// キネマティックキャラクターの処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pKinematicCharacter != NULL )
	{
		Mix::Tool::Win32::Dynamics::World* pWorld = GetWorldPtr();

		if( pWorld != NULL )
		{
			if( ( ( args.oldMode == Basic::EDIT ) && ( args.newMode == Basic::STAND ) ) ||
				( ( args.oldMode == Basic::RAGDOLL ) && ( args.newMode == Basic::STAND ) ) )
			{
				pWorld->AddCollisionObject( m_pKinematicCharacter );
			}
			else if(	( ( args.oldMode == Basic::STAND ) && ( args.newMode == Basic::EDIT ) ) ||
						( ( args.oldMode == Basic::RAGDOLL ) && ( args.newMode == Basic::EDIT ) ) )
			{
				pWorld->RemoveCollisionObject( m_pKinematicCharacter );
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// パートを処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pRoot != NULL )
	{
		m_pRoot->OnModeChanged( args );
	}
}

void Actor::OnColliderChanged( void )
{
	if( m_pRoot != NULL )
	{
		//パートへ通知
		m_pRoot->OnColliderChanged();
	}
}

void Actor::OnRefresh( void )
{
	Basic::MODE mode = GetMode();

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

	//コライダーが デフォルト or スタティック に設定されている場合は、キネマティックキャラクターは機能しない

	if( ( mode == Basic::STAND ) &&
		( Collider_IsDefault() == false ) &&
		( Collider_IsStatic() == false ) &&
		( m_pKinematicCharacter != NULL ) )
	{
		D3DXQUATERNION kcRot = m_pKinematicCharacter->GetWorldRotation();
		D3DXVECTOR3 kcPos = m_pKinematicCharacter->GetWorldPosition();
		D3DXMATRIX kcMat;
		D3DXMATRIX tempMat;

		D3DXMatrixScaling( &kcMat, m_WorldS.x, m_WorldS.y, m_WorldS.z );
		
		kcMat *= *D3DXMatrixRotationQuaternion( &tempMat, &kcRot );

		kcMat._41 = kcPos.x;
		kcMat._42 = kcPos.y;
		kcMat._43 = kcPos.z;
		kcMat._44 = 1.0f;

		m_PreWorldMat = m_WorldMat;
		m_WorldMat = *D3DXMatrixMultiply( &tempMat, &m_KCStoreMat, &kcMat );

		DecomposeWorldMatrix();
		Collider_UpdateTransform();

		m_bWorldPosRefreshed = true;
	}

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

	Basic::OnRefresh();
}

bool Actor::OnRefreshWorldTransform( D3DXMATRIX& worldMat )
{
	//キネマティックキャラクター、もしくわモーショントランスミッターで更新されるはずのなで、
	//無効なボディだった場合のワールドトランスフォームの更新は行わない
	return false;
}

void Actor::Reset( Mix::Tool::Win32::Dynamics::Design::Part* pPart )
{
	int childCount = pPart->GetChildCount();

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

	pPart->ResetWorldMatrix();

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

	for( int i = 0; i < childCount; i++ )
	{
		Reset( pPart->GetChildPtr( i ) );
	}
}

void Actor::UpdateCastMotionPart( void )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// キャストモーションパートを探す
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( Collider_IsAvailable() == false )
	{
		m_pCastMotionPart = FindCastMotionPart( m_pRoot );
	}
	else
	{
		m_pCastMotionPart = NULL;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// レシーブモーション行列を求める
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pCastMotionPart != NULL )
	{
		D3DXMATRIX mat;
		D3DXMATRIX invMat;

		D3DXMatrixMultiply( &mat,
							&( m_pCastMotionPart->Collider_GetCenterMatrix() ),
							&( m_pCastMotionPart->GetInitalWorldMatrix() ) );

		//スケーリングは除外しておく
		D3DXMatrixInverse( &invMat, NULL, &Mix::Tool::Win32::Matrix_ExcludeScaling( mat ) );

		D3DXMatrixMultiply( &m_ReceiveMotionMat, &m_InitalWorldMat, &invMat );
	}
	else
	{
		D3DXMatrixIdentity( &m_ReceiveMotionMat );
	}
}

Mix::Tool::Win32::Dynamics::Design::Part* Actor::FindCastMotionPart( Mix::Tool::Win32::Dynamics::Design::Part* pPart )
{
	Mix::Tool::Win32::Dynamics::Design::Part* pCastMotionPart = NULL;

	if( pPart != NULL )
	{
		if(	( ( pPart->Collider_IsDefault() == true ) || ( pPart->Collider_IsKinematic() == true ) ) &&
			( pPart->Joint_Get() == NULL ) )
		{
			//( デオフォルト or キネマティック ) and ( ジョイントが設定されていない )
			pCastMotionPart = pPart;
		}
		else
		{
			int childCount = pPart->GetChildCount();

			for( int i = 0; ( i < childCount ) && ( pCastMotionPart == NULL ); i++ )
			{
				pCastMotionPart = FindCastMotionPart( pPart->GetChildPtr( i ) );
			}
		}
	}

	return pCastMotionPart;
}

bool Actor::IsCastMotionPart( const Mix::Tool::Win32::Dynamics::Design::Basic* pBasic ) const
{
	return ( m_pCastMotionPart == pBasic );
}

bool Actor::IsCastMotion( const Mix::Tool::Win32::Dynamics::Design::Basic* pBasic )
{
	return ( GetMode() == Basic::RAGDOLL ) && ( m_pCastMotionPart == pBasic );
}

void Actor::CastMotion( const D3DXMATRIX& worldMat )
{
	SetWorldMatrix( m_ReceiveMotionMat * worldMat );
}

}}}}}
