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

#include "Mix/Dynamics/IWorld.h"
#include "Mix/Dynamics/IRigidBody.h"
#include "Mix/Dynamics/ISensor.h"

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

namespace Mix{ namespace Scene{ namespace Common{

ActorDynamicsCluster::ActorDynamicsCluster( void ) :
m_pParentWorldMat( NULL ),
m_pWorldMat( NULL ),
m_pRevision( NULL ),
m_pWorld( NULL ),
m_ColliderMode( Mix::Scene::DC_DEFAULT ),
m_pCollider( NULL ),
m_bSensorEnabled( False )
{
}

ActorDynamicsCluster::~ActorDynamicsCluster( void )
{
#ifdef _DEBUG

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// デバッグ : 破棄の確認
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_ASSERT( m_pWorld == NULL );

	/*
		コライダー
	*/

	if( m_pCollider != NULL )
	{
		MIX_ASSERT( m_pCollider->GetOwnerPtr() == NULL );
	}

	/*
		センサー
	*/

	if( m_SensorList.size() > 0 )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			MIX_ASSERT( ( *it_s )->GetOwnerPtr() == NULL );
		}
	}

#endif //_DEBUG

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 解放
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( ActorDynamicsCluster::SensorListMap::iterator it_sl = m_SensorListMap.begin(); it_sl != m_SensorListMap.end(); ++it_sl )
	{
		for( ActorDynamicsCluster::SensorList::iterator it_s = it_sl->second.begin(); it_s != it_sl->second.end(); ++it_s )
		{
			MIX_RELEASE( ( *it_s ) );
		}
	}

	MIX_RELEASE( m_pCollider );
}

Boolean ActorDynamicsCluster::Clone( ActorDynamicsCluster* pCloneCluster )
{
	MIX_ASSERT( pCloneCluster != NULL );
	MIX_ASSERT( pCloneCluster->m_pCollider == NULL );
	MIX_ASSERT( pCloneCluster->m_SensorListMap.size() == 0 );
	MIX_ASSERT( pCloneCluster->m_SensorList.size() == 0 );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダーの複製
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pCollider != NULL )
	{
		Mix::Scene::Common::ActorCollider* pCloneCollider = m_pCollider->Clone();

		if( pCloneCollider != NULL )
		{
			pCloneCluster->m_pCollider = pCloneCollider;
		}
		else
		{
			MIX_RELEASE( pCloneCollider );
			return False;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// センサーの複製
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_SensorList.size() > 0 )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			Mix::Scene::Common::ActorSensor* pCloneSensor = ( *it_s )->Clone();

			if( pCloneSensor != NULL )
			{
				pCloneCluster->AddSensor( pCloneSensor );
				MIX_RELEASE( pCloneSensor );
			}
			else
			{
				MIX_RELEASE( pCloneSensor );
				return False;
			}
		}

		pCloneCluster->FinishSensors( m_SensorList.size() );
	}

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

	return True;
}

void ActorDynamicsCluster::SetCollider( Mix::Scene::Common::ActorCollider* pCollider )
{
	MIX_ASSERT( pCollider != NULL );
	MIX_ASSERT( m_pCollider == NULL );

	MIX_ADD_REF( pCollider );
	m_pCollider = pCollider;
}

Mix::Scene::Common::ActorCollider* ActorDynamicsCluster::GetColliderPtr( void ) const
{
	return m_pCollider;
}

void ActorDynamicsCluster::AddSensor( Mix::Scene::Common::ActorSensor* pSensor )
{
	MIX_ASSERT( pSensor != NULL );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// デバッグ : 同じものを追加しようとしていないかチェック
	////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
	for( ActorDynamicsCluster::SensorListMap::iterator it_sl = m_SensorListMap.begin(); it_sl != m_SensorListMap.end(); ++it_sl )
	{
		for( ActorDynamicsCluster::SensorList::iterator it_s = it_sl->second.begin(); it_s != it_sl->second.end(); ++it_s )
		{
			MIX_ASSERT( ( *it_s ) != pSensor );
		}
	}
#endif //_DEBUG

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 追加
	////////////////////////////////////////////////////////////////////////////////////////////////////

	ActorDynamicsCluster::SensorListMap::iterator it_sl = m_SensorListMap.find( pSensor->GetName() );

	if( it_sl == m_SensorListMap.end() )
	{
		m_SensorListMap.insert( ActorDynamicsCluster::SensorListMap::value_type( pSensor->GetName(), ActorDynamicsCluster::SensorList() ) );
		it_sl = m_SensorListMap.find( pSensor->GetName() );
		MIX_ASSERT( it_sl != m_SensorListMap.end() );
	}

	MIX_ADD_REF( pSensor );
	it_sl->second.push_back( pSensor );
}

void ActorDynamicsCluster::FinishSensors( UIntT num )
{
	MIX_ASSERT( m_SensorList.size() == 0 );

	m_SensorList.reserve( num );

	for( ActorDynamicsCluster::SensorListMap::iterator it_sl = m_SensorListMap.begin(); it_sl != m_SensorListMap.end(); ++it_sl )
	{
		for( ActorDynamicsCluster::SensorList::iterator it_s = it_sl->second.begin(); it_s != it_sl->second.end(); ++it_s )
		{
			m_SensorList.push_back( ( *it_s ) );
		}
	}

	m_bSensorEnabled = True;
}

UInt32 ActorDynamicsCluster::GetSensorCount( void ) const
{
	return MIX_UIT_TO_UI32( m_SensorList.size() );
}

Mix::Scene::Common::ActorSensor* ActorDynamicsCluster::GetSensorPtr( UInt32 index ) const
{
	if( m_SensorList.size() <= index )
	{
		return NULL;
	}

	return m_SensorList[index];
}

void ActorDynamicsCluster::SetLink( Mix::Scene::IRendererObject* pOwner, Mix::Matrix4x4* pParentWorldMat, Mix::Matrix4x4* pWorldMat )
{
	MIX_ASSERT( pOwner != NULL );
	MIX_ASSERT( pWorldMat != NULL );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ワールド行列のポインタ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pParentWorldMat = pParentWorldMat;
	m_pWorldMat = pWorldMat;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// オーナーの設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		コライダー
	*/

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

	/*
		センサー
	*/

	if( m_SensorList.size() > 0 )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			MIX_ASSERT( ( *it_s )->GetOwnerPtr() == NULL );
			( *it_s )->SetOwner( pOwner );
		}
	}
}

Boolean ActorDynamicsCluster::IsColliderEnabled( void ) const
{
	return ( m_pCollider != NULL )? m_pCollider->IsEnabled() : False;
}

void ActorDynamicsCluster::SetColliderEnabled( Boolean state )
{
	if( ( m_pCollider != NULL ) &&
		( m_pCollider->IsEnabled() != state ) )
	{
		m_pCollider->SetEnabled( state );

		OnColliderStateChanged( state );
	}
}

Mix::Scene::DYNAMICS_COLLIDER_MODE ActorDynamicsCluster::GetColliderMode( void ) const
{
	return m_ColliderMode;
}

void ActorDynamicsCluster::SetColliderMode( Mix::Scene::DYNAMICS_COLLIDER_MODE mode )
{
	if( m_ColliderMode != mode )
	{
		if( m_pCollider != NULL )
		{
			m_pCollider->SetMode( mode );
		}

		m_ColliderMode = mode;

		OnColliderModeChanged( mode );
	}
}

Mix::Matrix4x4 ActorDynamicsCluster::GetColliderMatrix( void ) const
{
	MIX_ASSERT( m_pCollider != NULL );

	return m_pCollider->GetWorldMatrix();
}

Boolean ActorDynamicsCluster::IsSensorEnabled( void ) const
{
	return m_bSensorEnabled;
}

void ActorDynamicsCluster::SetSensorEnabled( Boolean state )
{
	if( ( m_SensorList.size() > 0 ) &&
		( m_bSensorEnabled != state ) )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			( *it_s )->SetEnabled( state );
		}

		m_bSensorEnabled = state;
	
		OnSensorStateChanged( state );
	}
}

Boolean ActorDynamicsCluster::IsCollide( void ) const
{
	return ( m_pCollider != NULL );
}

Boolean ActorDynamicsCluster::IsControl( void ) const
{
	if( ( m_pCollider == NULL ) ||
		( m_pCollider->CanRefresh( m_ColliderMode ) == False ) )
	{
		return False;
	}

	return True;
}

Boolean ActorDynamicsCluster::IsActive( void ) const
{
	if( ( m_pCollider == NULL ) ||
		( m_pCollider->CanRefresh( m_ColliderMode ) == False ) ||
		( m_pCollider->IsActive() == False ) )
	{
		return False;
	}

	return True;
}

void ActorDynamicsCluster::AttachToWorld( Mix::Dynamics::IWorld* pWorld, Mix::Dynamics::IObjectListener* pObjectListener )
{
	MIX_ASSERT( pWorld != NULL );
	MIX_ASSERT( pObjectListener != NULL );
	MIX_ASSERT( m_pWorld == NULL );

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// ワールド
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pWorld = pWorld;

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダー
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// センサー
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_SensorList.size() > 0 )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			( *it_s )->Attach( pWorld, pObjectListener );
		}
	}
}

void ActorDynamicsCluster::DetachFromWorld( Mix::Dynamics::IWorld* pWorld )
{
	MIX_ASSERT( pWorld != NULL );
	MIX_ASSERT( m_pWorld != NULL );
	MIX_ASSERT( m_pWorld == pWorld );

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// センサー
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_SensorList.size() > 0 )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			( *it_s )->Detach( pWorld );
		}
	}

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダー
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// ワールド
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pWorld = NULL;
}

void ActorDynamicsCluster::Reset( void )
{
	MIX_ASSERT( m_pWorldMat != NULL );

	Mix::Matrix4x4 baseMat = m_pWorldMat->Remake( False, True, True );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダー
	//   センサーのトランスフォームのベースとなる行列を取得
	//   リジッドボディのステータスがデフォルトでない場合は、外部から設定されているワールドトランスフォームを設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

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

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 子のリセット
	////////////////////////////////////////////////////////////////////////////////////////////////////

	ResetChild( baseMat );
}

void ActorDynamicsCluster::ResetChild( const Mix::Matrix4x4& baseMat )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// センサー
	//   以降に行われるワールドシミュレーションのために、トランスフォームを設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_SensorList.size() > 0 )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			( *it_s )->Reset( baseMat );
		}
	}
}

void ActorDynamicsCluster::Update( void )
{
	MIX_ASSERT( m_pRevision != NULL );

	Boolean bUpdateChild = NeedsUpdateChild();
	Mix::Matrix4x4 baseMat;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダー
	//   リジッドボディのステータスがデフォルトでない場合は、外部から設定されているワールドトランスフォームを設定
	//   以降のトランスフォームのベースとなる行列を取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pCollider != NULL )
	{
		if( m_pCollider->CanUpdateDefault( m_ColliderMode ) == True )
		{
			if( bUpdateChild == True )
			{
				baseMat = m_pCollider->UpdateDefault();
			}
		}
		else
		{
			MIX_ASSERT( m_pWorldMat != NULL );

			baseMat = m_pWorldMat->Remake( False, True, True );

			m_pCollider->UpdateKinematicOrStatic( baseMat );
		}
	}
	else
	{
		if( bUpdateChild == True )
		{
			baseMat = m_pWorldMat->Remake( False, True, True );
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 子の更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( bUpdateChild == True )
	{
		UpdateChild( baseMat );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リビジョン
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( IsControl() == True )
	{
		m_pRevision->SetControlWorldTransform();
	}
}

Boolean ActorDynamicsCluster::NeedsUpdateChild( void ) const
{
	return ( m_SensorList.size() > 0 );
}

void ActorDynamicsCluster::UpdateChild( const Mix::Matrix4x4& baseMat )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// センサー
	//   以降に行われるワールドシミュレーションのために、トランスフォームを設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_SensorList.size() > 0 )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			( *it_s )->Reset( baseMat );
		}
	}
}

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

	Boolean result = False;

	if( m_pRevision->IsControlWorldTransform() == True )
	{
		if( ActorDynamicsCluster::IsActive() == True )
		{
			*m_pWorldMat = m_pCollider->Refresh( m_pWorldMat->GetScaling() );
			m_pRevision->SetWorldTransform();
			result = True;
		}
	}

	return result;
}

void ActorDynamicsCluster::Dispose( void )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 破棄
	////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		コライダー
	*/

	if( m_pCollider != NULL )
	{
		m_pCollider->Dispose();
	}

	/*
		センサー
	*/

	if( m_SensorList.size() > 0 )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			( *it_s )->Dispose();
		}
	}

	/*
		リビジョン
	*/

	m_pRevision = NULL;
}

#ifdef _DEBUG

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

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダー
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( m_pCollider != NULL ) &&
		( MIX_TESTBIT( flags, Mix::Scene::DDF_ACTORMODEL_COLLIDER ) == Mix::Scene::DDF_ACTORMODEL_COLLIDER ) )
	{
		Mix::Dynamics::IRigidBody* pInternalRigidBody = m_pCollider->GetInternalRigidBodyPtr();
		MIX_ASSERT( pInternalRigidBody != NULL );

		Float32 opacity = ( pInternalRigidBody->IsInWorld() == True )? 1.0f : 0.25f;

		pInternalRigidBody->Debug_SetDrawAxisScaling( axisScaling );
		pInternalRigidBody->Debug_Draw( pPerspectiveRenderer, opacity );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// センサー
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( m_SensorList.size() > 0 ) &&
		( MIX_TESTBIT( flags, Mix::Scene::DDF_ACTORMODEL_SENSOR ) == Mix::Scene::DDF_ACTORMODEL_SENSOR ) )
	{
		ActorDynamicsCluster::SensorList::iterator it_s_begin = m_SensorList.begin();
		ActorDynamicsCluster::SensorList::iterator it_s_end = m_SensorList.end();
		ActorDynamicsCluster::SensorList::iterator it_s;

		for( it_s = it_s_begin; it_s != it_s_end; ++it_s )
		{
			Mix::Dynamics::ISensor* pInternalSensor = ( *it_s )->GetInternalSensorPtr();
			MIX_ASSERT( pInternalSensor != NULL );

			Float32 opacity = ( pInternalSensor->IsInWorld() == True )? 1.0f : 0.25f;

			pInternalSensor->Debug_SetDrawAxisScaling( axisScaling );
			pInternalSensor->Debug_Draw( pPerspectiveRenderer, opacity );
		}
	}
}

#endif //_DEBUG

}}}
