#include "Mix/Private/Scene/Common/ActorDynamicsFigure.h"

#include "Mix/Private/Dynamics/KinematicCharacter.h"
#include "Mix/Private/Scene/Common/ActorRevision.h"
#include "Mix/Private/Scene/Common/ActorModel.h"
#include "Mix/Private/Scene/Common/ActorCollider.h"
#include "Mix/Private/Scene/Common/ActorKinematicCharacter.h"

namespace Mix{ namespace Scene{ namespace Common{

ActorDynamicsFigure* ActorDynamicsFigure::CreateInstance( void )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, ActorDynamicsFigure );
}

ActorDynamicsFigure::ActorDynamicsFigure( void ) :
m_pKinematicCharacter( NULL ),
m_pCastMotionCluster( NULL )
{
}

ActorDynamicsFigure::~ActorDynamicsFigure( void )
{
#ifdef _DEBUG
	if( m_pKinematicCharacter != NULL )
	{
		MIX_ASSERT( m_pKinematicCharacter->GetOwnerPtr() == NULL );
	}
#endif //_DEBUG

	MIX_RELEASE( m_pCastMotionCluster );
	MIX_RELEASE( m_pKinematicCharacter );
}

void ActorDynamicsFigure::SetCharacter( Mix::Scene::Common::ActorKinematicCharacter* pKinematicCharacter )
{
	MIX_ASSERT( pKinematicCharacter != NULL );
	MIX_ASSERT( m_pKinematicCharacter == NULL );

	MIX_ADD_REF( pKinematicCharacter );
	m_pKinematicCharacter = pKinematicCharacter;
}

Mix::Scene::Common::ActorKinematicCharacter* ActorDynamicsFigure::GetCharacterPtr( void ) const
{
	return m_pKinematicCharacter;
}

void ActorDynamicsFigure::SetReceiveMotionMatrix( const Mix::Matrix4x4& mat )
{
	m_ReceiveMotionMat = mat;
}

Boolean ActorDynamicsFigure::IsCharacterEnabled( void ) const
{
	return ( m_pKinematicCharacter != NULL )? m_pKinematicCharacter->IsEnabled() : False;
}

void ActorDynamicsFigure::SetCharacterEnabled( Boolean state )
{
	if( ( m_pKinematicCharacter != NULL ) &&
		( m_pKinematicCharacter->IsEnabled() != state ) )
	{
		m_pKinematicCharacter->SetEnabled( state );

		OnCharacterStateChanged( state );
	}
}

void ActorDynamicsFigure::SetCharacterMode( Mix::Scene::DYNAMICS_KCHAR_MODE mode )
{
	if( ( m_pKinematicCharacter != NULL ) &&
		( m_pKinematicCharacter->GetMode() != mode ) )
	{
		m_pKinematicCharacter->SetMode( mode );

		OnCharacterModeChanged( mode );
	}
}

void ActorDynamicsFigure::SetCastMotionCluster( Mix::Scene::Common::ActorDynamicsCluster* pCluster )
{
	MIX_ASSERT( pCluster != NULL );
	MIX_ASSERT( m_pCastMotionCluster == NULL );

	MIX_ADD_REF( pCluster );
	m_pCastMotionCluster = pCluster;
}

void ActorDynamicsFigure::Idling( void )
{
	if( ( m_pKinematicCharacter != NULL ) &&
		( m_pKinematicCharacter->GetMode() == Mix::Scene::DKC_RECEIVE ) )
	{
		MIX_ASSERT( m_pWorldMat != NULL );
		m_pKinematicCharacter->Update( m_pWorldMat->Remake( False, True, True ) );
	}
}

ActorDynamicsFigure* ActorDynamicsFigure::Clone( void )
{
	ActorDynamicsFigure* pCloneFigure = ActorDynamicsFigure::CreateInstance();
	if( pCloneFigure == NULL )
	{
		return NULL;
	}

	//ベース
	if( ActorDynamicsCluster::Clone( pCloneFigure ) == False )
	{
		return NULL;
	}

	//キネマティックキャラクター
	if( m_pKinematicCharacter != NULL )
	{
		Mix::Scene::Common::ActorKinematicCharacter* pCloneKinematicCharacter = m_pKinematicCharacter->Clone();

		if( pCloneKinematicCharacter != NULL )
		{
			pCloneFigure->m_pKinematicCharacter = pCloneKinematicCharacter;
			pCloneFigure->m_ReceiveMotionMat = m_ReceiveMotionMat;
		}
		else
		{
			MIX_RELEASE( pCloneKinematicCharacter );
			MIX_RELEASE( pCloneFigure );

			return NULL;
		}
	}

	//レシーブモーション行列
	pCloneFigure->m_ReceiveMotionMat = m_ReceiveMotionMat;

	//キャストモーションクラスターは後で設定
	;

	return pCloneFigure;
}

void ActorDynamicsFigure::SetLink( Mix::Scene::IRendererObject* pOwner, Mix::Matrix4x4* pParentWorldMat, Mix::Matrix4x4* pWorldMat )
{
	MIX_ASSERT( pOwner != NULL );
	MIX_ASSERT( pOwner->GetType() == Mix::Scene::IRendererObject::ACTOR_MODEL );

	Mix::Scene::Common::ActorModel* pModel = static_cast<Mix::Scene::Common::ActorModel*>( pOwner );

	ActorDynamicsCluster::SetLink( pOwner, pParentWorldMat, pWorldMat );

	if( m_pKinematicCharacter != NULL )
	{
		MIX_ASSERT( m_pKinematicCharacter->GetOwnerPtr() == NULL );
		m_pKinematicCharacter->SetOwner( pOwner );
	}

	MIX_ASSERT( pModel->GetRevisionPtr() != NULL );
	m_pRevision = pModel->GetRevisionPtr();
}

Boolean ActorDynamicsFigure::IsCollide( void ) const
{
	if( m_pKinematicCharacter != NULL )
	{
		return True;
	}

	return ActorDynamicsCluster::IsCollide();
}

Boolean ActorDynamicsFigure::IsControl( void ) const
{
	if( IsControl_Ragdoll() == True )
	{
		return True;
	}
	else if( IsControl_Character() == True )
	{
		return True;
	}

	return ActorDynamicsCluster::IsControl();
}

Boolean ActorDynamicsFigure::IsControl_Ragdoll( void ) const
{
	if( ( m_pCastMotionCluster == NULL ) ||
		( m_pCastMotionCluster->IsColliderEnabled() == False ) ||
		( GetColliderMode() != Mix::Scene::DC_RAGDOLL ) )
	{
		return False;
	}

	return True;
}

Boolean ActorDynamicsFigure::IsControl_Character( void ) const
{
	if(	( m_pKinematicCharacter == NULL ) ||
		( m_pKinematicCharacter->CanRefresh() == False ) )
	{
		return False;
	}

	return True;
}

Boolean ActorDynamicsFigure::IsActive( void ) const
{
	if( IsControl_Ragdoll() == True )
	{
		MIX_ASSERT( m_pCastMotionCluster != NULL );

		return m_pCastMotionCluster->IsActive();
	}
	else if( IsControl_Character() == True )
	{
		MIX_ASSERT( m_pKinematicCharacter != NULL );

		return m_pKinematicCharacter->IsActive();
	}

	return ActorDynamicsCluster::IsActive();
}

void ActorDynamicsFigure::AttachToWorld( Mix::Dynamics::IWorld* pWorld, Mix::Dynamics::IObjectListener* pObjectListener )
{
	ActorDynamicsCluster::AttachToWorld( pWorld, pObjectListener );

	if( m_pKinematicCharacter != NULL )
	{
		m_pKinematicCharacter->Attach( pWorld, pObjectListener );
	}
}

void ActorDynamicsFigure::DetachFromWorld( Mix::Dynamics::IWorld* pWorld )
{
	ActorDynamicsCluster::DetachFromWorld( pWorld );

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

void ActorDynamicsFigure::ResetChild( const Mix::Matrix4x4& baseMat )
{
	ActorDynamicsCluster::ResetChild( baseMat );

	if( m_pKinematicCharacter != NULL )
	{
		m_pKinematicCharacter->Reset( baseMat );
	}
}

Boolean ActorDynamicsFigure::NeedsUpdateChild( void ) const
{
	if( m_pKinematicCharacter != NULL )
	{
		return True;
	}

	return ActorDynamicsCluster::NeedsUpdateChild();
}

void ActorDynamicsFigure::UpdateChild( const Mix::Matrix4x4& baseMat )
{
	ActorDynamicsCluster::UpdateChild( baseMat );

	if( m_pKinematicCharacter != NULL )
	{
		m_pKinematicCharacter->Update( baseMat );
	}
}

Boolean ActorDynamicsFigure::Refresh( void )
{
	MIX_ASSERT( m_pWorldMat != NULL );

	Boolean result = False;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダー
	// シミュレーション後のトランスフォームを反映
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( IsControl_Ragdoll() == True )
	{
		MIX_ASSERT( m_pRevision != NULL );
		MIX_ASSERT( m_pCastMotionCluster != NULL );
		MIX_ASSERT( m_pCastMotionCluster->GetColliderPtr() != NULL );

		//ラグドール
		if( m_pRevision->IsControlWorldTransform() == True )
		{
			if( m_pCastMotionCluster->GetColliderPtr()->IsActive() == True )
			{
				*m_pWorldMat = m_ReceiveMotionMat * m_pCastMotionCluster->GetColliderMatrix();
				m_pRevision->SetWorldTransform();
				result = True;
			}
		}
	}
	else if( IsControl_Character() == True )
	{
		MIX_ASSERT( m_pRevision != NULL );
		MIX_ASSERT( m_pKinematicCharacter != NULL );

		//キネマティックキャラクター
		if( m_pRevision->IsControlWorldTransform() == True )
		{
			if( m_pKinematicCharacter->IsActive() == True )
			{
				*m_pWorldMat = m_pKinematicCharacter->Refresh( m_pWorldMat->GetScaling() );
				m_pRevision->SetWorldTransform();
				result = True;
			}
		}
	}
	else
	{
		//通常
		result = ActorDynamicsCluster::Refresh();
	}

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

	return result;
}

void ActorDynamicsFigure::Dispose( void )
{
	ActorDynamicsCluster::Dispose();

	if( m_pKinematicCharacter != NULL )
	{
		m_pKinematicCharacter->Dispose();
	}
}
#ifdef _DEBUG

void ActorDynamicsFigure::Debug_Draw(	UInt32 flags,
										Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer,
										Float32 axisScaling,
										Float32 jointFrameMinSize,
										Float32 jointLimitScaling )
{
	ActorDynamicsCluster::Debug_Draw( flags, pPerspectiveRenderer, axisScaling, jointFrameMinSize, jointLimitScaling );

	if( ( m_pKinematicCharacter != NULL ) &&
		( MIX_TESTBIT( flags, Mix::Scene::DDF_ACTORMODEL_KCHAR ) == Mix::Scene::DDF_ACTORMODEL_KCHAR ) )
	{
		Mix::Dynamics::IKinematicCharacter* pInternalKChar = m_pKinematicCharacter->GetInternalKinematicCharacterPtr();

		MIX_ASSERT( pInternalKChar != NULL );

		pInternalKChar->Debug_Draw( pPerspectiveRenderer, ( m_pKinematicCharacter->IsEnabled() == True )? 1.0f : 0.25f );
	}
}

#endif //_DEBUG

}}}
