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

#include "Mix/Dynamics/IManager.h"
#include "Mix/Dynamics/IWorld.h"
#include "Mix/Dynamics/IStaticPlane.h"

namespace Mix{ namespace Scene{ namespace Common{

const wchar_t* Floor::FAILED_CREATE = L"フロアの作成に失敗";

////////////////////////////////////////////////////////////////////////////////////////////////////
// Floor::InternalCollider
////////////////////////////////////////////////////////////////////////////////////////////////////

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

Floor::InternalCollider::InternalCollider( Mix::Scene::IRendererObject* pOwner ) :
m_pOwner( NULL ),
m_pStaticPlane( NULL )
{
	MIX_ASSERT( pOwner != NULL );
	m_pOwner = pOwner;
}

Floor::InternalCollider::~InternalCollider( void )
{
	MIX_ASSERT( m_pOwner == NULL );
	MIX_RELEASE( m_pStaticPlane );
}

Boolean Floor::InternalCollider::Initialize( const Mix::Vector3& normal, Float32 offset, const wchar_t* pDebugName )
{
	MIX_ASSERT( Mix::Dynamics::GetManagerPtr() != NULL );
	MIX_ASSERT( m_pStaticPlane == NULL );

	if( Mix::Dynamics::GetManagerPtr()->CreateStaticPlane( normal, offset, &m_pStaticPlane, pDebugName ) == True )
	{
		DynamicsObject::Initialize( this );
	}
	else
	{
		return False;
	}

	return True;
}

void Floor::InternalCollider::Dispose( void )
{
	m_pOwner = NULL;
}

void Floor::InternalCollider::SetWorldRotation( const Mix::Quaternion& rot )
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	m_pStaticPlane->SetWorldRotation( rot );
}

void Floor::InternalCollider::SetWorldPosition( const Mix::Vector3& pos )
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	m_pStaticPlane->SetWorldPosition( pos );
}

void Floor::InternalCollider::SetWorldMatrix( const Mix::Matrix4x4& mat )
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	Mix::Vector3 ws;
	Mix::Quaternion wr;
	Mix::Vector3 wt;

	mat.Decompose( ws, wr, wt );

	m_pStaticPlane->SetWorldTransform( wr, wt );
}

Mix::Dynamics::IStaticPlane* Floor::InternalCollider::GetStaticPlanePtr( void ) const
{
	return m_pStaticPlane;
}

Mix::Dynamics::IObject* Floor::InternalCollider::GetInternalObjectPtr( void ) const
{
	return m_pStaticPlane;
}

UInt16 Floor::InternalCollider::GetFilterGroup( void ) const
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	return m_pStaticPlane->GetFilterGroup();
}

void Floor::InternalCollider::SetFilterGroup( UInt16 filterGroup )
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	m_pStaticPlane->SetFilterGroup( filterGroup );
}

UInt16 Floor::InternalCollider::GetFilterMask( void ) const
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	return m_pStaticPlane->GetFilterMask();
}

void Floor::InternalCollider::SetFilterMask( UInt16 filterMask )
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	m_pStaticPlane->SetFilterMask( filterMask );
}

const Mix::Dynamics::MATERIAL& Floor::InternalCollider::GetMaterial( void ) const
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	return m_pStaticPlane->GetMaterial();
}

void Floor::InternalCollider::SetMaterial( const Mix::Dynamics::MATERIAL& material )
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	m_pStaticPlane->SetMaterial( material );
}

Boolean Floor::InternalCollider::IsDefault( void ) const
{
	return False;
}

Boolean Floor::InternalCollider::IsStatic( void ) const
{
	return True;
}

Boolean Floor::InternalCollider::IsKinematic( void ) const
{
	return False;
}

Mix::Scene::IDynamicsObject::TYPE Floor::InternalCollider::GetType( void ) const
{
	return Mix::Scene::IDynamicsObject::SIMPLE_COLLIDER;
}

Boolean Floor::InternalCollider::GetOwner( Mix::Scene::IRendererObject** ppOwner )
{
	if( m_pOwner != NULL )
	{
		MIX_ADD_REF( m_pOwner );
		( *ppOwner ) = m_pOwner;
	}
	else
	{
		return False;
	}

	return True;
}

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

Mix::Matrix4x4 Floor::InternalCollider::GetWorldMatrix( void ) const
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	return m_pStaticPlane->GetWorldMatrix();
}

Mix::Quaternion Floor::InternalCollider::GetWorldRotation( void ) const
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	return m_pStaticPlane->GetWorldRotation();
}

Mix::Vector3 Floor::InternalCollider::GetWorldPosition( void ) const
{
	MIX_ASSERT( m_pStaticPlane != NULL );

	return m_pStaticPlane->GetWorldPosition();
}

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

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

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

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

void Floor::InternalCollider::ClearContactListener( void )
{
	DynamicsObject::ClearContactListener();
}

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

void Floor::InternalCollider::SetUserIndex( Int32 index )
{
	DynamicsObject::SetUserIndex( index );
}

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

void Floor::InternalCollider::SetUserPtr( void* ptr )
{
	DynamicsObject::SetUserPtr( ptr );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Floor
////////////////////////////////////////////////////////////////////////////////////////////////////

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

Floor::Floor( void ) :
m_pCollider( NULL ),
m_UserIndex( 0 ),
m_UserPtr( NULL )
{
}

Floor::~Floor( void )
{
	if( m_pCollider != NULL )
	{
		m_pCollider->Dispose();
	}

	MIX_RELEASE( m_pCollider );
}

Boolean Floor::Initialize( const Mix::Vector3& normal, Float32 offset, const wchar_t* pDebugName )
{
	MIX_ASSERT( pDebugName != NULL );
	MIX_ASSERT( m_pCollider == NULL );

	Mix::String safeDebugName = MIX_SAFE_NAME( pDebugName );

	if( Mix::Dynamics::GetManagerPtr() == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", Floor::FAILED_CREATE, Mix::Scene::STR_DISABLED_DYNAMICS, Mix::STR_DEBUGNAME, safeDebugName.GetConstPtr() );
		return False;
	}

	m_pCollider = Floor::InternalCollider::CreateInstance( this );
	if( m_pCollider != NULL )
	{
		if( m_pCollider->Initialize( normal, offset, pDebugName ) == False )
		{
			MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", Floor::FAILED_CREATE, Mix::STR_INITERROR, Mix::STR_DEBUGNAME, safeDebugName.GetConstPtr() );
			return False;
		}
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", Floor::FAILED_CREATE, Mix::STR_OUTOFMEMORY, Mix::STR_DEBUGNAME, safeDebugName.GetConstPtr() );
		return False;
	}

	return True;
}

void Floor::Attach( void )
{
	RendererObject::SetRendering( True );
}

void Floor::Detach( void )
{
	RendererObject::SetRendering( False );
}

void Floor::AttachDynamics( Mix::Dynamics::IWorld* pWorld, Mix::Dynamics::IObjectListener* pObjectListener )
{
	MIX_ASSERT( m_pCollider != NULL );

	m_pCollider->Attach( pWorld, pObjectListener );
}

void Floor::DetachDynamics( Mix::Dynamics::IWorld* pWorld )
{
	MIX_ASSERT( m_pCollider != NULL );

	m_pCollider->Detach( pWorld );
}

Mix::Dynamics::IObject* Floor::GetInternalObjectPtr( void ) const
{
	return m_pCollider->GetInternalObjectPtr();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IFloor
////////////////////////////////////////////////////////////////////////////////////////////////////

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

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

	return True;
}

Mix::Quaternion Floor::GetWorldRotation( void ) const
{
	return m_pCollider->GetWorldRotation();
}

void Floor::SetWorldRotation( const Mix::Quaternion& rot )
{
	m_pCollider->SetWorldRotation( rot );
}

Int32 Floor::GetUserIndex( void )  const
{
	return m_UserIndex;
}

void Floor::SetUserIndex( Int32 index )
{
	m_UserIndex = index;
}

void* Floor::GetUserPtr( void ) const
{
	return m_UserPtr;
}

void Floor::SetUserPtr( void* pData )
{
	m_UserPtr = pData;
}

Float32 Floor::Debug_GetDrawInterval( void ) const
{
#ifdef _DEBUG

	if( m_pCollider != NULL )
	{
		return m_pCollider->GetStaticPlanePtr()->Debug_GetDrawInterval();
	}

#endif //_DEBUG

	return 0.0f;
}

void Floor::Debug_SetDrawInterval( Float32 interval )
{
#ifdef _DEBUG

	if( m_pCollider != NULL )
	{
		m_pCollider->GetStaticPlanePtr()->Debug_SetDrawInterval( interval );
	}

#endif //_DEBUG
}

UInt32 Floor::Debug_GetDrawExtent( void ) const
{
#ifdef _DEBUG

	if( m_pCollider != NULL )
	{
		return m_pCollider->GetStaticPlanePtr()->Debug_GetDrawExtent();
	}

#endif //_DEBUG

	return 0;
}

void Floor::Debug_SetDrawExtent( UInt32 extent )
{
#ifdef _DEBUG

	if( m_pCollider != NULL )
	{
		m_pCollider->GetStaticPlanePtr()->Debug_SetDrawExtent( extent );
	}

#endif //_DEBUG
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IRendererObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Scene::IRendererObject::TYPE Floor::GetType( void ) const
{
	return Mix::Scene::IRendererObject::FLOOR;
}

Boolean Floor::IsRendering( void ) const
{
	return RendererObject::IsRendering();
}

}}}
