#include "Mix/Tool/Win32/Core/Graphics/MotionController.h"

#include <algorithm>
#include <shlwapi.h>

#include "Mix/Tool/Win32/Core/Graphics/Node.h"
#include "Mix/Tool/Win32/Core/Graphics/ObjectModel.h"
#include "Mix/Tool/Win32/Core/Graphics/Motion.h"
#include "Mix/Tool/Win32/Core/Graphics/MotionCurve.h"
#include "Mix/Tool/Win32/Core/Graphics/RootMotionCurve.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Graphics{

MotionController::MotionController( unsigned int nodeCount ) :
m_pObjectModel( NULL ),
m_bAssigned( true ),
m_Name( L"" ),
m_Priority( 0 ),
m_pSelected( NULL ),
m_State( Mix::Tool::Win32::Graphics::MOTION_STOP ),
m_PreTime( -1.0f ),
m_Time( 0.0f )
{
}

MotionController::MotionController( const wchar_t* pName ) :
m_pObjectModel( NULL ),
m_bAssigned( false ),
m_Name( pName ),
m_Priority( 0 ),
m_pSelected( NULL ),
m_State( Mix::Tool::Win32::Graphics::MOTION_STOP ),
m_PreTime( -1.0f ),
m_Time( 0.0f )
{
}

MotionController::~MotionController( void )
{
	for( MotionController::List::iterator it = m_List.begin(); it != m_List.end(); ++it )
	{
		MIX_DELETE( ( *it ) );
	}
}

void MotionController::SetObjectModel( Mix::Tool::Win32::Graphics::ObjectModel* pObjectModel )
{
	m_pObjectModel = pObjectModel;
}

Mix::Tool::Win32::Graphics::ObjectModel* MotionController::GetObjectModel( void ) const
{
	return m_pObjectModel;
}

bool MotionController::IsAssigned( void ) const
{
	return m_bAssigned;
}

void MotionController::SetName( const wchar_t* pName )
{
	MIX_ASSERT( m_pObjectModel != NULL );
	MIX_ASSERT( m_bAssigned == true );

	m_pObjectModel->CreateMotionControllerName( this, pName, m_Name );
}

const wchar_t* MotionController::GetName( void ) const
{
	return m_Name.c_str();
}

void MotionController::SetPriority( unsigned int value )
{
	MIX_ASSERT( m_bAssigned == true );

	m_Priority = value;
}

unsigned int MotionController::GetPriority( void ) const
{
	return m_Priority;
}

unsigned int MotionController::GetCount( void ) const
{
	return m_List.size();
}

Mix::Tool::Win32::Graphics::Motion* MotionController::Get( unsigned int index ) const
{
	if( m_List.size() <= index )
	{
		return NULL;
	}

	return m_List[index];
}

Mix::Tool::Win32::Graphics::Motion* MotionController::Add( const char* pName, float framesPerSec )
{
	MIX_ASSERT( pName != NULL );
	MIX_ASSERT( ::strlen( pName ) != 0 );
	MIX_ASSERT( framesPerSec > 0.0f );

	std::wstring temp;

	AnsiToWide( pName, temp );

	return Add( temp.c_str(), framesPerSec );
}

Mix::Tool::Win32::Graphics::Motion* MotionController::Add( const wchar_t* pName, float framesPerSec )
{
	MIX_ASSERT( m_pObjectModel != NULL );
	MIX_ASSERT( pName != NULL );
	MIX_ASSERT( ::wcslen( pName ) != 0 );
	MIX_ASSERT( framesPerSec > 0.0f );

	Mix::Tool::Win32::Graphics::Motion* pMotion = new Mix::Tool::Win32::Graphics::Motion( m_pObjectModel, framesPerSec );
	if( pMotion != NULL )
	{
		pMotion->SetController( this );
		pMotion->SetName( pName );
		pMotion->SetDefaultName( pMotion->GetName() );
	}
	else
	{
		return NULL;
	}

	return pMotion;
}

void MotionController::Add( const wchar_t* pFilePath )
{
}

void MotionController::Add( Mix::Tool::Win32::Graphics::Motion* pMotion )
{
	MIX_ASSERT( pMotion != NULL );
	MIX_ASSERT( pMotion->m_pController == NULL );

	pMotion->SetController( this );
	pMotion->SetName( pMotion->GetName() );
}

void MotionController::Remove( Mix::Tool::Win32::Graphics::Motion* pMotion )
{
	MIX_ASSERT( pMotion != NULL );
	MIX_ASSERT( pMotion->m_pController != NULL );

	MotionController::IndexMap::iterator it_index;
	MotionController::List::iterator it_motion;

	it_index = m_IndexMap.find( pMotion->GetName() );
	MIX_ASSERT( it_index != m_IndexMap.end() );

	it_motion = m_List.begin() + it_index->second;
	MIX_ASSERT( ( *it_motion ) == pMotion );

	pMotion->SetController( NULL );
}

void MotionController::Attach( Mix::Tool::Win32::Graphics::Motion* pMotion )
{
	MIX_ASSERT( pMotion != NULL );
	MIX_ASSERT( pMotion->m_pController != NULL );

	unsigned int index = m_List.size();
	std::wstring name;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 名前はだいじょうぶ？
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pMotion->SetName( pMotion->GetName() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リストに追加
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_IndexMap.insert( std::pair<std::wstring, unsigned int>( pMotion->GetName(), index ) );
	m_List.push_back( pMotion );
}

void MotionController::Detach( Mix::Tool::Win32::Graphics::Motion* pMotion )
{
	MIX_ASSERT( pMotion != NULL );
	MIX_ASSERT( pMotion->m_pController != NULL );

	MotionController::IndexMap::iterator it_index = m_IndexMap.find( pMotion->GetName() );
	unsigned int index = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リストからモーションを取り除く
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_ASSERT( it_index != m_IndexMap.end() );
	m_List.erase( m_List.begin() + it_index->second );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 現在選択されているモーションだったらリセット
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pSelected == pMotion )
	{
		SelectMotion( NULL );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インデックスマップを再構築
	////////////////////////////////////////////////////////////////////////////////////////////////////

	CreateIndexMap();
}

bool MotionController::CreateMotionName( Mix::Tool::Win32::Graphics::Motion* pMotion, const wchar_t* pRequestName, std::wstring& name )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ユニークな名前を作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( CreateUniqueName( pMotion, m_List.begin(), m_List.end(), pRequestName, name ) == false )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インデックスマップを再構築
	////////////////////////////////////////////////////////////////////////////////////////////////////

	CreateIndexMap();

	return true;
}

void MotionController::Destroy( void )
{
	if( m_pObjectModel != NULL )
	{
		m_pObjectModel->RemoveMotionController( this );
		m_pObjectModel = NULL;
	}

	delete this;
}

void MotionController::CreateIndexMap( void )
{
	unsigned int index = 0;

	m_IndexMap.clear();

	for( MotionController::List::iterator it = m_List.begin(); it != m_List.end(); ++it, index++ )
	{
		m_IndexMap.insert( std::pair<std::wstring, unsigned int>( ( *it )->GetName(), index ) );
	}
}

void MotionController::SelectMotion( Mix::Tool::Win32::Graphics::Motion* pMotion )
{
	if( m_pSelected == pMotion )
	{
		return;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 直前のものを一時停止状態にする
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pSelected != NULL )
	{
		PauseMotion( m_pSelected );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// トランスフォームリストの初期化
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_TransformList.clear();

	if( pMotion != NULL )
	{
		MIX_ASSERT( pMotion->m_pController == this );

		//ボディトランスフォーム
		for( unsigned int i = 0; i < pMotion->GetCurveCount(); i++ )
		{
			Mix::Tool::Win32::Graphics::MotionCurve* pCurve = pMotion->GetCurve( i );
			int nodeIndex = m_pObjectModel->GetNodeIndexByName( pCurve->GetLinkNodeName() );

			if( nodeIndex < 0 )
			{
				LogPrint( LT_WARNING, L"モーションカーブとノードのリンクに失敗しました : 存在しないノードを参照しています : Location[%s/%s] LinkNodeName[%s]",
					m_Name.c_str(),
					pMotion->GetName(),
					pCurve->GetLinkNodeName() );

				continue;
			}

			m_TransformList.push_back( MotionController::TRANSFORM( nodeIndex, pCurve ) );
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// パラメータの初期化 & 設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_PreTime = -1.0f;
	m_Time = 0.0f;

	m_pSelected = pMotion;
}

bool MotionController::IsMotionSelected( void ) const
{
	return ( m_pSelected != NULL );
}

void MotionController::PlayMotion( Mix::Tool::Win32::Graphics::Motion* pMotion )
{
	SelectMotion( pMotion );

	m_State = Mix::Tool::Win32::Graphics::MOTION_PLAY;
}

void MotionController::PauseMotion( Mix::Tool::Win32::Graphics::Motion* pMotion )
{
	if( m_pSelected != pMotion )
	{
		return;
	}

	m_State = Mix::Tool::Win32::Graphics::MOTION_PAUSE;
}

void MotionController::StopMotion( Mix::Tool::Win32::Graphics::Motion* pMotion )
{
	if( m_pSelected != pMotion )
	{
		return;
	}

	SelectMotion( NULL );

	m_State = Mix::Tool::Win32::Graphics::MOTION_STOP;
}

Mix::Tool::Win32::Graphics::MOTION_STATE MotionController::GetMotionState( const Mix::Tool::Win32::Graphics::Motion* pMotion ) const
{
	if( m_pSelected != pMotion )
	{
		return Mix::Tool::Win32::Graphics::MOTION_STOP;
	}

	return m_State;
}

void MotionController::SetMotionTime( Mix::Tool::Win32::Graphics::Motion* pMotion, int time )
{
	SelectMotion( pMotion );

	float timeF = static_cast<float>( time );
	float lastTime = m_pSelected->GetLastTime();

	if( lastTime >= timeF )
	{
		m_Time = timeF;
	}
	else
	{
		m_Time = lastTime;
	}

	m_PreTime = m_Time;
}

int MotionController::GetMotionTime( Mix::Tool::Win32::Graphics::Motion* pMotion )
{
	MIX_ASSERT( pMotion != NULL );

	if( m_pSelected != pMotion )
	{
		return 0;
	}

	return static_cast<int>( m_Time );
}

void MotionController::UpdateRootTransform( void )
{
	for( MotionController::List::iterator it = m_List.begin(); it != m_List.end(); ++it )
	{
		( *it )->UpdateRootTransform();
	}
}

bool MotionController::WriteDefault( const wchar_t* pFilePath, Mix::Tool::Win32::File::OutputStream& output )
{
	MotionController::MC_DESC desc;
	std::wstring filePath;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	::wcscpy_s( desc.name, sizeof( desc.name ) >> 1, m_Name.c_str() );
	desc.priority = m_Priority;
	desc.motionNum = 0;

	for( MotionController::List::iterator it = m_List.begin(); it != m_List.end(); ++it )
	{
		if( ( *it )->GetDefault() == true )
		{
			desc.motionNum++;
		}
	}

	if( output.Write( &desc, sizeof( desc ) ) == false )
	{
		return false;
	}

	if( desc.motionNum == 0 )
	{
		return true;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// モーションファイル名
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( MotionController::List::iterator it = m_List.begin(); it != m_List.end(); ++it )
	{
		Mix::Tool::Win32::Graphics::Motion* pMotion = ( *it );

		if( pMotion->GetDefault() == false )
		{
			continue;
		}

		if( pMotion->Write( pFilePath, output ) == false )
		{
			return false;
		}
	}

	return true;
}

bool MotionController::SaveFile( void )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// モーションを書き込む
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( MotionController::List::iterator it = m_List.begin(); it != m_List.end(); ++it )
	{
		Mix::Tool::Win32::Graphics::Motion* pMotion = ( *it );

		if( pMotion->GetDefault() == true )
		{
			continue;
		}

		if( pMotion->Save() == false )
		{
			return false;
		}
	}

	return true;
}

void MotionController::OnUpdateRootTransform( std::vector<Mix::Tool::Win32::Graphics::RT_BLEND>& blendList )
{
	if( m_pSelected == NULL )
	{
		return;
	}

	Mix::Tool::Win32::Graphics::RootMotionCurve* pCurve = m_pSelected->GetRootTransformCurvePtr();
	if( ( pCurve == NULL ) ||
		( ( pCurve->GetRotationKeyCount() == 0 ) && ( pCurve->GetTranslationKeyCount() == 0 ) ) )
	{
		return;
	}

//	Mix::Tool::Win32::Graphics::ROOT_ROTATION rot = pCurve->GetAngularVelocity( m_PreTime, m_Time );
	Mix::Tool::Win32::Graphics::RT_BLEND blend;

	blend.priority = m_Priority;
	blend.ratio = m_pSelected->GetBlendRatio();
//	blend.angularOffset = rot.offset;
//	blend.angularVelocity = rot.value;
//	blend.angularOffset = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
	blend.angularVelocity = pCurve->GetRotation( m_PreTime, m_Time );
	blend.linearVelocity = pCurve->GetTranslation( m_PreTime, m_Time );

	blendList.push_back( blend );
}

void MotionController::OnUpdateBodyTransform( std::vector<Mix::Tool::Win32::Graphics::BT_BLEND>& blendList )
{
	if( m_pSelected == NULL )
	{
		return;
	}

	const wchar_t* rtNodeName = m_pObjectModel->GetRootTransformNodeName();
	const unsigned int rtFlags = m_pSelected->GetRootTransformFlags();

	float blendRatio = m_pSelected->GetBlendRatio();

	MotionController::TransformList::iterator it_begin = m_TransformList.begin();
	MotionController::TransformList::iterator it_end = m_TransformList.end();
	MotionController::TransformList::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		MotionController::TRANSFORM* pTransform = &( *it );

		if( pTransform->pCurve->GetEnabled() == false )
		{
			continue;
		}

		MotionCurve* pCurve = pTransform->pCurve;

		D3DXVECTOR3 scaling;
		D3DXQUATERNION rotation;
		D3DXVECTOR3 translation;

		Mix::Tool::Win32::Graphics::BT_BLEND blend;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// ルートトランスフォーム対策
		////////////////////////////////////////////////////////////////////////////////////////////////////

		scaling = pCurve->GetScaling( m_Time );

		if( ( rtNodeName != NULL ) &&
			( ::wcscmp( pCurve->GetLinkNodeName(), rtNodeName ) == 0 ) )
		{
			Mix::Tool::Win32::Graphics::Node* pRTNode = m_pObjectModel->GetRootTransformNodePtr();

			//回転運動
			if( MIX_TEST_BIT( rtFlags, MOTION_RT_ROTATION ) != 0 )
			{
//				rotation = pCurve->GetRotationKey( 0 ).value;
				rotation = pRTNode->GetDefLocalRotation();
			}
			else
			{
				rotation = pCurve->GetRotation( m_Time );
			}

			//線形運動
			if( MIX_TEST_BIT( rtFlags, MOTION_RT_TRANSLATION ) != 0 )
			{
				const D3DXVECTOR3& firstT = pRTNode->GetDefLocalTranslation();
//				const D3DXVECTOR3& firstT = pCurve->GetTranslation( m_Time );

				translation = pCurve->GetTranslation( m_Time );

				//水平面
				if( MIX_TEST_BIT( rtFlags, MOTION_RT_TRANSLATION_XZ ) == MOTION_RT_TRANSLATION_XZ )
				{
					switch( m_pObjectModel->GetRootTranslationHorizonal() )
					{
					case MOTION_RT_H_XY:
						translation.x = firstT.x;
						translation.y = firstT.y;
						break;
					case MOTION_RT_H_XZ:
						translation.x = firstT.x;
						translation.z = firstT.z;
						break;
					case MOTION_RT_H_YZ:
						translation.y = firstT.y;
						translation.z = firstT.z;
						break;
					}
				}

				//鉛直方向
				if( MIX_TEST_BIT( rtFlags, MOTION_RT_TRANSLATION_Y ) == MOTION_RT_TRANSLATION_Y )
				{
					switch( m_pObjectModel->GetRootTranslationVertical() )
					{
					case MOTION_RT_V_X:
						translation.x = firstT.x;
						break;
					case MOTION_RT_V_Y:
						translation.y = firstT.y;
						break;
					case MOTION_RT_V_Z:
						translation.z = firstT.z;
						break;
					}
				}
			}
			else
			{
				translation = pCurve->GetTranslation( m_Time );
			}
		}
		else
		{
			rotation = pCurve->GetRotation( m_Time );
			translation = pCurve->GetTranslation( m_Time );
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// ブレンドを追加
		////////////////////////////////////////////////////////////////////////////////////////////////////

		blend.priority = m_Priority;
		blend.nodeIndex = pTransform->nodeIndex;
		blend.ratio = blendRatio;
		blend.scaling = scaling;
		blend.rotation = rotation;
		blend.translation = translation;

		blendList.push_back( blend );
	}
}

void MotionController::OnUpdateTime( float timeStep )
{
	if( m_pSelected == NULL )
	{
		return;
	}

	m_PreTime = m_Time;

	if( m_State == Mix::Tool::Win32::Graphics::MOTION_PLAY )
	{
		////////////////////////////////////////////////////////////////////////////////////////////////////
		// 時間( フレーム )の更新
		////////////////////////////////////////////////////////////////////////////////////////////////////

		m_Time += timeStep * m_pSelected->GetSpeed();

		if( m_pSelected->GetLoop() == true )
		{
			if( m_pSelected->GetLoopEndTime() < m_Time )
			{
				m_Time = m_pSelected->GetLoopStartTime();
			}
		}
		else if( m_pSelected->GetLastTime() < m_Time )
		{
			m_Time = m_pSelected->GetLastTime();
			PauseMotion( m_pSelected );
		}
	}
}

Mix::Tool::Win32::Object::TYPE MotionController::GetType( void ) const
{
	return Mix::Tool::Win32::Object::GRAPHICS__MOTION_CONTROLLER;
}

}}}}
