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

#include "Mix/Private/Scene/Common/ActorRevision.h"
#include "Mix/Private/Scene/Common/ActorModel.h"
#include "Mix/Private/Scene/Common/MotionController.h"

namespace Mix{ namespace Scene{ namespace Common{

////////////////////////////////////////////////////////////////////////////////////////////////////
// MotionMixer
////////////////////////////////////////////////////////////////////////////////////////////////////

const UInt32 MotionMixer::RB_DEF_SIZE = 8;
const UInt32 MotionMixer::RB_RESIZE_STEP = 2;

const UInt32 MotionMixer::BB_DEF_SIZE = 256;
const UInt32 MotionMixer::BB_RESIZE_STEP = 64;

const UInt32 MotionMixer::MAX_SKIP_FRAMES = 30;

bool MotionMixer::CONTROLLER_SORT::operator()( const MotionController* l, const MotionController* r )
{
	return ( l->GetPriority() < r->GetPriority() );
}

MotionMixer* MotionMixer::CreateInstance( Mix::Scene::Common::ActorModel* pModel )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, MotionMixer, pModel );
}

MotionMixer::MotionMixer( Mix::Scene::Common::ActorModel* pModel ) :
m_pModel( NULL ),
m_Speed( 1.0f ),
m_Flags( Mix::Scene::IMotionMixer::ALL_FLAGS ),
m_UpdateFlags( 0 ),
m_FrameCounter( MotionMixer::MAX_SKIP_FRAMES ),
m_DeltaTimer( 0.0f )
{
	MIX_ASSERT( pModel != NULL );
	m_pModel = pModel;

#ifdef _DEBUG

	Mix::String tempStr;

	tempStr.Sprintf( L"MotionMixer/%s/RootBlendList", pModel->GetName() );
	m_RootBlendList.Initialize( MotionMixer::RB_DEF_SIZE, MotionMixer::RB_RESIZE_STEP, tempStr.GetConstPtr() );

	tempStr.Sprintf( L"MotionMixer/%s/BodyBlendList", pModel->GetName() );
	m_BodyBlendList.Initialize( MotionMixer::BB_DEF_SIZE, MotionMixer::BB_RESIZE_STEP, tempStr.GetConstPtr() );

#else //_DEBUG
	m_RootBlendList.Initialize( MotionMixer::RB_DEF_SIZE, MotionMixer::RB_RESIZE_STEP );
	m_BodyBlendList.Initialize( MotionMixer::BB_DEF_SIZE, MotionMixer::BB_RESIZE_STEP );
#endif //_DEBUG
}

MotionMixer::~MotionMixer( void )
{
	if( m_ControllerList.size() > 0 )
	{
		for( MotionMixer::ControllerList::iterator it = m_ControllerList.begin(); it != m_ControllerList.end(); ++it )
		{
			MIX_RELEASE( ( *it ) );
		}

		m_ControllerList.clear();
	}
}

UInt32 MotionMixer::GetControllerCount( void ) const
{
	return MIX_UIT_TO_UI32( m_ControllerList.size() );
}

Boolean MotionMixer::GetControllerByIndex( UInt32 index, Mix::Scene::IMotionController** ppController )
{
	if( ( ppController == NULL ) ||
		( m_ControllerList.size() <= index ) )
	{
		return False;
	}

	IMotionController* pController = m_ControllerList[index];

	MIX_ADD_REF( pController );
	( *ppController ) = pController;

	return True;
}

Boolean MotionMixer::GetControllerByName( const wchar_t* pName, Mix::Scene::IMotionController** ppController )
{
	if( ( pName == NULL ) ||
		( ::wcslen( pName ) == 0 ) ||
		( ppController == NULL ) )
	{
		return False;
	}

	MotionMixer::IndexMap::iterator it = m_ControllerMap.find( pName );
	if( it == m_ControllerMap.end() )
	{
		return False;
	}

	MIX_ASSERT( m_ControllerList.size() > it->second );

	MotionController* pController = m_ControllerList[it->second];

	MIX_ADD_REF( pController );
	( *ppController ) = pController;

	return True;
}

void MotionMixer::ForceStopAll( void )
{
	if( m_ControllerList.size() > 0 )
	{
		MotionMixer::ControllerList::iterator it_ctrl_begin = m_ControllerList.begin();
		MotionMixer::ControllerList::iterator it_ctrl_end = m_ControllerList.end();
		MotionMixer::ControllerList::iterator it_ctrl;

		Mix::Scene::MOTION_COMMAND com;

		com.flags = 0;
		com.handle = NULL;
		com.frame = 0.0f;
		com.loopCount = 0;
		com.transitionTimeLength = 0.0f;
			
		for( it_ctrl = it_ctrl_begin; it_ctrl != it_ctrl_end; ++it_ctrl )
		{
			Mix::Scene::Common::MotionController* pCtrl = ( *it_ctrl );
			pCtrl->SendCommand( Mix::Scene::MOTION_CE_FORCE, com );
		}
	}
}

Float32 MotionMixer::GetSpeed( void ) const
{
	return m_Speed;
}

void MotionMixer::SetSpeed( Float32 speed )
{
	m_Speed = max( 0.0f, speed );
}

UInt32 MotionMixer::GetFlags( void ) const
{
	return m_Flags;
}

void MotionMixer::SetFlags( UInt32 flags )
{
	m_Flags = flags;
}

UInt32 MotionMixer::GetUpdateFlags( void ) const
{
	return m_UpdateFlags;
}

const Mix::Quaternion& MotionMixer::GetAngularVelocity( void ) const
{
	return m_AngularVelocity;
}

const Mix::Vector3& MotionMixer::GetLinearVelocity( void ) const
{
	return m_LinearVelocity;
}

void MotionMixer::ReserveControllerList( UInt32 count )
{
	if( count == 0 )
	{
		return;
	}

	MIX_ASSERT( m_ControllerList.size() == 0 );

	m_ControllerList.reserve( count );
}

MotionController* MotionMixer::AddController( const wchar_t* pName, UInt32 priority )
{
	MIX_ASSERT( ( pName != NULL ) && ( ::wcslen( pName ) > 0 ) );

	MotionController* pController = MotionController::CreateInstance( m_pModel, this, pName, priority );
	if( pController != NULL )
	{
		m_ControllerList.push_back( pController );
	}

	return pController;
}

void MotionMixer::CreateControllerIndexMap( void )
{
	MotionMixer::ControllerList::iterator it_begin;
	MotionMixer::ControllerList::iterator it_end;
	MotionMixer::ControllerList::iterator it;

	UInt32 index;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ソート
	////////////////////////////////////////////////////////////////////////////////////////////////////

	std::sort( m_ControllerList.begin(), m_ControllerList.end(), MotionMixer::CONTROLLER_SORT() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マップを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	it_begin = m_ControllerList.begin();
	it_end = m_ControllerList.end();

	index = 0;

	for( it = it_begin; it != it_end; ++it, index++ )
	{
		m_ControllerMap.insert( MotionMixer::IndexMap::value_type( ( *it )->GetName(), index ) );
	}
}

MotionController* MotionMixer::GetControllerPtr( UInt32 index ) const
{
	MIX_ASSERT( m_ControllerList.size() > index );

	return m_ControllerList[index];
}

void MotionMixer::ReservePoseTable( UIntT nodeCount )
{
	if( nodeCount == 0 )
	{
		return;
	}

	m_PoseTable.reserve( nodeCount );
}

void MotionMixer::AddPose( const Mix::Vector3& scaling, const Mix::Quaternion& rotation, const Mix::Vector3& translation, Mix::Matrix4x4* pMat, Mix::Scene::Common::ActorRevision* pRevision )
{
	MIX_ASSERT( pMat != NULL );
	MIX_ASSERT( pRevision != NULL );

	MotionMixer::POSE pose;

	pose.bUpdate = False;
	pose.defScaling = scaling;
	pose.defRotation = rotation;
	pose.defTranslation = translation;
	pose.scaling = scaling;
	pose.rotation = rotation;
	pose.translation = translation;
	pose.pMat = pMat;
	pose.pRevision = pRevision;

	m_PoseTable.push_back( pose );
}

const MotionMixer::POSE& MotionMixer::GetPose( UIntT index ) const
{
	MIX_ASSERT( m_PoseTable.size() > index );

	return m_PoseTable[index];
}

void MotionMixer::Finalize( void )
{
	for( MotionMixer::ControllerList::iterator it = m_ControllerList.begin(); it != m_ControllerList.end(); ++it )
	{
		( *it )->Finalize();
	}
}

void MotionMixer::Dispose( void )
{
	if( m_ControllerList.size() > 0 )
	{
		for( MotionMixer::ControllerList::iterator it = m_ControllerList.begin(); it != m_ControllerList.end(); ++it )
		{
			( *it )->Dispose();
			MIX_RELEASE( ( *it ) );
		}

		m_ControllerList.clear();
	}

	m_pModel = NULL;
}

void MotionMixer::Reset( void )
{
	MotionMixer::ControllerList::iterator it_ctrl_begin = m_ControllerList.begin();
	MotionMixer::ControllerList::iterator it_ctrl_end = m_ControllerList.end();
	MotionMixer::ControllerList::iterator it_ctrl;

	Float32 dummy0;
	UInt32 dummy1;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コントローラーの全てのコマンドの処理を完了する
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( it_ctrl = it_ctrl_begin; it_ctrl != it_ctrl_end; ++it_ctrl )
	{
		Mix::Scene::Common::MotionController* pCtrl = ( *it_ctrl );
		pCtrl->OnReset();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ボディのトランスフォームを実行
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_FrameCounter = MotionMixer::MAX_SKIP_FRAMES;
	m_DeltaTimer = 0.0f;

	Update( 0.0f, IMotionMixer::BODY_TRANSFORM, 0, dummy0, dummy1 );
}

void MotionMixer::Update( Float32 dt, UInt32 mask, UInt32 maxSkipFrames, Float32& outDeltaTimer, UInt32& outSkipFrames )
{
	MIX_ASSERT( maxSkipFrames <= MotionMixer::MAX_SKIP_FRAMES );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 事前処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
		初期化
	*/

	outDeltaTimer = dt;
	outSkipFrames = 0;

	m_AngularVelocity = Mix::Quaternion::Identity();
	m_LinearVelocity = Mix::Vector3::Zero();

	m_UpdateFlags = 0;

	/*
		コントローラーのチェック
	*/

	if( m_ControllerList.size() == 0 )
	{
		return;
	}

	/*
		フレームのスキップ処理
	*/

	m_DeltaTimer += dt;

	outDeltaTimer = m_DeltaTimer;
	outSkipFrames = m_FrameCounter;

	if( maxSkipFrames > m_FrameCounter )
	{
		m_FrameCounter++;
		return;
	}
	else
	{
		m_FrameCounter = 0;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 変数宣言
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Float32 deltaTime = m_Speed * m_DeltaTimer;

	UInt32 flags = m_Flags & mask;
	Boolean bFrameStep = ( MIX_TESTBIT( flags, IMotionMixer::FRAME_STEP ) == IMotionMixer::FRAME_STEP );

	MotionMixer::ControllerList::iterator it_ctrl_begin = m_ControllerList.begin();
	MotionMixer::ControllerList::iterator it_ctrl_end = m_ControllerList.end();
	MotionMixer::ControllerList::iterator it_ctrl;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コントローラーを更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( flags != 0 )
	{
		Boolean bRootTransform = ( MIX_TESTBIT( flags, IMotionMixer::ROOT_TRANSFORM ) == IMotionMixer::ROOT_TRANSFORM );
		Boolean bBodyTransform = ( MIX_TESTBIT( flags, IMotionMixer::BODY_TRANSFORM ) == IMotionMixer::BODY_TRANSFORM );

		for( it_ctrl = it_ctrl_begin; it_ctrl != it_ctrl_end; ++it_ctrl )
		{
			Mix::Scene::Common::MotionController* pCtrl = ( *it_ctrl );

			if( bRootTransform == True ) { pCtrl->OnRootTransform( m_RootBlendList ); }
			if( bBodyTransform == True ) { pCtrl->OnBodyTransform( m_BodyBlendList ); }
			if( bFrameStep == True ) { pCtrl->OnUpdate( deltaTime ); }
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ルートのブレンド
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_RootBlendList.GetCount() > 0 )
	{
		MOTION_ROOT_BLEND* pBlend = &( m_RootBlendList[0] );
		MOTION_ROOT_BLEND* pBlendEnd = pBlend + m_RootBlendList.GetCount();

		Mix::Matrix4x4 rootMat;
		Mix::Matrix4x4 worldMat;

		while( pBlend != pBlendEnd )
		{
			m_AngularVelocity = Mix::Quaternion::Slerp( m_AngularVelocity, pBlend->rotation, pBlend->ratio );
			m_LinearVelocity = Mix::Vector3::Lerp( m_LinearVelocity, pBlend->translation, pBlend->ratio );

			pBlend++;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ボディのブレンド
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_BodyBlendList.GetCount() > 0 )
	{
		MOTION_BODY_BLEND* pBlend = &( m_BodyBlendList[0] );
		MOTION_BODY_BLEND* pBlendEnd = pBlend + m_BodyBlendList.GetCount();

		MotionMixer::POSE* poseTable = &( m_PoseTable[0] );

		MotionMixer::POSE* pPose = &( m_PoseTable[0] );
		MotionMixer::POSE* pPoseEnd = pPose + m_PoseTable.size();

		//ブレンド
		while( pBlend != pBlendEnd )
		{
			MotionMixer::POSE* pPose = &( poseTable[pBlend->nodeIndex] );

			pPose->bUpdate = True;

			pPose->scaling = Mix::Vector3::Lerp( pPose->scaling, pBlend->scaling, pBlend->ratio );
			pPose->rotation = Mix::Quaternion::Slerp( pPose->rotation, pBlend->rotation, pBlend->ratio );
			pPose->translation = Mix::Vector3::Lerp( pPose->translation, pBlend->translation, pBlend->ratio );

			pBlend++;
		}

		//ノードに適用
		while( pPose != pPoseEnd )
		{
			MIX_ASSERT( pPose->pRevision != NULL );

			if( ( pPose->bUpdate == True ) &&
				( pPose->pRevision->SetLocalTransform() == True ) )
			{
				const Mix::Vector3& scaling = pPose->scaling;
				const Mix::Quaternion& rotation = pPose->rotation;
				const Mix::Vector3& translation = pPose->translation;

				Mix::Matrix4x4* pMat = pPose->pMat;

				//行列 : スケーリング
				pMat->SetScaling( scaling );

				//行列 : ローテーション
				*pMat *= Mix::Matrix4x4( rotation );

				//行列 : トランスレーション
				pMat->SetRow( 3, translation );
			}

			pPose->bUpdate = False;
			pPose->scaling = pPose->defScaling;
			pPose->rotation = pPose->defRotation;
			pPose->translation = pPose->defTranslation;

			pPose++;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 更新フラグ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_RootBlendList.GetCount() > 0 ) { MIX_SETBIT( m_UpdateFlags, IMotionMixer::ROOT_TRANSFORM ); }
	if( m_BodyBlendList.GetCount() > 0 ) { MIX_SETBIT( m_UpdateFlags, IMotionMixer::BODY_TRANSFORM ); }
	if( bFrameStep == True ) { MIX_SETBIT( m_UpdateFlags, IMotionMixer::FRAME_STEP ); }

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 後処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_RootBlendList.Clear();
	m_BodyBlendList.Clear();

	m_DeltaTimer = 0.0f;
}

}}}
