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

#include "Mix/Dynamics/IRigidBody.h"
#include "Mix/Scene/IContactListener.h"
#include "Mix/Private/Scene/Common/ActorDynamicsFigure.h"
#include "Mix/Private/Scene/Common/ActorDynamicsPart.h"
#include "Mix/Private/Scene/Common/ActorCollider.h"
#include "Mix/Private/Scene/Common/ActorSensor.h"

namespace Mix{ namespace Scene{ namespace Common{

ActorDynamicsDirector* ActorDynamicsDirector::CreateInstance( const wchar_t* pDebugName )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, ActorDynamicsDirector, pDebugName );
}

ActorDynamicsDirector::ActorDynamicsDirector( const wchar_t* pDebugName ) :
m_KCharMode( Mix::Scene::DKC_CAST ),
m_bColliderEnabled( True ),
m_ColliderMode( Mix::Scene::DC_DEFAULT ),
m_bSensorEnabled( True ),
m_pFigure( NULL ),
m_pColliderCollection( NULL ),
m_pSensorCollection( NULL ),
m_bActive( True ),
m_bDisposed( False )
{
	MIX_ASSERT( pDebugName != NULL );

	btRigidBody::btRigidBodyConstructionInfo dummy( 0.0f, NULL, NULL );
	Mix::StringW tempStr;

	tempStr.Sprintf( L"%s/DynamicsDirector", pDebugName );
	m_Commands.Initialize( ActorDynamicsDirector::CQ_DEF_CAPACITY, ActorDynamicsDirector::CQ_RESIZE_STEP, tempStr.GetConstPtr() );

	m_LinearSleepingThreshold = dummy.m_linearSleepingThreshold;
	m_AngularSleepingThreshold = dummy.m_angularSleepingThreshold;
}

ActorDynamicsDirector::~ActorDynamicsDirector( void )
{
	for( ActorDynamicsDirector::ClusterList::iterator it = m_ClusterList.begin(); it != m_ClusterList.end(); ++it )
	{
		MIX_RELEASE( ( *it ) );
	}

	for( ActorDynamicsDirector::ColliderCollectionMap::iterator it = m_ColliderCollectionMap.begin(); it != m_ColliderCollectionMap.end(); ++it )
	{
		MIX_RELEASE( it->second );
	}

	for( ActorDynamicsDirector::SensorCollectionMap::iterator it = m_SensorCollectionMap.begin(); it != m_SensorCollectionMap.end(); ++it )
	{
		MIX_RELEASE( it->second );
	}

	MIX_RELEASE( m_pSensorCollection );
	MIX_RELEASE( m_pColliderCollection );

	//クラスターの参照カウンタを +1 しているので、コライダー、センサーは解放する必要はない
}

Boolean ActorDynamicsDirector::Initialize( void )
{
	MIX_ASSERT( m_pColliderCollection == NULL );
	MIX_ASSERT( m_pSensorCollection == NULL );

	m_pColliderCollection = ActorDynamicsDirector::ColliderCollection::CreateInstance();
	if( m_pColliderCollection == NULL )
	{
		return False;
	}

	m_pSensorCollection = ActorDynamicsDirector::SensorCollection::CreateInstance();
	if( m_pSensorCollection == NULL )
	{
		return False;
	}

	return True;
}

void ActorDynamicsDirector::Dispose( void )
{
	m_bDisposed = True;
}

Boolean ActorDynamicsDirector::SetFigure( Mix::Scene::Common::ActorDynamicsFigure* pFigure )
{
	MIX_ASSERT( pFigure != NULL );
	MIX_ASSERT( m_bDisposed == False );
	MIX_ASSERT( m_pFigure == NULL );

	m_pFigure = pFigure;

	return AddCluster( pFigure );
}

Boolean ActorDynamicsDirector::AddPart( Mix::Scene::Common::ActorDynamicsPart* pPart )
{
	return AddCluster( pPart );
}

Boolean ActorDynamicsDirector::AddCluster( Mix::Scene::Common::ActorDynamicsCluster* pCluster )
{
	MIX_ASSERT( pCluster != NULL );
	MIX_ASSERT( m_bDisposed == False );
	MIX_ASSERT( m_pColliderCollection != NULL );
	MIX_ASSERT( m_pSensorCollection != NULL );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// クラスターを追加
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_ADD_REF( pCluster );
	m_ClusterList.push_back( pCluster );

	if( pCluster->IsCollide() == True )
	{
		//活動状態の確認するための衝突するクラスター( キネマティックキャラクター、もしくわコライダーを所持 )
		m_CollideClusterList.push_back( pCluster );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダーを追加
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pCluster->GetColliderPtr() != NULL )
	{
		Mix::Scene::Common::ActorCollider* pCollider = pCluster->GetColliderPtr();
		ActorDynamicsDirector::ColliderCollection* pGroupColliderCollection = NULL;

		ActorDynamicsDirector::ColliderCollectionMap::iterator it_cc = m_ColliderCollectionMap.find( pCollider->GetName() );
		if( it_cc == m_ColliderCollectionMap.end() )
		{
			pGroupColliderCollection = ActorDynamicsDirector::ColliderCollection::CreateInstance();

			if( pGroupColliderCollection != NULL )
			{
				m_ColliderCollectionMap.insert( ActorDynamicsDirector::ColliderCollectionMap::value_type( pCollider->GetName(), pGroupColliderCollection ) );
			}
			else
			{
				return False;
			}
		}
		else
		{
			pGroupColliderCollection = it_cc->second;
		}

		//コレクションに追加
		pGroupColliderCollection->Add( pCollider );
		m_pColliderCollection->Add( pCollider );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// センサーを追加
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pCluster->GetSensorCount() > 0 )
	{
		UInt32 sensorCount = pCluster->GetSensorCount();

		for( UInt32 i = 0; i < sensorCount; i++ )
		{
			Mix::Scene::Common::ActorSensor* pSensor = pCluster->GetSensorPtr( i );
			ActorDynamicsDirector::SensorCollection* pGroupSensorCollection = NULL;

			ActorDynamicsDirector::SensorCollectionMap::iterator it_sc = m_SensorCollectionMap.find( pSensor->GetName() );
			if( it_sc == m_SensorCollectionMap.end() )
			{
				pGroupSensorCollection = ActorDynamicsDirector::SensorCollection::CreateInstance();

				if( pGroupSensorCollection != NULL )
				{
					m_SensorCollectionMap.insert( ActorDynamicsDirector::SensorCollectionMap::value_type( pSensor->GetName(), pGroupSensorCollection ) );
				}
				else
				{
					return False;
				}
			}

			//コレクションに追加
			pGroupSensorCollection->Add( pSensor );
			m_pSensorCollection->Add( pSensor );
		}
	}

	return True;
}

void ActorDynamicsDirector::AttachToWorld( Mix::Dynamics::IWorld* pWorld, Mix::Dynamics::IObjectListener* pObjectListener )
{
	MIX_ASSERT( pWorld != NULL );
	MIX_ASSERT( pObjectListener != NULL );
	MIX_ASSERT( m_bDisposed == False );

	for( ActorDynamicsDirector::ClusterList::iterator it = m_ClusterList.begin(); it != m_ClusterList.end(); ++it )
	{
		( *it )->AttachToWorld( pWorld, pObjectListener );
	}
}

void ActorDynamicsDirector::DetachFromWorld( Mix::Dynamics::IWorld* pWorld )
{
	MIX_ASSERT( pWorld != NULL );
	MIX_ASSERT( m_bDisposed == False );

	for( ActorDynamicsDirector::ClusterList::iterator it = m_ClusterList.begin(); it != m_ClusterList.end(); ++it )
	{
		( *it )->DetachFromWorld( pWorld );
	}
}

void ActorDynamicsDirector::Reset( void )
{
	MIX_ASSERT( m_bDisposed == False );

	UIntT clusterNum = m_ClusterList.size();

	if( clusterNum > 0 )
	{
		Mix::Scene::Common::ActorDynamicsCluster** ppCluster = &m_ClusterList[0];
		Mix::Scene::Common::ActorDynamicsCluster** ppClusterEnd = ppCluster + clusterNum;

		while( ppCluster != ppClusterEnd )
		{
			( *ppCluster )->Reset();
			ppCluster++;
		}
	}
}

void ActorDynamicsDirector::Update( void )
{
	MIX_ASSERT( m_bDisposed == False );

	UIntT clusterNum = m_ClusterList.size();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コマンドの処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_Commands.IsEmpty() == False )
	{
		UInt32 data = 0;
		UInt32 comType = 0;

		do
		{
			data = m_Commands.GetHead();
			comType = data & ActorDynamicsDirector::CT_MASK;

			if( comType == ActorDynamicsDirector::CU_SET_COLLIDER_MODE )
			{
				// コライダーのモードを設定
				Proc_SetColliderMode( static_cast<Mix::Scene::DYNAMICS_COLLIDER_MODE>( ( data & ActorDynamicsDirector::CD_MASK ) ) );
				m_Commands.Dequeue();
			}
			else if( comType == ActorDynamicsDirector::CU_ACTIVATE )
			{
				// コライダーの活動状態を設定
				Proc_ActivateCollider();
				m_Commands.Dequeue();
			}
			else if( comType == ActorDynamicsDirector::CU_DEACTIVATE )
			{
				// コライダーの非活動状態を設定
				Proc_DeactivateCollider( ( ( data & ActorDynamicsDirector::CD_MASK ) > 0 ) );
				m_Commands.Dequeue();
			}
		}
		while(	( comType >= ActorDynamicsDirector::CU_FIRST ) &&
				( comType <= ActorDynamicsDirector::CU_LAST ) &&
				( m_Commands.IsEmpty() == False ) );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// クラスターの更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( clusterNum > 0 )
	{
		Mix::Scene::Common::ActorDynamicsCluster** ppCluster = &m_ClusterList[0];
		Mix::Scene::Common::ActorDynamicsCluster** ppClusterEnd = ppCluster + clusterNum;

		while( ppCluster != ppClusterEnd )
		{
			( *ppCluster )->Update();
			ppCluster++;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 非活動状態のアイドリング
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( m_bActive == False ) && ( m_pFigure != NULL ) )
	{
		m_pFigure->Idling();
	}
}

void ActorDynamicsDirector::Refresh( void )
{
	MIX_ASSERT( m_bDisposed == False );

	UIntT clusterNum = m_ClusterList.size();
//	UInt32 collClusterNum = m_CollideClusterList.size();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// クラスターのリフレッシュ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( clusterNum > 0 )
	{
		Mix::Scene::Common::ActorDynamicsCluster** ppCluster = &m_ClusterList[0];
		Mix::Scene::Common::ActorDynamicsCluster** ppClusterEnd = ppCluster + clusterNum;

		UInt32 activeClusterCount = 0;

		while( ppCluster != ppClusterEnd )
		{
			if( ( *ppCluster )->Refresh() == True )
			{
				activeClusterCount++;
			}

			ppCluster++;
		}

		m_bActive = ( activeClusterCount > 0 );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コマンドの処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_Commands.IsEmpty() == False )
	{
		UInt32 data = 0;
		UInt32 comType = 0;

		do
		{
			data = m_Commands.GetHead();
			comType = data & ActorDynamicsDirector::CT_MASK;

			if( comType == ActorDynamicsDirector::CR_CLEAR_FORCE )
			{
				// フォースをクリア
				Proc_ClearColliderForce();
				m_Commands.Dequeue();
			}
			else if( comType == ActorDynamicsDirector::CR_CLEAR_MOTION )
			{
				// モーションをクリア
				Proc_ClearColliderMotion();
				m_Commands.Dequeue();
			}
		}
		while(	( comType >= ActorDynamicsDirector::CR_FIRST ) &&
				( comType <= ActorDynamicsDirector::CR_LAST ) &&
				( m_Commands.IsEmpty() == False ) );
	}
}

void ActorDynamicsDirector::Proc_SetColliderMode( Mix::Scene::DYNAMICS_COLLIDER_MODE mode )
{
	ActorDynamicsDirector::ClusterList::iterator it_begin = m_ClusterList.begin();
	ActorDynamicsDirector::ClusterList::iterator it_end = m_ClusterList.end();
	ActorDynamicsDirector::ClusterList::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		( *it )->SetColliderMode( mode );
	}
}

void ActorDynamicsDirector::Proc_ActivateCollider( void )
{
	ColliderCollection::Type::iterator it_begin = m_pColliderCollection->Begin();
	ColliderCollection::Type::iterator it_end = m_pColliderCollection->End();
	ColliderCollection::Type::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		( *it )->GetInternalRigidBodyPtr()->Activate();
	}
}

void ActorDynamicsDirector::Proc_DeactivateCollider( Boolean bForce )
{
	ColliderCollection::Type::iterator it_begin = m_pColliderCollection->Begin();
	ColliderCollection::Type::iterator it_end = m_pColliderCollection->End();
	ColliderCollection::Type::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		( *it )->GetInternalRigidBodyPtr()->Deactivate( bForce );
	}
}

void ActorDynamicsDirector::Proc_ClearColliderForce( void )
{
	ColliderCollection::Type::iterator it_begin = m_pColliderCollection->Begin();
	ColliderCollection::Type::iterator it_end = m_pColliderCollection->End();
	ColliderCollection::Type::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		( *it )->ClearForce();
	}
}

void ActorDynamicsDirector::Proc_ClearColliderMotion( void )
{
	ColliderCollection::Type::iterator it_begin = m_pColliderCollection->Begin();
	ColliderCollection::Type::iterator it_end = m_pColliderCollection->End();
	ColliderCollection::Type::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		( *it )->ClearMotion();
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IActorDynamicsDirector
////////////////////////////////////////////////////////////////////////////////////////////////////

Boolean ActorDynamicsDirector::IsCharacterEnabled( void ) const
{
	return ( m_pFigure != NULL )? m_pFigure->IsCharacterEnabled() : False;
}

void ActorDynamicsDirector::SetCharacterEnabled( Boolean state )
{
	if( m_pFigure != NULL )
	{
		m_pFigure->SetCharacterEnabled( state );
	}
}

Mix::Scene::DYNAMICS_KCHAR_MODE ActorDynamicsDirector::GetCharacterMode( void ) const
{
	return m_KCharMode;
}

void ActorDynamicsDirector::SetCharacterMode( Mix::Scene::DYNAMICS_KCHAR_MODE mode, Boolean bInstantly )
{
	MIX_ASSERT( mode <= ActorDynamicsDirector::CD_MASK );

	if( m_pFigure != NULL )
	{
		m_pFigure->SetCharacterMode( mode );
		m_KCharMode = mode;
	}
}

Boolean ActorDynamicsDirector::IsColliderEnabled( void ) const
{
	return m_bColliderEnabled;
}

void ActorDynamicsDirector::SetColliderEnabled( Boolean state )
{
	if( m_bColliderEnabled != state )
	{
		ActorDynamicsDirector::ClusterList::iterator it_begin = m_ClusterList.begin();
		ActorDynamicsDirector::ClusterList::iterator it_end = m_ClusterList.end();
		ActorDynamicsDirector::ClusterList::iterator it;

		for( it = it_begin; it != it_end; ++it )
		{
			Mix::Scene::Common::ActorDynamicsCluster* pCluster = ( *it );
			pCluster->SetColliderEnabled( state );
		}

		m_bColliderEnabled = state;
	}
}

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

void ActorDynamicsDirector::SetColliderMode( Mix::Scene::DYNAMICS_COLLIDER_MODE mode, Boolean bInstantly )
{
	MIX_ASSERT( mode <= CD_MASK );

	if( ( m_bDisposed == False ) &&
		( m_ColliderMode != mode ) )
	{
		if( bInstantly == True )
		{
			Proc_SetColliderMode( mode );
		}
		else
		{
			m_Commands.Enqueue( ActorDynamicsDirector::CU_SET_COLLIDER_MODE | mode );
		}

		m_ColliderMode = mode;
	}
}

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

void ActorDynamicsDirector::SetSensorEnabled( Boolean state )
{
	if( m_bSensorEnabled != state )
	{
		ActorDynamicsDirector::ClusterList::iterator it_begin = m_ClusterList.begin();
		ActorDynamicsDirector::ClusterList::iterator it_end = m_ClusterList.end();
		ActorDynamicsDirector::ClusterList::iterator it;

		for( it = it_begin; it != it_end; ++it )
		{
			( *it )->SetSensorEnabled( state );
		}

		m_bSensorEnabled = state;
	}
}

Boolean ActorDynamicsDirector::GetColliders( Mix::Scene::IActorDynamicsDirector::ICollection<Mix::Scene::IActorCollider>** ppCollection )
{
	if( ppCollection == NULL )
	{
		return False;
	}

	MIX_ASSERT( m_pColliderCollection != NULL );

	MIX_ADD_REF( m_pColliderCollection );
	( *ppCollection ) = m_pColliderCollection;

	return True;
}

Boolean ActorDynamicsDirector::GetColliders( const wchar_t* pName, Mix::Scene::IActorDynamicsDirector::ICollection<Mix::Scene::IActorCollider>** ppCollection )
{
	MIX_ASSERT( pName != NULL );
	MIX_ASSERT( ppCollection != NULL );

	ActorDynamicsDirector::ColliderCollectionMap::iterator it = m_ColliderCollectionMap.find( pName );
	if( it != m_ColliderCollectionMap.end() )
	{
		MIX_ASSERT( it->second != NULL );

		MIX_ADD_REF( it->second );
		( *ppCollection ) = it->second;
	}
	else
	{
		return False;
	}

	return True;
}

Boolean ActorDynamicsDirector::GetSensors( Mix::Scene::IActorDynamicsDirector::ICollection<Mix::Scene::IActorSensor>** ppCollection )
{
	MIX_ASSERT( ppCollection != NULL );
	MIX_ASSERT( m_pSensorCollection != NULL );

	MIX_ADD_REF( m_pSensorCollection );
	( *ppCollection ) = m_pSensorCollection;

	return True;
}

Boolean ActorDynamicsDirector::GetSensors( const wchar_t* pName, Mix::Scene::IActorDynamicsDirector::ICollection<Mix::Scene::IActorSensor>** ppCollection )
{
	MIX_ASSERT( pName != NULL );
	MIX_ASSERT( ppCollection != NULL );

	ActorDynamicsDirector::SensorCollectionMap::iterator it = m_SensorCollectionMap.find( pName );
	if( it != m_SensorCollectionMap.end() )
	{
		MIX_ASSERT( it->second != NULL );

		MIX_ADD_REF( it->second );
		( *ppCollection ) = it->second;
	}
	else
	{
		return False;
	}

	return True;
}

Boolean ActorDynamicsDirector::IsActive( void ) const
{
	return m_bActive;
}

void ActorDynamicsDirector::Activate( Boolean bInstantly )
{
	if( m_bDisposed == False )
	{
		if( bInstantly == True )
		{
			Proc_ActivateCollider();
		}
		else
		{
			m_Commands.Enqueue( ActorDynamicsDirector::CU_ACTIVATE );
		}
	}
}

void ActorDynamicsDirector::Deactivate( Boolean bForce, Boolean bInstantly )
{
	if( m_bDisposed == False )
	{
		if( bInstantly == True )
		{
			Proc_DeactivateCollider( bForce );
		}
		else
		{
			m_Commands.Enqueue( ActorDynamicsDirector::CU_DEACTIVATE | ( ( bForce == True )? 0x00000001 : 0x00000000 ) );
		}
	}
}

void ActorDynamicsDirector::ClearForce( Boolean bInstantly )
{
	if( m_bDisposed == False )
	{
		if( bInstantly == True )
		{
			Proc_ClearColliderForce();
		}
		else
		{
			m_Commands.Enqueue( ActorDynamicsDirector::CR_CLEAR_FORCE );
		}
	}
}

void ActorDynamicsDirector::ClearMotion( Boolean bInstantly )
{
	if( m_bDisposed == False )
	{
		if( bInstantly == True )
		{
			Proc_ClearColliderMotion();
		}
		else
		{
			m_Commands.Enqueue( ActorDynamicsDirector::CR_CLEAR_MOTION );
		}
	}
}

Float32 ActorDynamicsDirector::GetLinearSleepingThresholds( void ) const
{
	return m_LinearSleepingThreshold;
}

void ActorDynamicsDirector::SetLinearSleepingThresholds( Float32 threshold )
{
	if( MIX_FLOAT_EQUAL( m_LinearSleepingThreshold, threshold ) == False )
	{
		MIX_ASSERT( m_pColliderCollection != NULL );

		ColliderCollection::Type::iterator it_begin = m_pColliderCollection->Begin();
		ColliderCollection::Type::iterator it_end = m_pColliderCollection->End();
		ColliderCollection::Type::iterator it;

		for( it = it_begin; it != it_end; ++it )
		{
			MIX_ASSERT( ( *it )->GetInternalRigidBodyPtr() != NULL );
			( *it )->GetInternalRigidBodyPtr()->SetLinearSleepingThresholds( threshold );
		}

		m_LinearSleepingThreshold = threshold;
	}
}

Float32 ActorDynamicsDirector::GetAngularSleepingThresholds( void ) const
{
	return m_AngularSleepingThreshold;
}

void ActorDynamicsDirector::SetAngularSleepingThresholds( Float32 threshold )
{
	if( MIX_FLOAT_EQUAL( m_AngularSleepingThreshold, threshold ) == False )
	{
		MIX_ASSERT( m_pColliderCollection != NULL );

		ColliderCollection::Type::iterator it_begin = m_pColliderCollection->Begin();
		ColliderCollection::Type::iterator it_end = m_pColliderCollection->End();
		ColliderCollection::Type::iterator it;

		for( it = it_begin; it != it_end; ++it )
		{
			MIX_ASSERT( ( *it )->GetInternalRigidBodyPtr() != NULL );
			( *it )->GetInternalRigidBodyPtr()->SetAngularSleepingThresholds( threshold );
		}

		m_AngularSleepingThreshold = threshold;
	}
}

}}}
