#include "Mix/Private/Dynamics/StaticPlane.h"

#include "Mix/Private/Dynamics/Utility.h"
#include "Mix/Private/Dynamics/ObjectContext.h"

#include "Mix/Graphics/Utility/IPerspectiveRenderer.h"

namespace Mix{ namespace Dynamics{

const wchar_t* StaticPlane::FAILED_CREATE = L"スタティックプレーンの作成に失敗";

StaticPlane* StaticPlane::CreateInstance( const Mix::Vector3& normal, float constant )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_DYNAMICS, StaticPlane, normal, constant );
}

StaticPlane::StaticPlane( const Mix::Vector3& normal, float constant ) :
Mix::Dynamics::Object( Mix::Dynamics::OF_STATIC, Mix::Dynamics::OF_STATIC_MASK, Mix::Dynamics::DD_AXIS | Mix::Dynamics::DD_WIREFRAME ),
m_pShape( NULL ),
m_pMotionState( NULL ),
m_pRigidBody( NULL ),
m_Normal( normal ),
m_Constant( constant ),
m_pContext( NULL ),
m_UserIndex( 0 ),
m_pUserPtr( NULL )
{
	m_Normal.Normalize();

	m_DebugDrawInterval = 1.0f;
	m_DebugDrawExtent = 50;

	Debug_UpdatePoints();
}

StaticPlane::~StaticPlane( void )
{
	MIX_LIB_DELETE_T( ObjectContext, m_pContext );

	MIX_LIB_DELETE( m_pRigidBody );
	MIX_LIB_DELETE( m_pMotionState );
	MIX_LIB_DELETE( m_pShape );
}

Boolean StaticPlane::Initialize( const wchar_t* pDebugName )
{
	MIX_ASSERT( m_pShape == NULL );
	MIX_ASSERT( m_pMotionState == NULL );
	MIX_ASSERT( m_pRigidBody == NULL );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// シェイプの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pShape = MIX_LIB_NEW btStaticPlaneShape( btVector3( m_Normal.x, m_Normal.y, m_Normal.z ), m_Constant );
	if( m_pShape == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s(1) : Name[%s]", StaticPlane::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// モーションステートの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pMotionState = MIX_LIB_NEW btDefaultMotionState();
	if( m_pMotionState == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s(2) : Name[%s]", StaticPlane::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リジッドボディの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pRigidBody = MIX_LIB_NEW btRigidBody( 0.0f, m_pMotionState, m_pShape );
	if( m_pRigidBody == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s(3) : Name[%s]", StaticPlane::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コンテキストの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pContext = MIX_LIB_NEW_T( Mix::Memory::SECTION_DYNAMICS, Mix::Dynamics::ObjectContext, this, pDebugName );
	if( m_pContext != NULL )
	{
		m_pRigidBody->setUserPointer( m_pContext );
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s(4) : Name[%s]", StaticPlane::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

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

	return True;
}

void StaticPlane::Debug_UpdatePoints( void )
{
	UInt32 lineCount = ( m_DebugDrawExtent + 1 );

	Float32 extent = m_DebugDrawInterval * static_cast<Float32>( m_DebugDrawExtent );
	Float32 halfExtent = extent * 0.5f;

	Mix::Vector3 xp0( -halfExtent, m_Constant, -halfExtent );
	Mix::Vector3 xp1( xp0.x, xp0.y, xp0.z + extent );

	Mix::Vector3 zp0( -halfExtent, m_Constant, -halfExtent );
	Mix::Vector3 zp1( zp0.x + extent, zp0.y, zp0.z );

	m_DebugDrawPoints.clear();
	m_DebugDrawPoints.reserve( lineCount * 4 );

	for( UInt32 i = 0; i < lineCount; i++ )
	{
		m_DebugDrawPoints.push_back( xp0 );
		m_DebugDrawPoints.push_back( xp1 );

		m_DebugDrawPoints.push_back( zp0 );
		m_DebugDrawPoints.push_back( zp1 );

		xp0.x += m_DebugDrawInterval;
		xp1.x += m_DebugDrawInterval;

		zp0.z += m_DebugDrawInterval;
		zp1.z += m_DebugDrawInterval;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Dynamics::Object
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Dynamics::ObjectContext* StaticPlane::GetContextPtr( void ) const
{
	return m_pContext;
}

btCollisionObject* StaticPlane::Bullet_GetCollisionObjectPtr( void ) const
{
	return m_pRigidBody;
}

btRigidBody* StaticPlane::Bullet_GetRigidBodyPtr( void ) const
{
	return m_pRigidBody;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Dynamics::IStaticPlane
////////////////////////////////////////////////////////////////////////////////////////////////////

const Mix::Vector3& StaticPlane::GetNormal( void ) const
{
	return m_Normal;
}

float StaticPlane::GetConstant( void ) const
{
	return m_Constant;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Dynamics::IObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Dynamics::IObject::TYPE StaticPlane::GetType( void ) const
{
	return Mix::Dynamics::IObject::STATIC_PLANE;
}

Boolean StaticPlane::GetShape( Mix::Dynamics::IShape** ppShape )
{
	return False;
}

Float32 StaticPlane::GetShapeMargin( void ) const
{
	return m_pShape->getMargin();
}

void StaticPlane::SetShapeMargin( Float32 margin )
{
	m_pShape->setMargin( margin );
}

UInt16 StaticPlane::GetFilterGroup( void ) const
{
	return Object::Bullet_GetFilterGroup();
}

void StaticPlane::SetFilterGroup( UInt16 filterGroup )
{
	Object::Bullet_SetFilterGroup( filterGroup );
}

UInt16 StaticPlane::GetFilterMask( void ) const
{
	return Object::Bullet_GetFilterMask();
}

void StaticPlane::SetFilterMask( UInt16 filterMask )
{
	Object::Bullet_SetFilterMask( filterMask );
}

const Mix::Dynamics::MATERIAL& StaticPlane::GetMaterial( void ) const
{
	return m_pContext->GetMaterial();
}

void StaticPlane::SetMaterial( const Mix::Dynamics::MATERIAL& material )
{
	m_pContext->SetMaterial( material );

	m_pRigidBody->setFriction( material.friction );
	m_pRigidBody->setRestitution( material.restitution );
}

Mix::Quaternion StaticPlane::GetWorldRotation( void ) const
{
	return ToMixQuaternion( m_pRigidBody->getWorldTransform().getRotation() );
}

Mix::Vector3 StaticPlane::GetWorldPosition( void ) const
{
	return ToMixVector3( m_pRigidBody->getWorldTransform().getOrigin() );
}

Mix::Matrix4x4 StaticPlane::GetWorldMatrix( void ) const
{
	const btTransform& tr = m_pRigidBody->getWorldTransform();

	return Mix::Matrix4x4( ToMixQuaternion( tr.getRotation() ), ToMixVector3( tr.getOrigin() ) );
}

void StaticPlane::SetWorldRotation( const Mix::Quaternion& rot )
{
	m_pRigidBody->getWorldTransform().setRotation( ToBulletQuaternion( rot ) );
}

void StaticPlane::SetWorldPosition( const Mix::Vector3& pos )
{
	m_pRigidBody->getWorldTransform().setOrigin( ToBulletVector3( pos ) );
}

void StaticPlane::SetWorldTransform( const Mix::Quaternion& rot, const Mix::Vector3& pos )
{
	m_pRigidBody->setWorldTransform( btTransform( ToBulletQuaternion( rot ), ToBulletVector3( pos ) ) );
}

Boolean StaticPlane::IsInWorld( void ) const
{
	return Object::IsInWorld();
}

Mix::Geometry::AABB StaticPlane::GetBounds( void ) const
{
	btVector3 aabbMin;
	btVector3 aabbMax;

	m_pRigidBody->getAabb( aabbMin, aabbMax );

	return Mix::Geometry::AABB( ToMixVector3( aabbMin ), ToMixVector3( aabbMax ) );
}

Boolean StaticPlane::AddListener( Mix::Dynamics::IObjectListener* pListener )
{
	return m_pContext->AddListener( pListener );
}

void StaticPlane::RemoveListener( Mix::Dynamics::IObjectListener* pListener )
{
	m_pContext->RemoveListener( pListener );
}

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

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

void* StaticPlane::GetUserPtr( void ) const
{
	return m_pUserPtr;
}

void StaticPlane::SetUserPtr( void* pData )
{
	m_pUserPtr = pData;
}

UInt32 StaticPlane::Debug_GetDrawFlags( void ) const
{
	return m_DebugDrawFlags;
}

void StaticPlane::Debug_SetDrawFlags( UInt32 flags )
{
	m_DebugDrawFlags = flags;
}

Float32 StaticPlane::Debug_GetDrawAxisScaling( void ) const
{
	return m_DebugDrawAxisScaling;
}

void StaticPlane::Debug_SetDrawAxisScaling( Float32 scaling )
{
	m_DebugDrawAxisScaling = max( 0.0f, scaling );
}

Float32 StaticPlane::Debug_GetDrawInterval( void ) const
{
	return m_DebugDrawInterval;
}

void StaticPlane::Debug_SetDrawInterval( Float32 interval )
{
	if( m_DebugDrawInterval != interval )
	{
		m_DebugDrawInterval = max( 0.01f, interval );
		Debug_UpdatePoints();
	}
}

UInt32 StaticPlane::Debug_GetDrawExtent( void ) const
{
	return m_DebugDrawExtent;
}

void StaticPlane::Debug_SetDrawExtent( UInt32 extent )
{
	if( m_DebugDrawExtent != extent )
	{
		m_DebugDrawExtent = extent;
		Debug_UpdatePoints();
	}
}

void StaticPlane::Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, Float32 opacity )
{
	Mix::Matrix4x4 oldMat = pPerspectiveRenderer->GetMatrix();
	Mix::Vector4 oldColor = pPerspectiveRenderer->GetColor();

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

	pPerspectiveRenderer->SetMatrix( GetWorldMatrix() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 軸
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( MIX_TESTBIT( m_DebugDrawFlags, Mix::Dynamics::DD_AXIS ) == Mix::Dynamics::DD_AXIS )
	{
		pPerspectiveRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, opacity ) );
		pPerspectiveRenderer->AddAxis( Debug_GetDrawAxisScaling() );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ワイヤーフレーム
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( m_DebugDrawPoints.size() > 0 ) &&
		( MIX_TESTBIT( m_DebugDrawFlags, Mix::Dynamics::DD_WIREFRAME ) == Mix::Dynamics::DD_WIREFRAME ) )
	{
		pPerspectiveRenderer->SetColor( Mix::Dynamics::Debug::GetColor( Mix::Dynamics::DDC_RIGIDBODY_REST ) );
		pPerspectiveRenderer->AddLines( &( m_DebugDrawPoints[0] ), MIX_UIT_TO_UI32( m_DebugDrawPoints.size() ) );
	}

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

	pPerspectiveRenderer->SetColor( oldColor = pPerspectiveRenderer->GetColor() );
	pPerspectiveRenderer->SetMatrix( oldMat );
}

}}
