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

#include "Mix/Private/Dynamics/KinematicCharacter.h"
#include "Mix/Scene/IRendererObject.h"

namespace Mix{ namespace Scene{ namespace Common{

const UInt16 ActorKinematicCharacter::FILTER_CAST_GROUP		= Mix::Dynamics::OF_CHARACTER;
const UInt16 ActorKinematicCharacter::FILTER_CAST_MASK		= Mix::Dynamics::OF_STATIC | Mix::Dynamics::OF_CHARACTER;

const UInt16 ActorKinematicCharacter::FILTER_RECEIVE_GROUP	= Mix::Dynamics::OF_DEBRIS;
const UInt16 ActorKinematicCharacter::FILTER_RECEIVE_MASK	= Mix::Dynamics::OF_STATIC;

ActorKinematicCharacter* ActorKinematicCharacter::CreateInstance(	Mix::Dynamics::IKinematicCharacter* pInternalObject,
																	const Mix::Matrix4x4& loadMat,
																	const Mix::Matrix4x4& storeMat )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, ActorKinematicCharacter, pInternalObject, loadMat, storeMat );
}

ActorKinematicCharacter::ActorKinematicCharacter(	Mix::Dynamics::IKinematicCharacter* pInternalObject,
													const Mix::Matrix4x4& loadMat,
													const Mix::Matrix4x4& storeMat ) :
m_pOwner( NULL ),
m_pInternalObject( NULL ),
m_Mode( Mix::Scene::DKC_CAST ),
m_LoadMat( loadMat ),
m_StoreMat( storeMat ),
m_Gravity( 0.0f ),
m_AngularVelocity( Mix::Quaternion::Identity() ),
m_LinearVelocity( Mix::Vector3::Zero() )
{
	MIX_ASSERT( pInternalObject != NULL );

	m_FilterTable[Mix::Scene::DKC_CAST].group = ActorKinematicCharacter::FILTER_CAST_GROUP;
	m_FilterTable[Mix::Scene::DKC_CAST].mask = ActorKinematicCharacter::FILTER_CAST_MASK;
	m_FilterTable[Mix::Scene::DKC_RECEIVE].group = ActorKinematicCharacter::FILTER_RECEIVE_GROUP;
	m_FilterTable[Mix::Scene::DKC_RECEIVE].mask = ActorKinematicCharacter::FILTER_RECEIVE_MASK;

	MIX_ADD_REF( pInternalObject );
	m_pInternalObject = pInternalObject;
	m_Gravity = m_pInternalObject->GetGravity();

	UpdateFilter();

	DynamicsObject::Initialize( this );
}

ActorKinematicCharacter::~ActorKinematicCharacter( void )
{
	MIX_ASSERT( m_pOwner == NULL );
	MIX_RELEASE( m_pInternalObject );
}

void ActorKinematicCharacter::UpdateFilter( void )
{
	MIX_ASSERT( ( m_Mode == Mix::Scene::DKC_CAST ) || ( m_Mode == Mix::Scene::DKC_RECEIVE ) );

	const Mix::Scene::DYNAMICS_FILTER& filter = m_FilterTable[m_Mode];

	m_pInternalObject->SetFilterGroup( filter.group );
	m_pInternalObject->SetFilterMask( filter.mask );
}

void ActorKinematicCharacter::SetOwner( Mix::Scene::IRendererObject* pOwner )
{
	MIX_ASSERT( pOwner != NULL );
	MIX_ASSERT( m_pOwner == NULL );

	m_pOwner = pOwner;
}

Mix::Scene::DYNAMICS_KCHAR_MODE ActorKinematicCharacter::GetMode( void ) const
{
	return m_Mode;
}

void ActorKinematicCharacter::SetMode( Mix::Scene::DYNAMICS_KCHAR_MODE mode )
{
	if( mode == Mix::Scene::DKC_RECEIVE )
	{
		//重力を 0.0f にしておかないと、地面に近づくとプルプルする
		m_pInternalObject->SetGravity( 0.0f );
	}
	else
	{
		m_pInternalObject->SetGravity( m_Gravity );
	}

	m_Mode = mode;

	UpdateFilter();
}

void ActorKinematicCharacter::ApplyVelocity( const Mix::Quaternion& angularVelocity, const Mix::Vector3& linearVelocity )
{
	if( m_pInternalObject->IsInWorld() == True )
	{
		m_pInternalObject->SetWorldRotation( ( m_pInternalObject->GetWorldRotation() * angularVelocity ).ToNormalize() );
		m_pInternalObject->SetLinearVelocity( linearVelocity );
	}
}

void ActorKinematicCharacter::Reset( const Mix::Matrix4x4& baseMat )
{
	Mix::Matrix4x4 mat = m_LoadMat * baseMat;

	m_pInternalObject->SetWorldTransform( mat.GetRotation(), mat.GetTranslation() );
}

void ActorKinematicCharacter::Update( const Mix::Matrix4x4& baseMat )
{
	if( m_pInternalObject->IsInWorld() == True )
	{
		if( m_Mode == Mix::Scene::DKC_RECEIVE )
		{
			Mix::Matrix4x4 worldMat = m_LoadMat * baseMat;
			Mix::Quaternion worldRot = worldMat.GetRotation();
			Mix::Vector3 linearVelocity = worldMat.GetTranslation() - m_pInternalObject->GetWorldPosition();

			m_pInternalObject->SetWorldRotation( worldRot );
			m_pInternalObject->SetLinearVelocity( linearVelocity );
		}
	}
	else
	{
		Mix::Matrix4x4 worldMat = m_LoadMat * baseMat;

		m_pInternalObject->SetWorldTransform( worldMat.GetRotation(), worldMat.GetTranslation() );
	}
}

Boolean ActorKinematicCharacter::CanRefresh( void ) const
{
	if( ( m_pInternalObject->IsInWorld() == False ) ||
		( GetMode() == Mix::Scene::DKC_RECEIVE ) )
	{
		return False;
	}

	return True;
}

Mix::Matrix4x4 ActorKinematicCharacter::Refresh( const Mix::Vector3& worldS ) const
{
	Mix::Matrix4x4 matS;
	Mix::Matrix4x4 matRT;

	matS.SetScaling( worldS );
	matRT = m_StoreMat * m_pInternalObject->GetWorldMatrix();

	return matS * matRT;
}

void ActorKinematicCharacter::Dispose( void )
{
	m_pOwner = NULL;
}

Mix::Scene::Common::ActorKinematicCharacter* ActorKinematicCharacter::Clone( void )
{
	Mix::Dynamics::KinematicCharacter* pInternalKChar = NULL;
	Mix::Scene::Common::ActorKinematicCharacter* pKChar = NULL;

	pInternalKChar = Mix::Dynamics::KinematicCharacter::CreateInstance();
	if( pInternalKChar != NULL )
	{
		if( pInternalKChar->Initialize( m_pInternalObject->GetHeight(), m_pInternalObject->GetRadius(), m_pInternalObject->GetStepHeight(), L"KinematicCharacter" ) == True )
		{
			pInternalKChar->SetGravity( m_pInternalObject->GetGravity() );
			pInternalKChar->SetMaxFallSpeed( m_pInternalObject->GetMaxFallSpeed() );
			pInternalKChar->SetInitalJumpSpeed( m_pInternalObject->GetInitalJumpSpeed() );
			pInternalKChar->SetSlopeLimit( m_pInternalObject->GetSlopeLimit() );
		}
		else
		{
			MIX_RELEASE( pInternalKChar );
			return NULL;
		}
	}
	else
	{
		return NULL;
	}

	pKChar = Mix::Scene::Common::ActorKinematicCharacter::CreateInstance( pInternalKChar, m_LoadMat, m_StoreMat );
	if( pKChar == NULL )
	{
		MIX_RELEASE( pInternalKChar );
		return False;
	}

	MIX_RELEASE( pInternalKChar );

	return pKChar;
}

Mix::Dynamics::IKinematicCharacter* ActorKinematicCharacter::GetInternalKinematicCharacterPtr( void ) const
{
	return m_pInternalObject;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::Common::DynamicsObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Dynamics::IObject* ActorKinematicCharacter::GetInternalObjectPtr( void ) const
{
	return m_pInternalObject;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IActorKinematicCharacter
////////////////////////////////////////////////////////////////////////////////////////////////////

UInt16 ActorKinematicCharacter::GetCurrentFilterGroup( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetFilterGroup();
}

UInt16 ActorKinematicCharacter::GetFilterGroup( Mix::Scene::DYNAMICS_KCHAR_MODE mode ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_FilterTable[mode].group;
}

void ActorKinematicCharacter::SetFilterGroup( Mix::Scene::DYNAMICS_KCHAR_MODE mode, UInt16 filterGroup )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	m_FilterTable[mode].group = filterGroup;

	if( m_Mode == mode )
	{
		UpdateFilter();
	}
}

UInt16 ActorKinematicCharacter::GetCurrentFilterMask( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetFilterMask();
}

UInt16 ActorKinematicCharacter::GetFilterMask( Mix::Scene::DYNAMICS_KCHAR_MODE mode ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_FilterTable[mode].mask;
}

void ActorKinematicCharacter::SetFilterMask( Mix::Scene::DYNAMICS_KCHAR_MODE mode, UInt16 filterMask )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	m_FilterTable[mode].mask = filterMask;

	if( m_Mode == mode )
	{
		UpdateFilter();
	}
}

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

void ActorKinematicCharacter::SetAngularVelocity( const Mix::Quaternion& vel )
{
	m_AngularVelocity = vel;
}

void ActorKinematicCharacter::Stand( UInt32 upAxisIndex, const Mix::Vector3& worldUp )
{
	MIX_ASSERT( upAxisIndex <= 2 );
	MIX_ASSERT( m_pInternalObject != NULL );

	Mix::Quaternion diffRot = Mix::ShortestArc( m_pInternalObject->GetWorldMatrix().GetRow( upAxisIndex ), worldUp );
	Mix::Quaternion worldRot = m_pInternalObject->GetWorldRotation() * diffRot;

	m_pInternalObject->SetWorldRotation( worldRot );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IKinematicCharacter
////////////////////////////////////////////////////////////////////////////////////////////////////

Float32 ActorKinematicCharacter::GetHeight( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetHeight();
}

Float32 ActorKinematicCharacter::GetRadius( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetRadius();
}

Float32 ActorKinematicCharacter::GetGravity( void ) const
{
	return m_Gravity;
}

void ActorKinematicCharacter::SetGravity( Float32 gravity )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	if( m_Mode == Mix::Scene::DKC_RECEIVE )
	{
		m_pInternalObject->SetGravity( 0.0f );
	}
	else
	{
		m_pInternalObject->SetGravity( gravity );
	}

	m_Gravity = gravity;
}

Float32 ActorKinematicCharacter::GetMaxFallSpeed( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetMaxFallSpeed();
}

void ActorKinematicCharacter::SetMaxFallSpeed( Float32 speed )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	m_pInternalObject->SetMaxFallSpeed( speed );
}

Float32 ActorKinematicCharacter::GetInitalJumpSpeed( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetInitalJumpSpeed();
}

void ActorKinematicCharacter::SetInitalJumpSpeed( Float32 speed )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	m_pInternalObject->SetInitalJumpSpeed( speed );
}

Float32 ActorKinematicCharacter::GetStepHeight( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetStepHeight();
}

void ActorKinematicCharacter::SetStepHeight( Float32 height )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	m_pInternalObject->SetStepHeight( height );
}

Float32 ActorKinematicCharacter::GetSlopeLimit( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetSlopeLimit();
}

void ActorKinematicCharacter::SetSlopeLimit( Float32 rad )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	m_pInternalObject->SetSlopeLimit( rad );
}

const Mix::Dynamics::MATERIAL& ActorKinematicCharacter::GetMaterial( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetMaterial();
}

void ActorKinematicCharacter::SetMaterial( const Mix::Dynamics::MATERIAL& material )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	m_pInternalObject->SetMaterial( material );
}

Boolean ActorKinematicCharacter::OnGround( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->OnGround();
}

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

void ActorKinematicCharacter::SetLinearVelocity( const Mix::Vector3& vel )
{
	m_LinearVelocity = vel;
}

Float32 ActorKinematicCharacter::GetActiveThreshold( void ) const
{
	return m_pInternalObject->GetActiveThreshold();
}

void ActorKinematicCharacter::SetActiveThreshold( Float32 threshold )
{
	m_pInternalObject->SetActiveThreshold( threshold );
}

Float32 ActorKinematicCharacter::GetDeactivationElapsedTime( void ) const
{
	return m_pInternalObject->GetDeactivationElapsedTime();
}

void ActorKinematicCharacter::SetDeactivationElapsedTime( Float32 elapsedTime )
{
	m_pInternalObject->SetDeactivationElapsedTime( elapsedTime );
}

Boolean ActorKinematicCharacter::IsActive( void ) const
{
	return ( ( m_pInternalObject->IsInWorld() == True ) && ( m_pInternalObject->IsActive() == True ) );
}

Boolean ActorKinematicCharacter::CanJump( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	if( ( m_Mode != Mix::Scene::DKC_CAST ) ||
		( m_pInternalObject->OnGround() == False ) )
	{
		return False;
	}

	return True;
}

Boolean ActorKinematicCharacter::Jump( void )
{
	if( CanJump() == True )
	{
		MIX_ASSERT( m_pInternalObject != NULL );
		m_pInternalObject->Jump();
	}
	else
	{
		return False;
	}

	return True;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IDynamicsObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Scene::IDynamicsObject::TYPE ActorKinematicCharacter::GetType( void ) const
{
	return Mix::Scene::IDynamicsObject::ACTOR_KINEMATIC_CHARACTER;
}

Boolean ActorKinematicCharacter::GetOwner( Mix::Scene::IRendererObject** ppOwner )
{
	if( m_pOwner == NULL )
	{
		return False;
	}

	MIX_ADD_REF( m_pOwner );
	( *ppOwner ) = m_pOwner;

	return True;
}

Mix::Scene::IRendererObject* ActorKinematicCharacter::GetOwnerPtr( void ) const
{
	return m_pOwner;
}

Mix::Matrix4x4 ActorKinematicCharacter::GetWorldMatrix( void ) const
{
	return m_pInternalObject->GetWorldMatrix();
}

Mix::Quaternion ActorKinematicCharacter::GetWorldRotation( void ) const
{
	return m_pInternalObject->GetWorldRotation();
}

Mix::Vector3 ActorKinematicCharacter::GetWorldPosition( void ) const
{
	return m_pInternalObject->GetWorldPosition();
}

Boolean ActorKinematicCharacter::HasContactListener( void ) const
{
	return DynamicsObject::HasContactListener();
}

Boolean ActorKinematicCharacter::ContainsContactListener( Mix::Scene::IContactListener* pListener ) const
{
	return DynamicsObject::ContainsContactListener( pListener );
}

Boolean ActorKinematicCharacter::AddContactListener( Mix::Scene::IContactListener* pListener )
{
	return DynamicsObject::AddContactListener( pListener );
}

Boolean ActorKinematicCharacter::RemoveContactListener( Mix::Scene::IContactListener* pListener )
{
	return DynamicsObject::RemoveContactListener( pListener );
}

void ActorKinematicCharacter::ClearContactListener( void )
{
	DynamicsObject::ClearContactListener();
}

Int32 ActorKinematicCharacter::GetUserIndex( void )  const
{
	return DynamicsObject::GetUserIndex();
}

void ActorKinematicCharacter::SetUserIndex( Int32 index )
{
	DynamicsObject::SetUserIndex( index );
}

void* ActorKinematicCharacter::GetUserPtr( void ) const
{
	return DynamicsObject::GetUserPtr();
}

void ActorKinematicCharacter::SetUserPtr( void* ptr )
{
	DynamicsObject::SetUserPtr( ptr );
}

}}}
