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

#include "Mix/IO/IReader.h"
#include "Mix/Private/Scene/Common/MotionCurve.h"
#include "Mix/Private/Scene/Common/RootMotionCurve.h"
#include "Mix/Private/Scene/Common/MotionEntryProcesser.h"

namespace Mix{ namespace Scene{ namespace Common{

const wchar_t* Motion::FAILED_CREATE = L"モーションの作成に失敗";

Motion* Motion::CreateInstance( void )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, Motion );
}

Motion* Motion::CreateInstance( Mix::IO::IReader* pReader, Boolean bWithMagicNumber, const wchar_t* pNameLabel, const wchar_t* pName )
{
	MIX_ASSERT( pReader != NULL );

	Motion::MOT_DATA_HEADER dataHeader;
	Motion* pInterface = NULL;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ファイルヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( bWithMagicNumber == True )
	{
		Motion::MOT_FILE_HEADER fileHeader;

		if( pReader->Read( &fileHeader, sizeof( fileHeader ) ) != sizeof( fileHeader ) )
		{
			return NULL;
		}

		if( fileHeader.magicNumber != Motion::MOT_MAGIC_NUMBER )
		{
			MIX_LOG_ERROR( L"%s : %s(0) : %s[%s]", Motion::FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pNameLabel, pName );
			return NULL;
		}

		if( fileHeader.version != Motion::MOT_VERSION )
		{
			MIX_LOG_ERROR( L"%s : %s(0) : %s[%s]", Motion::FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pNameLabel, pName );
			return NULL;
		}
	}
	else
	{
		UInt32 version;

		if( pReader->Read( &version, sizeof( version ) ) != sizeof( version ) )
		{
			return NULL;
		}

		if( version != Motion::MOT_VERSION )
		{
			MIX_LOG_ERROR( L"%s : %s(0) : %s[%s]", Motion::FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pNameLabel, pName );
			return NULL;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// データヘッダ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pReader->Read( &dataHeader, sizeof( dataHeader ) ) != sizeof( dataHeader ) )
	{
		MIX_LOG_ERROR( L"%s : %s(1) : %s[%s]", Motion::FAILED_CREATE, Mix::STR_ILLEGALFORMAT, pNameLabel, pName );
		return NULL;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インターフェースの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pInterface = MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, Motion );
	if( pInterface == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", Motion::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pNameLabel, pName );
		return NULL;
	}

	pInterface->m_Name = dataHeader.name;
	pInterface->m_FramesPerSec = dataHeader.framesPerSec;
	pInterface->m_LastFrame = dataHeader.lastFrame;
	pInterface->m_LoopStartFrame = dataHeader.loopStartFrame;
	pInterface->m_LoopEndFrame = dataHeader.loopEndFrame;
	pInterface->m_Speed = dataHeader.speed;
	pInterface->m_BlendRatio = dataHeader.blendRatio;
	pInterface->m_BodyCurveList.reserve( dataHeader.btCurveNum );

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

	if( dataHeader.rtCurveNum > 0 )
	{
		Mix::Scene::Common::RootMotionCurve* pCurve = RootMotionCurve::CreateInstance( pReader, pNameLabel, pName );
		if( pCurve != NULL )
		{
			pInterface->m_pRootCurve = pCurve;
		}
		else
		{
			MIX_RELEASE( pInterface );
			return NULL;
		}
	}
	else
	{
		Mix::Scene::Common::RootMotionCurve* pCurve = RootMotionCurve::CreateInstance();
		if( pCurve != NULL )
		{
			pInterface->m_pRootCurve = pCurve;
		}
		else
		{
			MIX_RELEASE( pInterface );
			return NULL;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ボディトランスフォーム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( UInt32 i = 0; i < dataHeader.btCurveNum; i++ )
	{
		Mix::Scene::Common::MotionCurve* pCurve = MotionCurve::CreateInstance( pReader, pNameLabel, pName );
		if( pCurve != NULL )
		{
			pInterface->m_BodyCurveList.push_back( pCurve );
		}
		else
		{
			MIX_RELEASE( pInterface );
			return NULL;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ログ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_LOG_INFO( L"モーションを作成 : Name[%s] FPS[%f] LastFrame[%f] Loop[%f-%f] Speed[%f] BlendRatio[%f] RootCurve[%s] BodyCurveCount[%d] %s[%s]",
		pInterface->GetName(),
		pInterface->GetFramesPerSec(),
		pInterface->GetLastFrame(),
		pInterface->GetLoopStartFrame(),
		pInterface->GetLoopEndFrame(),
		pInterface->GetSpeed(),
		pInterface->GetBlendRatio(),
		MIX_LOG_PTR( pInterface->GetRootCurvePtr() ),
		pInterface->GetBodyCurveCount(),
		pNameLabel, pName );

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

	return pInterface;
}

Motion::Motion( void ) :
m_Name( L"" ),
m_FramesPerSec( 0.0f ),
m_LastFrame( 0.0f ),
m_LoopStartFrame( 0.0f ),
m_LoopEndFrame( 0.0f ),
m_pRootCurve( NULL ),
m_BlendRatio( 1.0f ),
m_Speed( 1.0f )
{
}

Motion::~Motion( void )
{
	MIX_RELEASE( m_pRootCurve );

	for( Motion::CurveList::iterator it = m_BodyCurveList.begin(); it != m_BodyCurveList.end(); ++it )
	{
		MIX_RELEASE( ( *it ) );
	}

	for( Motion::EntryProcesserList::iterator it = m_EntryProcesserList.begin(); it != m_EntryProcesserList.end(); ++it )
	{
		( *it )->Build( NULL, 0 );
		( *it )->SetSubjectPtr( NULL );
	}
}

Mix::Scene::Common::RootMotionCurve* Motion::GetRootCurvePtr( void )
{
	return m_pRootCurve;
}

Mix::Scene::Common::MotionCurve* Motion::GetBodyCurvePtr( UInt32 index )
{
	MIX_ASSERT( m_BodyCurveList.size() > index );

	return m_BodyCurveList[index];
}

UInt32 Motion::GetBodyCurveCount( void ) const
{
	return MIX_UIT_TO_UI32( m_BodyCurveList.size() );
}

const wchar_t* Motion::GetName( void ) const
{
	return m_Name.GetConstPtr();
}

Float32 Motion::GetFramesPerSec( void ) const
{
	return m_FramesPerSec;
}

Float32 Motion::GetLastFrame( void ) const
{
	return m_LastFrame;
}

Float32 Motion::GetLoopStartFrame( void ) const
{
	return m_LoopStartFrame;
}

Float32 Motion::GetLoopEndFrame( void ) const
{
	return m_LoopEndFrame;
}

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

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

void Motion::SetBlendRatio( Float32 ratio )
{
	m_BlendRatio = MIX_FLOAT_SATURATE( ratio );
}

Float32 Motion::GetBlendRatio( void ) const
{
	return m_BlendRatio;
}

void Motion::SetEntry( const Mix::Scene::MOTION_ENTRY_DESC* entries, UInt32 count )
{
	Motion::EntryProcesserList::iterator it_begin = m_EntryProcesserList.begin();
	Motion::EntryProcesserList::iterator it_end = m_EntryProcesserList.end();
	Motion::EntryProcesserList::iterator it;

	m_EntryList.clear();

	if( ( entries != NULL ) &&
		( count > 0 ) )
	{
		Mix::Scene::MOTION_ENTRY_DESC* entrisPtr;
		UIntT entryCount;

		m_EntryList.reserve( count );

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

		std::sort( m_EntryList.begin(), m_EntryList.end(), Motion::ENTRY_SORT() );

		entrisPtr = &( m_EntryList[0] );
		entryCount = m_EntryList.size();

		for( it = it_begin; it != it_end; ++it )
		{
			( *it )->Build( entrisPtr, entryCount );
		}
	}
	else
	{
		for( it = it_begin; it != it_end; ++it )
		{
			( *it )->Build( NULL, 0 );
		}
	}
}

const Mix::Scene::MOTION_ENTRY_DESC* Motion::GetEntryPtr( UInt32 index ) const
{
	if( m_EntryList.size() <= index )
	{
		return NULL;
	}

	return &( m_EntryList[index] );
}

UInt32 Motion::GetEntryCount( void ) const
{
	return MIX_UIT_TO_UI32( m_EntryList.size() );
}

Boolean Motion::Clone( Mix::Scene::IMotion** ppMotion, Boolean bDuplicateEntries )
{
	if( ppMotion == NULL )
	{
		return False;
	}

	Mix::Scene::Common::Motion* pMotion = NULL;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インターフェースを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pMotion = Mix::Scene::Common::Motion::CreateInstance();
	if( pMotion == NULL )
	{
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// パラメータを複製
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pMotion->m_Name = m_Name;
	pMotion->m_FramesPerSec = m_FramesPerSec;
	pMotion->m_LastFrame = m_LastFrame;
	pMotion->m_LoopStartFrame = m_LoopStartFrame;
	pMotion->m_LoopEndFrame = m_LoopEndFrame;
	pMotion->m_Speed = m_Speed;
	pMotion->m_BlendRatio = m_BlendRatio;

	MIX_ADD_REF( m_pRootCurve );
	pMotion->m_pRootCurve = m_pRootCurve;

	if( m_BodyCurveList.size() > 0 )
	{
		pMotion->m_BodyCurveList.reserve( m_BodyCurveList.size() );

		for( Motion::CurveList::iterator it = m_BodyCurveList.begin(); it != m_BodyCurveList.end(); ++it )
		{
			Mix::Scene::Common::MotionCurve* pCurve = ( *it );

			MIX_ADD_REF( pCurve );
			pMotion->m_BodyCurveList.push_back( pCurve );
		}
	}

	if( ( bDuplicateEntries == True ) &&
		( m_EntryList.size() > 0 ) )
	{
		pMotion->m_EntryList.reserve( m_EntryList.size() );

		for( Motion::EntryList::iterator it = m_EntryList.begin(); it != m_EntryList.end(); ++it )
		{
			pMotion->m_EntryList.push_back( ( *it ) );
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 譲渡
	////////////////////////////////////////////////////////////////////////////////////////////////////

	( *ppMotion ) = pMotion;

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

	return True;
}

Mix::Scene::Common::MotionEntryProcesser* Motion::AddEntryProcesser( void )
{
	Mix::Scene::Common::MotionEntryProcesser* pEntryProcesser = MotionEntryProcesser::CreateInstance();
	if( pEntryProcesser == NULL )
	{
		return NULL;
	}

	pEntryProcesser->SetSubjectPtr( this );
	m_EntryProcesserList.push_back( pEntryProcesser );

	return pEntryProcesser;
}

void Motion::RemoveEntryProcesser( Mix::Scene::Common::MotionEntryProcesser* pEntryProcesser )
{
	MIX_ASSERT( pEntryProcesser != NULL );

	pEntryProcesser->SetSubjectPtr( NULL );
	m_EntryProcesserList.remove( pEntryProcesser );
}

}}}
