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

#include "Mix/Dynamics/IShape.h"

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

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

namespace Mix{ namespace Dynamics{

const wchar_t* Sensor::FAILED_CREATE = L"センサーの作成に失敗";

Sensor* Sensor::CreateInstance( Mix::Dynamics::IShape* pShape )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_DYNAMICS, Sensor, pShape );
}

Sensor::Sensor( Mix::Dynamics::IShape* pShape ) :
Mix::Dynamics::Object( Mix::Dynamics::OF_SENSOR, Mix::Dynamics::OF_SENSOR_MASK, Mix::Dynamics::DD_WIREFRAME ),
m_pShape( pShape ),
m_pContext( NULL ),
m_pMotionState( NULL ),
m_pObject( NULL ),
m_UserIndex( 0 ),
m_pUserPtr( NULL )
{
	MIX_ADD_REF( m_pShape );
}

Sensor::~Sensor( void )
{
	MIX_LIB_DELETE( m_pObject );
	MIX_LIB_DELETE( m_pMotionState );

	MIX_LIB_DELETE_T( ObjectContext, m_pContext );
	MIX_RELEASE( m_pShape );
}

Boolean Sensor::Initialize( const wchar_t* pDebugName )
{
	btCollisionShape* pCollisionShape = dynamic_cast<Mix::Dynamics::Shape*>( m_pShape )->Bullet_GetCollisionShapePtr();

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

	m_pContext = MIX_LIB_NEW_T( Mix::Memory::SECTION_DYNAMICS, Mix::Dynamics::ObjectContext, this, pDebugName );
	if( m_pContext == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", Sensor::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

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

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

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

	m_pObject = MIX_LIB_NEW btRigidBody( btRigidBody::btRigidBodyConstructionInfo( 0.0f, m_pMotionState, pCollisionShape ) );
	if( m_pObject != NULL )
	{
		Int32 collFlags = m_pObject->getCollisionFlags();

		//衝突応答を行わない
		MIX_SETBIT( collFlags, btCollisionObject::CF_NO_CONTACT_RESPONSE );

		m_pObject->setCollisionFlags( collFlags );
		m_pObject->setUserPointer( m_pContext );
	}
	else
	{
		MIX_LOG_ERROR( L"%s : %s : DebugName[%s]", Sensor::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pDebugName );
		return False;
	}

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

	return True;
}

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

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

btCollisionObject* Sensor::Bullet_GetCollisionObjectPtr( void ) const
{
	return m_pObject;
}

btRigidBody* Sensor::Bullet_GetRigidBodyPtr( void ) const
{
	return m_pObject;
}

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

Mix::Dynamics::IObject::TYPE Sensor::GetType( void ) const
{
	return Mix::Dynamics::IObject::SENSOR;
}

Boolean Sensor::GetShape( Mix::Dynamics::IShape** ppShape )
{
	MIX_ADD_REF( m_pShape );
	( *ppShape ) = m_pShape;

	return True;
}

Float32 Sensor::GetShapeMargin( void ) const
{
	return m_pShape->GetMargin();
}

void Sensor::SetShapeMargin( Float32 margin )
{
	m_pShape->SetMargin( margin );
}

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

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

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

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

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

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

Mix::Quaternion Sensor::GetWorldRotation( void ) const
{
	const btQuaternion& rot = m_pObject->getWorldTransform().getRotation();

	return Mix::Quaternion( rot.x(), rot.y(), rot.z(), rot.w() );
}

Mix::Vector3 Sensor::GetWorldPosition( void ) const
{
	const btVector3& pos = m_pObject->getWorldTransform().getOrigin();

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

Mix::Matrix4x4 Sensor::GetWorldMatrix( void ) const
{
	const btTransform& tr = m_pObject->getWorldTransform();
	btQuaternion rot = tr.getRotation();
	const btVector3& pos = tr.getOrigin();

	Mix::Matrix4x4 mat( Mix::Quaternion( rot.x(), rot.y(), rot.z(), rot.w() ) );

	mat.m30 = pos.x();
	mat.m31 = pos.y();
	mat.m32 = pos.z();
	mat.m33 = 1.0f;

	return mat;
}

void Sensor::SetWorldRotation( const Mix::Quaternion& rot )
{
	btTransform tr = m_pObject->getWorldTransform();

	tr.setRotation( btQuaternion( rot.x, rot.y, rot.z, rot.w ) );
	m_pObject->setWorldTransform( tr );
}

void Sensor::SetWorldPosition( const Mix::Vector3& pos )
{
	btTransform tr = m_pObject->getWorldTransform();

	tr.setOrigin( btVector3( pos.x, pos.y, pos.z ) );
	m_pObject->setWorldTransform( tr );
}

void Sensor::SetWorldTransform( const Mix::Quaternion& rot, const Mix::Vector3& pos )
{
	btTransform tr;

	tr.setRotation( btQuaternion( rot.x, rot.y, rot.z, rot.w ) );
	tr.setOrigin( btVector3( pos.x, pos.y, pos.z ) );

	m_pObject->setWorldTransform( tr );
}

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

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

	m_pObject->getAabb( aabbMin, aabbMax );

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

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

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

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

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

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

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

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

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

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

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

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

	pPerspectiveRenderer->SetMatrix( GetWorldMatrix() );
	pPerspectiveRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );

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

	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( m_DebugDrawAxisScaling );
	}

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

	if( MIX_TESTBIT( m_DebugDrawFlags, Mix::Dynamics::DD_WIREFRAME ) == Mix::Dynamics::DD_WIREFRAME )
	{
		pPerspectiveRenderer->SetColor( Mix::Dynamics::Debug::GetColor( Mix::Dynamics::DDC_SENSOR, opacity ) );
		m_pShape->DebugDraw( pPerspectiveRenderer );
	}

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

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

}}
