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

#include "Mix/Private/Dynamics/RigidBody.h"
#include "Mix/Scene/IRendererObject.h"
#include "Mix/Dynamics/IShape.h"

namespace Mix{ namespace Scene{ namespace Common{

////////////////////////////////////////////////////////////////////////////////////////////////////
// フィルター
////////////////////////////////////////////////////////////////////////////////////////////////////

// デフォルト
const UInt16 ActorCollider::DEFAULT_FILTER_GROUP	=	Mix::Dynamics::OF_DEFAULT;
const UInt16 ActorCollider::DEFAULT_FILTER_MASK		=	Mix::Dynamics::OF_DEFAULT |
														Mix::Dynamics::OF_STATIC |
														Mix::Dynamics::OF_KINEMATIC |
														Mix::Dynamics::OF_SENSOR;

//スタティック
const UInt16 ActorCollider::STATIC_FILTER_GROUP		=	Mix::Dynamics::OF_STATIC;
const UInt16 ActorCollider::STATIC_FILTER_MASK		=	Mix::Dynamics::OF_DEFAULT |
														Mix::Dynamics::OF_KINEMATIC |
														Mix::Dynamics::OF_SENSOR |
														Mix::Dynamics::OF_CHARACTER |
														Mix::Dynamics::OF_DEBRIS;

//キネマティック
const UInt16 ActorCollider::KINEMATIC_FILTER_GROUP	=	Mix::Dynamics::OF_KINEMATIC;
const UInt16 ActorCollider::KINEMATIC_FILTER_MASK	=	Mix::Dynamics::OF_DEFAULT |
														Mix::Dynamics::OF_SENSOR;

////////////////////////////////////////////////////////////////////////////////////////////////////
// ActorCollider
////////////////////////////////////////////////////////////////////////////////////////////////////

ActorCollider* ActorCollider::CreateInstance(	const wchar_t* pName,
												const Mix::Matrix4x4& centerMat,
												const Mix::Matrix4x4& restoreMat,
												Mix::Dynamics::IRigidBody* pObject,
												Boolean bCastMotion )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, ActorCollider, pName, centerMat, restoreMat, pObject, bCastMotion );
}

ActorCollider::ActorCollider(	const wchar_t* pName,
								const Mix::Matrix4x4& centerMat,
								const Mix::Matrix4x4& restoreMat,
								Mix::Dynamics::IRigidBody* pObject,
								Boolean bCastMotion ) :
m_pOwner( NULL ),
m_Name( L"" ),
m_CenterMat( centerMat ),
m_RestoreMat( restoreMat ),
m_pObject( NULL ),
m_bCastMotion( bCastMotion )
{
	MIX_ASSERT( pName != NULL );
	MIX_ASSERT( pObject != NULL );

	m_Name = pName;

	m_FilterTable[Mix::Dynamics::IRigidBody::DEFAULT].group = ActorCollider::DEFAULT_FILTER_GROUP;
	m_FilterTable[Mix::Dynamics::IRigidBody::DEFAULT].mask = ActorCollider::DEFAULT_FILTER_MASK;
	m_FilterTable[Mix::Dynamics::IRigidBody::STATIC].group = ActorCollider::STATIC_FILTER_GROUP;
	m_FilterTable[Mix::Dynamics::IRigidBody::STATIC].mask = ActorCollider::STATIC_FILTER_MASK;
	m_FilterTable[Mix::Dynamics::IRigidBody::KINEMATIC].group = ActorCollider::KINEMATIC_FILTER_GROUP;
	m_FilterTable[Mix::Dynamics::IRigidBody::KINEMATIC].mask = ActorCollider::KINEMATIC_FILTER_MASK;

	MIX_ADD_REF( pObject );
	m_pObject = pObject;

	m_DefStatus = m_pObject->GetStatus();

	UpdateFilter();

	DynamicsObject::Initialize( this );
}

ActorCollider::~ActorCollider( void )
{
	MIX_ASSERT( m_pOwner == NULL );
	MIX_RELEASE( m_pObject );
}

void ActorCollider::UpdateFilter( void )
{
	MIX_ASSERT( m_pObject != NULL );
	MIX_ASSERT( m_pObject->GetStatus() < Mix::Dynamics::RB_STATUS_MAX );

	const ActorCollider::FILTER& filter = m_FilterTable[m_pObject->GetStatus()];

	m_pObject->SetFilterGroup( filter.group );
	m_pObject->SetFilterMask( filter.mask );
}

void ActorCollider::SetOwner( Mix::Scene::IRendererObject* pOwner )
{
	MIX_ASSERT( pOwner != NULL );
	MIX_ASSERT( m_pOwner == NULL );

	m_pOwner = pOwner;
}

void ActorCollider::SetMode( Mix::Scene::DYNAMICS_COLLIDER_MODE mode )
{
	MIX_ASSERT( m_pObject != NULL );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リジッドボディのステータスを設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( mode == Mix::Scene::DC_STATIC )
	{
		//スタティック
		m_pObject->SetStatus( Mix::Dynamics::IRigidBody::STATIC );
	}
	else if( mode == Mix::Scene::DC_KINEMATIC )
	{
		//キネマティック
		m_pObject->SetStatus( Mix::Dynamics::IRigidBody::KINEMATIC );
	}
	else
	{
		//ラグドール
		if( m_DefStatus == Mix::Dynamics::IRigidBody::KINEMATIC )
		{
			if( mode == Mix::Scene::DC_DEFAULT )
			{
				m_pObject->SetStatus( Mix::Dynamics::IRigidBody::KINEMATIC );
			}
			else if( mode == Mix::Scene::DC_RAGDOLL )
			{
				m_pObject->SetStatus( Mix::Dynamics::IRigidBody::DEFAULT );
			}
		}
		else
		{
			m_pObject->SetStatus( m_DefStatus );
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// フィルターを更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	UpdateFilter();
}

void ActorCollider::Reset( const Mix::Matrix4x4& baseMat )
{
	MIX_ASSERT( m_pObject != NULL );

	Mix::Matrix4x4 worldMat = m_CenterMat * baseMat;

	m_pObject->ClearMotion( worldMat.GetRotation(), worldMat.GetTranslation() );
}

Boolean ActorCollider::CanUpdateDefault( Mix::Scene::DYNAMICS_COLLIDER_MODE mode ) const
{
	MIX_ASSERT( m_pObject != NULL );

	if( ( IsEnabled() == False ) ||
		( mode == Mix::Scene::DC_STATIC ) ||
		( mode == Mix::Scene::DC_KINEMATIC ) )
	{
		return False;
	}

	return ( m_pObject->GetStatus() == Mix::Dynamics::IRigidBody::DEFAULT );
}

Mix::Matrix4x4 ActorCollider::UpdateDefault( void )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_RestoreMat * m_pObject->GetWorldMatrix();
}

void ActorCollider::UpdateKinematicOrStatic( const Mix::Matrix4x4& baseMat )
{
	MIX_ASSERT( m_pObject != NULL );

	Mix::Matrix4x4 worldMat = m_CenterMat * baseMat;

	m_pObject->SetWorldTransform( worldMat.GetRotation(), worldMat.GetTranslation() );
}

Boolean ActorCollider::CanRefresh( Mix::Scene::DYNAMICS_COLLIDER_MODE mode ) const
{
	MIX_ASSERT( m_pObject != NULL );

	if( ( m_pObject->IsInWorld() == True ) &&
		( m_pObject->GetStatus() == Mix::Dynamics::IRigidBody::DEFAULT ) )
	{
		if( ( mode == Mix::Scene::DC_DEFAULT ) ||
			( ( mode == Mix::Scene::DC_RAGDOLL ) && ( m_bCastMotion == False ) ) )
		{
			return True;
		}
	}

	return False;
}

Mix::Matrix4x4 ActorCollider::Refresh( const Mix::Vector3& worldScaling )
{
	MIX_ASSERT( m_pObject != NULL );

	const Mix::Quaternion& worldRotation = m_pObject->GetWorldRotation();
	const Mix::Vector3& worldPosition = m_pObject->GetWorldPosition();

	Mix::Matrix4x4 worldMat( worldScaling, worldRotation, worldPosition );

	return m_RestoreMat * worldMat;
}

Boolean ActorCollider::IsCastMotion( void ) const
{
	return m_bCastMotion;
}

Mix::Dynamics::IRigidBody* ActorCollider::GetInternalRigidBodyPtr( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject;
}

ActorCollider* ActorCollider::Clone( void )
{
	ActorCollider* pDstCollider = NULL;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リジッドボディの複製
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Dynamics::IShape* pSrcShape = NULL;
	Mix::Dynamics::RigidBody* pDstObject = NULL;

	m_pObject->GetShape( &pSrcShape );
	MIX_ASSERT( pSrcShape != NULL );

	pDstObject = Mix::Dynamics::RigidBody::CreateInstance( m_pObject->GetMass(), pSrcShape );
	if( pDstObject != NULL )
	{
		if( pDstObject->Initialize( m_Name.GetConstPtr() ) == True )
		{
			pDstObject->SetMaterial( m_pObject->GetMaterial() );
			pDstObject->SetStatus( m_DefStatus );
			pDstObject->SetAngularFactor( m_pObject->GetAngularFactor() );
			pDstObject->SetAlwaysActive( m_pObject->IsAlwaysActive() );

			//ワールドトランスフォームはあとで設定
		}
		else
		{
			MIX_RELEASE( pDstObject );
			MIX_RELEASE( pSrcShape );
			return NULL;
		}
	}
	else
	{
		MIX_RELEASE( pSrcShape );
		return NULL;
	}

	MIX_RELEASE( pSrcShape );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 本体の複製
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pDstCollider = ActorCollider::CreateInstance( m_Name.GetConstPtr(), m_CenterMat, m_RestoreMat, pDstObject, m_bCastMotion );
	if( pDstCollider == NULL )
	{
		MIX_RELEASE( pDstObject );
		return NULL;
	}

	MIX_RELEASE( pDstObject );

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

	return pDstCollider;
}

void ActorCollider::Dispose( void )
{
	m_pOwner = NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::Common::DynamicsObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Dynamics::IObject* ActorCollider::GetInternalObjectPtr( void ) const
{
	return m_pObject;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IActorCollider
////////////////////////////////////////////////////////////////////////////////////////////////////

const wchar_t* ActorCollider::GetName( void ) const
{
	return m_Name.GetConstPtr();
}

UInt16 ActorCollider::GetCurrentFilterGroup( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetFilterGroup();
}

UInt16 ActorCollider::GetFilterGroup( Mix::Dynamics::IRigidBody::STATUS status ) const
{
	return m_FilterTable[status].group;
}

void ActorCollider::SetFilterGroup( Mix::Dynamics::IRigidBody::STATUS status, UInt16 value )
{
	MIX_ASSERT( m_pObject != NULL );

	if( m_pObject->GetStatus() == status )
	{
		m_pObject->SetFilterGroup( value );
	}

	m_FilterTable[status].group = value;
}

UInt16 ActorCollider::GetCurrentFilterMask( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetFilterMask();
}

UInt16 ActorCollider::GetFilterMask( Mix::Dynamics::IRigidBody::STATUS status ) const
{
	return m_FilterTable[status].mask;
}

void ActorCollider::SetFilterMask( Mix::Dynamics::IRigidBody::STATUS status, UInt16 value )
{
	MIX_ASSERT( m_pObject != NULL );

	if( m_pObject->GetStatus() == status )
	{
		m_pObject->SetFilterMask( value );
	}

	m_FilterTable[status].mask = value;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IKineticCollider
////////////////////////////////////////////////////////////////////////////////////////////////////

Float32 ActorCollider::GetMass( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetMass();
}

void ActorCollider::SetMass( Float32 mass )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->SetMass( mass );
}

const Mix::Dynamics::MATERIAL& ActorCollider::GetMaterial( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetMaterial();
}

void ActorCollider::SetMaterial( const Mix::Dynamics::MATERIAL& material )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->SetMaterial( material );
}

Boolean ActorCollider::IsAlwaysActive( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->IsAlwaysActive();
}

Boolean ActorCollider::SetAlwaysActive( Boolean state )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->SetAlwaysActive( state );
}

Boolean ActorCollider::IsActive( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return ( ( m_pObject->IsInWorld() == True ) && ( m_pObject->IsActive() == True ) );
}

Boolean ActorCollider::Activate( void )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->Activate();
}

Boolean ActorCollider::Deactivate( Boolean bForce )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->Deactivate( bForce );
}

Mix::Vector3 ActorCollider::GetLinearFactor( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetLinearFactor();
}

void ActorCollider::SetLinearFactor( const Mix::Vector3& factor )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->SetLinearFactor( factor );
}

Float32 ActorCollider::GetLinearDamping( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetLinearDamping();
}

void ActorCollider::SetLinearDamping( Float32 damping )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->SetLinearDamping( damping );
}

Mix::Vector3 ActorCollider::GetLinearVelocity( void )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetLinearVelocity();
}

Boolean ActorCollider::SetLinearVelocity( const Mix::Vector3& velocity )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->SetLinearVelocity( velocity );
}

Float32 ActorCollider::GetLinearSleepingThresholds( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetLinearSleepingThresholds();
}

void ActorCollider::SetLinearSleepingThresholds( Float32 thresholds )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->SetLinearSleepingThresholds( thresholds );
}

Mix::Vector3 ActorCollider::GetAngularFactor( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetAngularFactor();
}

void ActorCollider::SetAngularFactor( const Mix::Vector3& factor )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->SetAngularFactor( factor );
}

Float32 ActorCollider::GetAngularDamping( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetAngularDamping();
}

void ActorCollider::SetAngularDamping( Float32 damping )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->SetAngularDamping( damping );
}

Mix::Vector3 ActorCollider::GetAngularVelocity( void )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetAngularVelocity();
}

Boolean ActorCollider::SetAngularVelocity( const Mix::Vector3& velocity )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->SetAngularVelocity( velocity );
}

Float32 ActorCollider::GetAngularSleepingThresholds( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetAngularSleepingThresholds();
}

void ActorCollider::SetAngularSleepingThresholds( Float32 thresholds )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->SetAngularSleepingThresholds( thresholds );
}

Boolean ActorCollider::ApplyLinearImpulse( const Mix::Vector3& impulse )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->ApplyLinearImpulse( impulse );
}

Boolean ActorCollider::ApplyAngularImpulse( const Mix::Vector3& impulse )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->ApplyAngularImpulse( impulse );
}

Boolean ActorCollider::ApplyImpulse( const Mix::Vector3& impulse, const Mix::Vector3& pos )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->ApplyImpulse( impulse, pos );
}

Mix::Vector3 ActorCollider::GetTotalLinearForce( void )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetTotalLinearForce();
}

Mix::Vector3 ActorCollider::GetTotalAngularForce( void )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetTotalAngularForce();
}

Boolean ActorCollider::ApplyLinearForce( const Mix::Vector3& force )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->ApplyLinearForce( force );
}

Boolean ActorCollider::ApplyAngularForce( const Mix::Vector3& torque )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->ApplyAngularForce( torque );
}

Boolean ActorCollider::ApplyForce( const Mix::Vector3& force, const Mix::Vector3& pos )
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->ApplyForce( force, pos );
}

void ActorCollider::ClearForce( void )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->ClearForce();
}

void ActorCollider::ClearMotion( void )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->ClearMotion();
}

void ActorCollider::ClearMotion( const Mix::Quaternion& rot, const Mix::Vector3& pos )
{
	MIX_ASSERT( m_pObject != NULL );

	m_pObject->ClearMotion( rot, pos );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::ICollider
////////////////////////////////////////////////////////////////////////////////////////////////////

Boolean ActorCollider::IsDefault( void ) const
{
	return ( m_pObject->GetStatus() == Mix::Dynamics::IRigidBody::DEFAULT );
}

Boolean ActorCollider::IsStatic( void ) const
{
	return ( m_pObject->GetStatus() == Mix::Dynamics::IRigidBody::STATIC );
}

Boolean ActorCollider::IsKinematic( void ) const
{
	return ( m_pObject->GetStatus() == Mix::Dynamics::IRigidBody::KINEMATIC );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IDynamicsObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Scene::IDynamicsObject::TYPE ActorCollider::GetType( void ) const
{
	return Mix::Scene::IDynamicsObject::ACTOR_COLLIDER;
}

Boolean ActorCollider::GetOwner( Mix::Scene::IRendererObject** ppOwner )
{
	if( m_pOwner != NULL )
	{
		MIX_ADD_REF( m_pOwner );
		( *ppOwner ) = m_pOwner;
	}
	else
	{
		return False;
	}

	return True;
}

Mix::Scene::IRendererObject* ActorCollider::GetOwnerPtr( void ) const
{
	return m_pOwner;
}

Mix::Matrix4x4 ActorCollider::GetWorldMatrix( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetWorldMatrix();
}

Mix::Quaternion ActorCollider::GetWorldRotation( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetWorldRotation();
}

Mix::Vector3 ActorCollider::GetWorldPosition( void ) const
{
	MIX_ASSERT( m_pObject != NULL );

	return m_pObject->GetWorldPosition();
}

Boolean ActorCollider::HasContactListener( void ) const
{
	return DynamicsObject::HasContactListener();
}

Boolean ActorCollider::ContainsContactListener( Mix::Scene::IContactListener* pListener ) const
{
	return DynamicsObject::ContainsContactListener( pListener );
}

Boolean ActorCollider::AddContactListener( Mix::Scene::IContactListener* pListener )
{
	return DynamicsObject::AddContactListener( pListener );
}

Boolean ActorCollider::RemoveContactListener( Mix::Scene::IContactListener* pListener )
{
	return DynamicsObject::RemoveContactListener( pListener );
}

void ActorCollider::ClearContactListener( void )
{
	DynamicsObject::ClearContactListener();
}

Int32 ActorCollider::GetUserIndex( void )  const
{
	return DynamicsObject::GetUserIndex();
}

void ActorCollider::SetUserIndex( Int32 index )
{
	DynamicsObject::SetUserIndex( index );
}

void* ActorCollider::GetUserPtr( void ) const
{
	return DynamicsObject::GetUserPtr();
}

void ActorCollider::SetUserPtr( void* ptr )
{
	DynamicsObject::SetUserPtr( ptr );
}

}}}
