#include "Utility/Motion/StateMachine.h"

////////////////////////////////////////////////////////////////////////////////////////////////////
// StateMachine::State
////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Utility{ namespace Motion{

StateMachine::State::State( UInt32 id, UInt32 channelMask, Boolean bInherit ) :
m_ID( id ),
m_ChannelMask( channelMask ),
m_bInherit( bInherit ),
m_MotionHandle( NULL ),
m_pMotion( NULL ),
m_pMotionState( NULL ),
m_MotionLoopCount( 0 ),
m_bActive( False ),
m_pTask( NULL )
{
}

StateMachine::State::~State( void )
{
	MIX_RELEASE( m_pMotion );
	MIX_RELEASE( m_pMotionState );
}

void StateMachine::State::SetMotion(	const Mix::Scene::MOTION_HANDLE& handle,
										Mix::Scene::IMotion* pInterface,
										Mix::Scene::IMotionState* pState,
										UInt32 loopCount )
{
	m_MotionHandle = handle;
	m_pMotion = pInterface;
	m_pMotionState = pState;
	m_MotionLoopCount = loopCount;
}

void StateMachine::State::SetTransition( const StateMachine::TRANSITION_DESC* descs, UInt32 count )
{
	if( ( descs == NULL ) &&
		( count == 0 ) )
	{
		return;
	}

	MIX_ASSERT( ( descs != NULL ) && ( count > 0 ) );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// トランジションリストの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_TransitionList.reserve( count );

	for( UInt32 i = 0; i < count; i++ )
	{
		m_TransitionList.push_back( descs[i] );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// トランジションポインタマップの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( State::TransitionList::iterator it = m_TransitionList.begin(); it != m_TransitionList.end(); ++it )
	{
		const TRANSITION_DESC* pDesc = &( *it );

		m_TransitionPtrMap.insert( State::TransitionPtrMap::value_type( pDesc->state, pDesc ) );
	}
}

void StateMachine::State::SetActive( Boolean state )
{
	m_bActive = state;
}

UInt32 StateMachine::State::GetID( void ) const
{
	return m_ID;
}

UInt32 StateMachine::State::GetChannelMask( void ) const
{
	return m_ChannelMask;
}

Boolean StateMachine::State::IsInherit( void ) const
{
	return m_bInherit;
}

const Mix::Scene::MOTION_HANDLE& StateMachine::State::GetMotionHandle( void ) const
{
	return m_MotionHandle;
}

Mix::Scene::IMotion* StateMachine::State::GetMotionPtr( void ) const
{
	return m_pMotion;
}

Mix::Scene::IMotionState* StateMachine::State::GetMotionStatePtr( void ) const
{
	return m_pMotionState;
}

UInt32 StateMachine::State::GetMotionLoopCount( void ) const
{
	return m_MotionLoopCount;
}

UInt32 StateMachine::State::GetTransitionCount( void ) const
{
	return MIX_UIT_TO_UI32( m_TransitionList.size() );
}

const StateMachine::TRANSITION_DESC* StateMachine::State::GetTransitionPtrByIndex( UInt32 index ) const
{
	if( m_TransitionList.size() <= index )
	{
		return NULL;
	}

	return &( m_TransitionList[index] );
}

const StateMachine::TRANSITION_DESC* StateMachine::State::GetTransitionPtrByState( UInt32 state ) const
{
	State::TransitionPtrMap::const_iterator it = m_TransitionPtrMap.find( state );
	if( it == m_TransitionPtrMap.end() )
	{
		return NULL;
	}

	return it->second;
}

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

void StateMachine::State::SetTaskPtr( StateMachine::Task* pTask )
{
	m_pTask = pTask;
}

StateMachine::Task* StateMachine::State::GetTaskPtr( void )
{
	return m_pTask;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// StateMachine::Channel
////////////////////////////////////////////////////////////////////////////////////////////////////

StateMachine::Channel::Channel( StateMachine* pSubject, UInt32 id, Mix::Scene::IMotionController* pMotionController ) :
m_pSubject( pSubject ),
m_ID( id ),
m_pMotionController( pMotionController ),
m_pActiveState( NULL ),
m_bEnabled( True )
{
	MIX_ADD_REF( m_pMotionController );
}

StateMachine::Channel::~Channel( void )
{
	for( Channel::StatePtrMap::iterator it = m_StatePtrMap.begin(); it != m_StatePtrMap.end(); ++it )
	{
		MIX_DELETE( it->second );
	}

	MIX_RELEASE( m_pMotionController );
}

UInt32 StateMachine::Channel::GetID( void ) const
{
	return m_ID;
}

StateMachine::State* StateMachine::Channel::AddState( UInt32 id, const StateMachine::STATE_DESC& desc )
{
	if( id == 0xFFFFFFFF )
	{
		return NULL;
	}

	StateMachine::State* pState = new StateMachine::State( id, desc.channelMask, desc.bInherit );
	if( pState == NULL )
	{
		return NULL;
	}

	if( ( desc.pMotionName != NULL ) &&
		( ::wcslen( desc.pMotionName ) > 0 ) )
	{
		Mix::Scene::MOTION_HANDLE motionHandle;
		Mix::Scene::IMotion* pMotion;
		Mix::Scene::IMotionState* pMotionState;

		motionHandle = m_pMotionController->GetHandleByName( desc.pMotionName );
		if( motionHandle == NULL )
		{
			MIX_DELETE( pState );
			return NULL;
		}

		if( m_pMotionController->Get( motionHandle, &pMotion ) == False )
		{
			MIX_DELETE( pState );
			return NULL;
		}

		if( m_pMotionController->GetState( motionHandle, &pMotionState ) == False )
		{
			MIX_DELETE( pState );
			return NULL;
		}

		pState->SetMotion( motionHandle, pMotion, pMotionState, desc.loopCount );
	}

	if( ( desc.transitions != NULL ) &&
		( desc.transitionCount > 0 ) )
	{
		pState->SetTransition( desc.transitions, desc.transitionCount );
	}

	m_StatePtrMap.insert( Channel::StatePtrMap::value_type( id, pState ) );

	return pState;
}

const StateMachine::State* StateMachine::Channel::GetStatePtr( UInt32 id ) const
{
	Channel::StatePtrMap::const_iterator it = m_StatePtrMap.find( id );
	if( it == m_StatePtrMap.end() )
	{
		return NULL;
	}

	return it->second;
}

void StateMachine::Channel::ChangeState( UInt32 id )
{
	UInt32 nextID = 0xFFFFFFFF;
	Boolean bForce = False;
	Float32 timeLength = 0.0f;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 次のステートのパラメータを取得
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pActiveState != NULL )
	{
		const StateMachine::TRANSITION_DESC* pDesc = m_pActiveState->GetTransitionPtrByState( id );
		if( pDesc != NULL )
		{
			nextID = pDesc->state;
			bForce = pDesc->bForce;
			timeLength = pDesc->timeLength;
		}
	}
	else
	{
		nextID = id;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// ステートの変更
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	Channel::StatePtrMap::iterator it = m_StatePtrMap.find( nextID );
	if( it != m_StatePtrMap.end() )
	{
		StateMachine::Task* pTask;

		//ピレビアスステート
		if( m_pActiveState != NULL )
		{
			pTask = m_pActiveState->GetTaskPtr();
			if( pTask != NULL )
			{
				pTask->OnEnd();
			}

			m_pActiveState->SetActive( False );
		}

		//アクティブステート
		m_pActiveState = it->second;
		m_pActiveState->SetActive( True );

		//新規のタスクに開始を通知
		pTask = m_pActiveState->GetTaskPtr();
		if( pTask != NULL )
		{
			pTask->OnBegin();
		}

		//サンブジェクトにステートの変更と通知
		m_pSubject->ChangeState( this, bForce, timeLength );
	}
}

UInt32 StateMachine::Channel::GetActiveStateID( void ) const
{
	if( m_pActiveState == NULL )
	{
		return 0xFFFFFFFF;
	}

	return m_pActiveState->GetID();
}

const StateMachine::State* StateMachine::Channel::GetActiveStatePtr( void ) const
{
	return m_pActiveState;
}

Mix::Scene::IMotionController* StateMachine::Channel::GetMotionControllerPtr( void ) const
{
	return m_pMotionController;
}

void StateMachine::Channel::SetEnabled( Boolean state )
{
	m_bEnabled = state;
}

Boolean StateMachine::Channel::IsEnabled( void ) const
{
	return m_bEnabled;
}

void StateMachine::Channel::Update( Float32 dt )
{
	if( m_pActiveState != NULL )
	{
		StateMachine::Task* pTask = m_pActiveState->GetTaskPtr();
		if( pTask != NULL )
		{
			pTask->OnUpdate( dt );
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// StateMachine
////////////////////////////////////////////////////////////////////////////////////////////////////

StateMachine::StateMachine( void ) :
m_ChannelMask( 0 )
{
	m_ChannelPtrList.resize( StateMachine::MAX_CHANNEL, NULL );
}

StateMachine::~StateMachine( void )
{
	for( StateMachine::ChannelPtrList::iterator it = m_ChannelPtrList.begin(); it != m_ChannelPtrList.end(); ++it )
	{
		MIX_DELETE( ( *it ) );
	}
}

StateMachine::Channel* StateMachine::AddChannel( UInt32 id, Mix::Scene::IMotionController* pMotionController )
{
	if( ( id >= StateMachine::MAX_CHANNEL ) ||
		( pMotionController == NULL ) ||
		( m_ChannelPtrList[id] != NULL ) )
	{
		return NULL;
	}

	StateMachine::Channel* pChannel = new StateMachine::Channel( this, id, pMotionController );
	if( pChannel == NULL )
	{
		return NULL;
	}

	m_ChannelPtrList[id] = pChannel;

	return pChannel;
}

StateMachine::Channel* StateMachine::GetChannel( UInt32 id ) const
{
	if( id >= StateMachine::MAX_CHANNEL )
	{
		return NULL;
	}

	return m_ChannelPtrList[id];
}

void StateMachine::Update( Float32 dt )
{
	StateMachine::ChannelPtrList::iterator it_begin = m_ChannelPtrList.begin();
	StateMachine::ChannelPtrList::iterator it_end = m_ChannelPtrList.end();
	StateMachine::ChannelPtrList::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		StateMachine::Channel* pChannel = ( *it );

		if( ( pChannel != NULL ) &&
			( pChannel->IsEnabled() == True ) )
		{
			pChannel->Update( dt );
		}
	}
}

void StateMachine::ChangeState( StateMachine::Channel* pActiveChannel, Boolean bForce, Float32 timeLength )
{
	Mix::Scene::MOTION_COMMAND_EXECUTE_TYPE comExe = ( bForce == True )? Mix::Scene::MOTION_CE_FORCE : Mix::Scene::MOTION_CE_DEFAULT;

	StateMachine::ChannelPtrList::iterator it_begin = m_ChannelPtrList.begin();
	StateMachine::ChannelPtrList::iterator it_end = m_ChannelPtrList.end();
	StateMachine::ChannelPtrList::iterator it;

	UInt32 nextChannelMask = 0xFFFFFFFF;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 次のチャンネルマスクを決定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( it = it_begin; it != it_end; ++it )
	{
		StateMachine::Channel* pChannel = ( *it );
		if( pChannel == NULL )
		{
			continue;
		}

		const StateMachine::State* pState = pChannel->GetActiveStatePtr();
		if( pState == NULL )
		{
			continue;
		}

		nextChannelMask = nextChannelMask & pState->GetChannelMask();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// チャンネルの操作
	//
	// チャンネルマスクによるチャンネルの有効、無効を設定
	// チャンネルのステートを変更
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( UInt32 i = 0; i < StateMachine::MAX_CHANNEL; i++ )
	{
		StateMachine::Channel* pChannel = m_ChannelPtrList[i];
		if( pChannel == NULL )
		{
			continue;
		}

		UInt32 bit = 1 << i;

		pChannel->SetEnabled( MIX_TESTBIT( nextChannelMask, bit ) != 0 );

		const StateMachine::State* pState = pChannel->GetActiveStatePtr();
		if( pState == NULL )
		{
			continue;
		}

		if( MIX_TESTBIT( nextChannelMask, bit ) != 0 )
		{
			//再生
			Boolean bSend;

			if( MIX_TESTBIT( m_ChannelMask, bit ) != 0 )
			{
				bSend = ( pChannel == pActiveChannel );
			}
			else
			{
				bSend = True;
			}

			if( bSend == True )
			{
				Mix::Scene::MOTION_COMMAND com;

				if( pState->GetMotionPtr() != NULL )
				{
					com.flags = ( pState->IsInherit() == True )? Mix::Scene::MOTION_CF_PLAY_INHERIT : Mix::Scene::MOTION_CF_PLAY;
					com.handle = pState->GetMotionHandle();
					com.frame = 0.0f;
					com.loopCount = pState->GetMotionLoopCount();
					com.transitionTimeLength = timeLength;
/*
					if( MIX_TESTBIT( pState->GetMotionStatePtr()->GetCondition(), Mix::Scene::MOTION_COND_CUR_ACTIVE ) != Mix::Scene::MOTION_COND_CUR_ACTIVE )
					{
						MIX_TRACELINE( L"Play %s %s", pState->GetMotionPtr()->GetName(), ( bForce == True )? L"Force" : L"Default" );
					}
*/
					pChannel->GetMotionControllerPtr()->SendCommand( comExe, com );
				}
				else
				{
					com.flags = Mix::Scene::MOTION_CF_STOP;
					com.handle = NULL;
					com.frame = 0.0f;
					com.loopCount = 0;
					com.transitionTimeLength = timeLength;

					pChannel->GetMotionControllerPtr()->SendCommand( comExe, com );

//					MIX_TRACELINE( L"Stop" );
				}
			}
		}
		else
		{
			if( MIX_TESTBIT( m_ChannelMask, bit ) != 0 )
			{
				//停止
				Mix::Scene::MOTION_COMMAND com;

				com.flags = Mix::Scene::MOTION_CF_STOP;
				com.handle = NULL;
				com.frame = 0.0f;
				com.loopCount = 0;
				com.transitionTimeLength = timeLength;

				pChannel->GetMotionControllerPtr()->SendCommand( comExe, com );

//				MIX_TRACELINE( L"Stop" );
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// チャンネルマスクを更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_ChannelMask = nextChannelMask;
}

}}
