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

#include "Mix/Dynamics/IManager.h"
#include "Mix/Dynamics/ICapsuleShape.h"
#include "Mix/Dynamics/IKinematicCharacter.h"

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

namespace Mix{ namespace Scene{ namespace Common{

const wchar_t* KinematicGhost::FAILED_CREATE = L"キネマティックゴーストの作成に失敗";

const UInt16 KinematicGhost::FILTER_SKIN_GROUP	= Mix::Dynamics::OF_CHARACTER;
const UInt16 KinematicGhost::FILTER_SKIN_MASK	= Mix::Dynamics::OF_STATIC | Mix::Dynamics::OF_CHARACTER;

const UInt16 KinematicGhost::FILTER_FLESH_GROUP	= Mix::Dynamics::OF_KINEMATIC;
const UInt16 KinematicGhost::FILTER_FLESH_MASK	= Mix::Dynamics::OF_DEFAULT | Mix::Dynamics::OF_SENSOR;

////////////////////////////////////////////////////////////////////////////////////////////////////
// KinematicGhost::InternalKinematicCharacter
////////////////////////////////////////////////////////////////////////////////////////////////////

KinematicGhost::InternalKinematicCharacter* KinematicGhost::InternalKinematicCharacter::CreateInstance( Mix::Scene::IRendererObject* pOwner )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, KinematicGhost::InternalKinematicCharacter, pOwner );
}

KinematicGhost::InternalKinematicCharacter::InternalKinematicCharacter( Mix::Scene::IRendererObject* pOwner ) :
m_pOwner( NULL ),
m_pInternalObject( NULL )
{
	MIX_ASSERT( pOwner != NULL );
	m_pOwner = pOwner;
}

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

Boolean KinematicGhost::InternalKinematicCharacter::Initialize( Float32 height, Float32 radius, Float32 stepHeight, const wchar_t* pDebugName )
{
	MIX_ASSERT( pDebugName != NULL );
	MIX_ASSERT( m_pInternalObject == NULL );
	MIX_ASSERT( Mix::Dynamics::GetManagerPtr() != NULL );

	if( Mix::Dynamics::GetManagerPtr()->CreateKinematicCharacter( height, radius, stepHeight, &m_pInternalObject, pDebugName ) == True )
	{
		m_pInternalObject->SetFilterGroup( KinematicGhost::FILTER_SKIN_GROUP );
		m_pInternalObject->SetFilterMask( KinematicGhost::FILTER_SKIN_MASK );

		DynamicsObject::Initialize( this );
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : Type[Kinematic] %s[%s]", Ghost::FAILED_ADD, Mix::STR_OUTOFMEMORY, Mix::STR_DEBUGNAME, pDebugName );
		return False;
	}

	return True;
}

void KinematicGhost::InternalKinematicCharacter::Dispose( void )
{
	m_pOwner = NULL;
}

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

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

/*
	Mix::Scene::ISimpleKinematicCharacter
*/

UInt16 KinematicGhost::InternalKinematicCharacter::GetFilterGroup( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetFilterGroup();
}

void KinematicGhost::InternalKinematicCharacter::SetFilterGroup( UInt16 filterGroup )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	m_pInternalObject->SetFilterGroup( filterGroup );
}

UInt16 KinematicGhost::InternalKinematicCharacter::GetFilterMask( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetFilterMask();
}

void KinematicGhost::InternalKinematicCharacter::SetFilterMask( UInt16 filterMask )
{
	MIX_ASSERT( m_pInternalObject != NULL );
	m_pInternalObject->SetFilterMask( filterMask );
}

/*
	Mix::Scene::IKinematicCharacter
*/

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

	return m_pInternalObject->GetHeight();
}

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

	return m_pInternalObject->GetRadius();
}

Float32 KinematicGhost::InternalKinematicCharacter::GetGravity( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetGravity();
}

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

	m_pInternalObject->SetGravity( gravity );
}

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

	return m_pInternalObject->GetMaxFallSpeed();
}

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

	m_pInternalObject->SetMaxFallSpeed( speed );
}

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

	return m_pInternalObject->GetInitalJumpSpeed();
}

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

	m_pInternalObject->SetInitalJumpSpeed( speed );
}

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

	return m_pInternalObject->GetStepHeight();
}

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

	m_pInternalObject->SetStepHeight( height );
}

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

	return m_pInternalObject->GetSlopeLimit();
}

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

	m_pInternalObject->SetSlopeLimit( rad );
}

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

	return m_pInternalObject->GetMaterial();
}

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

	m_pInternalObject->SetMaterial( material );
}

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

	return m_pInternalObject->OnGround();
}

const Mix::Vector3& KinematicGhost::InternalKinematicCharacter::GetLinearVelocity( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetLinearVelocity();
}

void KinematicGhost::InternalKinematicCharacter::SetLinearVelocity( const Mix::Vector3& vel )
{
	MIX_ASSERT( m_pInternalObject != NULL );

	m_pInternalObject->SetLinearVelocity( vel );
}

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

	return ( m_pInternalObject->OnGround() == True );
}

Boolean KinematicGhost::InternalKinematicCharacter::Jump( void )
{
	if( IsEnabled() == False )
	{
		return False;
	}

	MIX_ASSERT( m_pInternalObject != NULL );

	m_pInternalObject->Jump();

	return ( m_pInternalObject->OnGround() == False );
}

/*
	Mix::Scene::IDynamicsObject
*/

Mix::Scene::IDynamicsObject::TYPE KinematicGhost::InternalKinematicCharacter::GetType( void ) const
{
	return Mix::Scene::IDynamicsObject::SIMPLE_KINEMATIC_CHARACTER;
}

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

	if( m_pOwner != NULL )
	{
		MIX_ADD_REF( m_pOwner );
		( *ppOwner ) = m_pOwner;
	}
	else
	{
		return False;
	}

	return True;
}

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

Mix::Matrix4x4 KinematicGhost::InternalKinematicCharacter::GetWorldMatrix( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetWorldMatrix();
}

Mix::Quaternion KinematicGhost::InternalKinematicCharacter::GetWorldRotation( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetWorldRotation();
}

Mix::Vector3 KinematicGhost::InternalKinematicCharacter::GetWorldPosition( void ) const
{
	MIX_ASSERT( m_pInternalObject != NULL );

	return m_pInternalObject->GetWorldPosition();
}

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

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

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

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

void KinematicGhost::InternalKinematicCharacter::ClearContactListener( void )
{
	DynamicsObject::ClearContactListener();
}

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

void KinematicGhost::InternalKinematicCharacter::SetUserIndex( Int32 index )
{
	DynamicsObject::SetUserIndex( index );
}

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

void KinematicGhost::InternalKinematicCharacter::SetUserPtr( void* ptr )
{
	DynamicsObject::SetUserPtr( ptr );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// KinematicGhost
////////////////////////////////////////////////////////////////////////////////////////////////////

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

KinematicGhost::KinematicGhost( void ) :
m_pKinematicCharacter( NULL ),
m_HalfHeight( 0.0f )
{
#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
	m_pCollider = NULL;
#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
}

KinematicGhost::~KinematicGhost( void )
{
#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	if( m_pCollider != NULL )
	{
		m_pCollider->Dispose();
	}

	MIX_RELEASE( m_pCollider );

#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	if( m_pKinematicCharacter != NULL )
	{
		m_pKinematicCharacter->Dispose();
	}

	MIX_RELEASE( m_pKinematicCharacter );
}

Boolean KinematicGhost::Initialize( Float32 height, Float32 radius, Float32 stepHeight, const wchar_t* pDebugName )
{
	MIX_ASSERT( height > 0.0f );
	MIX_ASSERT( radius > 0.0f );
	MIX_ASSERT( stepHeight > 0.0f );
	MIX_ASSERT( pDebugName != NULL );

#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
	MIX_ASSERT( m_pCollider == NULL );
#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	MIX_ASSERT( m_pKinematicCharacter == NULL );

	Mix::Dynamics::IManager* pDynamicsMgr = Mix::Dynamics::GetManagerPtr();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ダイナミクスが有効かどうかのチェック
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pDynamicsMgr == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : Type[Kinematic] %s[%s]", Ghost::FAILED_ADD, Mix::Scene::STR_DISABLED_DYNAMICS, Mix::STR_DEBUGNAME, pDebugName );
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 内部パラメータ
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_HalfHeight = height * 0.5f;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コライダーを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	Mix::Dynamics::ICapsuleShape* pShape = NULL;

	if( pDynamicsMgr->CreateCapsuleShape( Mix::Dynamics::AXIS_Y, height, radius, &pShape, pDebugName ) == True )
	{
		m_pCollider = LibertyCollider::CreateInstance( this );
		if( m_pCollider != NULL )
		{
			if( m_pCollider->Initialize( pShape, pDebugName ) == True )
			{
				m_pCollider->SetFilterGroup( KinematicGhost::FILTER_FLESH_GROUP );
				m_pCollider->SetFilterMask( KinematicGhost::FILTER_FLESH_MASK );
				m_pCollider->SetStatus( Mix::Dynamics::IRigidBody::KINEMATIC );
			}
			else
			{
				MIX_LOG_ERROR( L"%s : %s : Type[Kinematic] %s[%s]", Ghost::FAILED_ADD, Mix::STR_INITERROR, Mix::STR_DEBUGNAME, pDebugName );
				MIX_RELEASE( pShape );
				return False;
			}
		}
		else
		{
			MIX_LOG_ERROR( L"%s : %s : Type[Kinematic] %s[%s]", Ghost::FAILED_ADD, Mix::STR_OUTOFMEMORY, Mix::STR_DEBUGNAME, pDebugName );
			MIX_RELEASE( pShape );
			return False;
		}
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : Type[Kinematic] %s[%s]", Ghost::FAILED_ADD, Mix::STR_INITERROR, Mix::STR_DEBUGNAME, pDebugName );
		return False;
	}

	MIX_RELEASE( pShape );

#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// キネマティックキャラクターを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pKinematicCharacter = KinematicGhost::InternalKinematicCharacter::CreateInstance( this );
	if( m_pKinematicCharacter != NULL )
	{
		if( m_pKinematicCharacter->Initialize( height, radius, stepHeight, pDebugName ) == False )
		{
			return False;
		}
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : Type[Kinematic] %s[%s]", Ghost::FAILED_ADD, Mix::STR_OUTOFMEMORY, Mix::STR_DEBUGNAME, pDebugName );
		return False;
	}

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

	return True;
}

/*
	Mix::Scene::Comon::Ghost
*/

Mix::Dynamics::IObject* KinematicGhost::GetInternalObjectPtr( void ) const
{
	MIX_ASSERT( m_pKinematicCharacter != NULL );
	MIX_ASSERT( m_pKinematicCharacter->GetInternalObjectPtr() != NULL );

	return m_pKinematicCharacter->GetInternalObjectPtr();
}

void KinematicGhost::AttachDynamics( Mix::Dynamics::IWorld* pWorld, Mix::Dynamics::IObjectListener* pObjectListener )
{
	MIX_ASSERT( m_pKinematicCharacter != NULL );
	m_pKinematicCharacter->Attach( pWorld, pObjectListener );

#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	MIX_ASSERT( m_pCollider != NULL );
	m_pCollider->Attach( pWorld, pObjectListener );

#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
}

void KinematicGhost::DetachDynamics( Mix::Dynamics::IWorld* pWorld )
{
	MIX_ASSERT( m_pKinematicCharacter != NULL );
	m_pKinematicCharacter->Detach( pWorld );

#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	MIX_ASSERT( m_pCollider != NULL );
	m_pCollider->Detach( pWorld );

#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
}

/*
	Mix::Scene::IGhost
*/

Boolean KinematicGhost::GetDynamicsObject( Mix::Scene::IDynamicsObject** ppDynamicsObject )
{
	if( ppDynamicsObject == NULL )
	{
		return False;
	}

	if( m_pKinematicCharacter != NULL )
	{
		MIX_ADD_REF( m_pKinematicCharacter );
		( *ppDynamicsObject ) = m_pKinematicCharacter;
	}
	else
	{
		return False;
	}

	return True;
}

Mix::Quaternion KinematicGhost::GetWorldRotation( void ) const
{
	if( m_pKinematicCharacter == NULL )
	{
		return Mix::Quaternion::Identity();
	}

	return m_pKinematicCharacter->GetWorldRotation();
}

void KinematicGhost::SetWorldRotation( const Mix::Quaternion& rot )
{
	MIX_ASSERT( m_pKinematicCharacter->GetInternalObjectPtr() != NULL );

#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	if( ( m_pCollider != NULL ) &&
		( m_pKinematicCharacter != NULL ) )
	{
		//キネマティックキャラクターは回転できないので、コライダーに回転は設定しない
		m_pKinematicCharacter->GetInternalObjectPtr()->SetWorldRotation( rot );
	}

#else //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	if( m_pKinematicCharacter != NULL )
	{
		//キネマティックキャラクターは回転できないので、コライダーに回転は設定しない
		m_pKinematicCharacter->GetInternalObjectPtr()->SetWorldRotation( rot );
	}

#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
}

Mix::Vector3 KinematicGhost::GetWorldPosition( void ) const
{
	if( m_pKinematicCharacter == NULL )
	{
		return Mix::Vector3::Zero();
	}

	Mix::Vector3 pos = m_pKinematicCharacter->GetWorldPosition();

	return Mix::Vector3( pos.x, pos.y - m_HalfHeight, pos.z );
}

void KinematicGhost::SetWorldPosition( const Mix::Vector3& pos )
{
	Mix::Vector3 objPos( pos.x, pos.y + m_HalfHeight, pos.z );

#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	if( ( m_pCollider != NULL ) &&
		( m_pKinematicCharacter != NULL ) )
	{
		MIX_ASSERT( m_pCollider->GetInternalObjectPtr() != NULL );
		MIX_ASSERT( m_pKinematicCharacter->GetInternalObjectPtr() != NULL );

		m_pCollider->GetInternalObjectPtr()->SetWorldPosition( objPos );
		m_pKinematicCharacter->GetInternalObjectPtr()->SetWorldPosition( objPos );
	}

#else //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	if( m_pKinematicCharacter != NULL )
	{
		MIX_ASSERT( m_pKinematicCharacter->GetInternalObjectPtr() != NULL );

		m_pKinematicCharacter->GetInternalObjectPtr()->SetWorldPosition( objPos );
	}

#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
}

Mix::Matrix4x4 KinematicGhost::GetWorldMatrx( void ) const
{
	return Mix::Matrix4x4( GetWorldRotation(), GetWorldPosition() );
}

void KinematicGhost::SetWorldMatrix( const Mix::Matrix4x4& mat )
{
	Mix::Vector3 dummy;
	Mix::Quaternion rot;
	Mix::Vector3 pos;

	mat.Decompose( dummy, rot, pos );
	pos.y += m_HalfHeight;

#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	if( ( m_pCollider != NULL ) &&
		( m_pKinematicCharacter != NULL ) )
	{
		MIX_ASSERT( m_pCollider->GetInternalObjectPtr() != NULL );
		MIX_ASSERT( m_pKinematicCharacter->GetInternalObjectPtr() != NULL );

		m_pCollider->GetInternalObjectPtr()->SetWorldPosition( pos );
		m_pKinematicCharacter->GetInternalObjectPtr()->SetWorldTransform( rot, pos );
	}

#else //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED

	if( m_pKinematicCharacter != NULL )
	{
		MIX_ASSERT( m_pKinematicCharacter->GetInternalObjectPtr() != NULL );

		m_pKinematicCharacter->GetInternalObjectPtr()->SetWorldTransform( rot, pos );
	}

#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
}

Boolean KinematicGhost::NeedsRefresh( void ) const
{
	return True;
}

void KinematicGhost::Refresh( void )
{
#ifdef MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
	if( ( m_pCollider != NULL ) &&
		( m_pKinematicCharacter != NULL ) )
	{
		MIX_ASSERT( m_pCollider->GetInternalObjectPtr() != NULL );

		/*
			コライダーにワールドトランスフォームを設定
		*/

		m_pCollider->GetInternalObjectPtr()->SetWorldPosition( m_pKinematicCharacter->GetWorldPosition() );
	}
#endif //MIX_KINEMATIC_GHOST_COLLIDER_ENABLED
}

}}}
