#include "room.h"

#include "Mix/Dynamics.h"

const Float32 Room::EXPLOSION_RADIUS = 20.0f;

////////////////////////////////////////////////////////////////////////////////////////////////////
// Room::Exploser
////////////////////////////////////////////////////////////////////////////////////////////////////

Room::Exploser* Room::Exploser::CreateInstance( void )
{
	return MIX_NEW_T( Room::Exploser );
}

Room::Exploser::Exploser( void ) :
m_bApply( False ),
m_ApplyImpulse( 0.0f )
{
}

Room::Exploser::~Exploser( void )
{
}

void Room::Exploser::Apply( Float32 impulse )
{
	m_bApply = True;
	m_ApplyImpulse = impulse;
}

void Room::Exploser::Refresh( void )
{
	m_bApply = False;
}

void Room::Exploser::OnAddedToWorld( Mix::Dynamics::IObject* pObject, Mix::Dynamics::IWorld* pWorld )
{
}

void Room::Exploser::OnRemovedFromWorld( Mix::Dynamics::IObject* pObject, Mix::Dynamics::IWorld* pWorld )
{
}

void Room::Exploser::OnContact( Mix::Dynamics::IObject* pObject, const Mix::Dynamics::MANIFOLD& manifold )
{
	if( ( m_bApply == True ) &&
		( manifold.pObjectB->GetType() == Mix::Dynamics::IObject::RIGIDBODY ) )
	{
		Mix::Dynamics::IRigidBody* pRigidBodyB = static_cast<Mix::Dynamics::IRigidBody*>( manifold.pObjectB );

		Mix::Vector3 vec = pRigidBodyB->GetWorldPosition() - pObject->GetWorldPosition();
		Float32 len = vec.GetLength();
		Float32 ratio = MIX_CLAMP( 1.0f - MIX_FLOAT_DIV( len, Room::EXPLOSION_RADIUS ), 0.0f, 1.0f );

		pRigidBodyB->ApplyImpulse( vec.ToNormalize() * m_ApplyImpulse * ratio, manifold.points[0].localPositionB );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Room
////////////////////////////////////////////////////////////////////////////////////////////////////

Room::Room( void ) :
m_pDynamicsMgr( NULL ),
m_pWorld( NULL ),
m_pPick( NULL ),
m_pExplosionSensor( NULL ),
m_pExploser( NULL ),
m_Abyss( 0.0f )
{
}

Room::~Room( void )
{
	Dispose();
}

Boolean Room::Initialize(	Mix::Dynamics::IManager* pDynamicsMgr,
							Mix::Dynamics::IWorld* pWorld,
							Float32 abyss )
{
	MIX_ASSERT( pDynamicsMgr != NULL );
	MIX_ASSERT( pWorld != NULL );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 初期化
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_ADD_REF( pDynamicsMgr );
	m_pDynamicsMgr = pDynamicsMgr;

	MIX_ADD_REF( pWorld );
	m_pWorld = pWorld;

	m_Abyss = abyss;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 爆発用トリガーを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Dynamics::ISphereShape* pSphereShape = NULL;

	m_pExploser = Room::Exploser::CreateInstance();
	if( m_pExploser == NULL )
	{
		return False;
	}

	if( m_pDynamicsMgr->CreateSphereShape( Room::EXPLOSION_RADIUS, &pSphereShape ) == True )
	{
		if( m_pDynamicsMgr->CreateSensor( pSphereShape, &m_pExplosionSensor ) == True )
		{
			m_pExplosionSensor->SetFilterGroup( Room::GROUP_EXPLOSION );
			m_pExplosionSensor->SetFilterMask( Room::GROUP_EXPLOSION_MASK );
			m_pExplosionSensor->AddListener( m_pExploser );

			m_pWorld->AddObject( m_pExplosionSensor );
		}
	}

	MIX_RELEASE( pSphereShape );

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

	return True;
}

void Room::Dispose( void )
{
	Clear();

	if( m_pExplosionSensor != NULL )
	{
		if( m_pExploser != NULL )
		{
			m_pExplosionSensor->RemoveListener( m_pExploser );
		}

		if( m_pWorld != NULL )
		{
			m_pWorld->RemoveObject( m_pExplosionSensor );
		}
	}

	MIX_RELEASE( m_pExplosionSensor );
	MIX_RELEASE( m_pExploser );

	MIX_RELEASE( m_pWorld );
	MIX_RELEASE( m_pDynamicsMgr );
}

Mix::Dynamics::IRigidBody* Room::AddBox( const Mix::Vector3& scale, const Mix::Vector3& pos )
{
	MIX_ASSERT( m_pDynamicsMgr != NULL );
	MIX_ASSERT( m_pWorld != NULL );

	Mix::Dynamics::IBoxShape* pShape = NULL;
	Mix::Dynamics::IRigidBody* pRigidBody = NULL;

	if( m_pDynamicsMgr->CreateBoxShape( scale, &pShape ) == True )
	{
		if( m_pDynamicsMgr->CreateRigidBody( 1.0f, pShape, &pRigidBody, L"Box" ) == True )
		{
			pRigidBody->SetFilterGroup( Room::GROUP_OBJECT );
			pRigidBody->SetFilterMask( Room::GROUP_OBJECT_MASK );
			pRigidBody->SetWorldPosition( pos );
			pRigidBody->SetUserPtr( this );

			m_RigidBodyList.push_back( pRigidBody );
			m_pWorld->AddObject( pRigidBody );
		}

		MIX_RELEASE( pShape );
	}

	return pRigidBody;
}

Mix::Dynamics::IRigidBody* Room::AddSphere( Float32 radius, const Mix::Vector3& pos )
{
	MIX_ASSERT( m_pDynamicsMgr != NULL );
	MIX_ASSERT( m_pWorld != NULL );

	Mix::Dynamics::ISphereShape* pShape = NULL;
	Mix::Dynamics::IRigidBody* pRigidBody = NULL;

	if( m_pDynamicsMgr->CreateSphereShape( radius, &pShape ) == True )
	{
		if( m_pDynamicsMgr->CreateRigidBody( 1.0f, pShape, &pRigidBody, L"Sphere" ) == True )
		{
			pRigidBody->SetFilterGroup( Room::GROUP_OBJECT );
			pRigidBody->SetFilterMask( Room::GROUP_OBJECT_MASK );
			pRigidBody->SetWorldPosition( pos );
			pRigidBody->SetUserPtr( this );

			m_RigidBodyList.push_back( pRigidBody );
			m_pWorld->AddObject( pRigidBody );
		}

		MIX_RELEASE( pShape );
	}

	return pRigidBody;
}

void Room::Clear()
{
	Room::RigidBodyList::iterator it_rb_begin = m_RigidBodyList.begin();
	Room::RigidBodyList::iterator it_rb_end = m_RigidBodyList.end();
	Room::RigidBodyList::iterator it_rb;

	if( m_pPick != NULL )
	{
		m_pWorld->RemoveJoint( m_pPick );
		MIX_RELEASE( m_pPick );
	}

	for( it_rb = it_rb_begin; it_rb != it_rb_end; ++it_rb )
	{
		Mix::Dynamics::IRigidBody* pRigidBody = ( *it_rb );

		m_pWorld->RemoveObject( pRigidBody );
		MIX_RELEASE( pRigidBody );
	}

	m_RigidBodyList.clear();
}

void Room::MousePressed( const Mix::Vector3& eyePos, const Mix::Matrix4x4& viewMat, const Mix::Matrix4x4& projMat, const Mix::Vector2& mousePos, const Mix::Vector2& screenSize )
{
	Mix::Vector3 from;
	Mix::Vector3 to;
	Mix::Dynamics::IWorld::TEST_RESULT result;

	if( m_pPick != NULL )
	{
		m_pWorld->RemoveJoint( m_pPick );
		MIX_RELEASE( m_pPick );
	}

	from = Mix::Unproject( viewMat, projMat, Mix::Vector3( mousePos.x, mousePos.y, 0.0f ), screenSize );
	to = Mix::Unproject( viewMat, projMat, Mix::Vector3( mousePos.x, mousePos.y, 1.0f ), screenSize );

	if( m_pWorld->TestRay( from, to, Room::GROUP_PICK, Room::GROUP_PICK_MASK, NULL, result ) == True )
	{
		if( ( result.pObject->GetUserPtr() == this ) &&
			( result.pObject->GetType() == Mix::Dynamics::IObject::RIGIDBODY ) )
		{
			Mix::Dynamics::IRigidBody* pRigidBody = static_cast<Mix::Dynamics::IRigidBody*>( result.pObject );
			Mix::Vector3 pivotA = ( result.pObject->GetWorldMatrix().ToInverse() * result.worldPos );

			if( m_pDynamicsMgr->CreatePointJoint( pRigidBody, pivotA, &m_pPick ) == True )
			{
				m_pPick->SetPivotSpring( 0.9f );
				m_pPick->SetPivotDamper( 0.3f );
				m_pPick->Debug_SetDrawLimitScaling( 2.0f );

				m_pWorld->AddJoint( m_pPick );

				m_pPickRigidBody = pRigidBody;
				m_pPickRigidBody->SetAlwaysActive( True );

				m_PrePickDist = ( result.worldPos - eyePos ).GetLength();
			}
		}
	}
}

void Room::MouseDown( const Mix::Vector3& eyePos, const Mix::Matrix4x4& viewMat, const Mix::Matrix4x4& projMat, const Mix::Vector2& mousePos, const Mix::Vector2& screenSize )
{
	if( m_pPick != NULL )
	{
		Mix::Vector3 to;
		Mix::Vector3 from;
		Mix::Vector3 dir;
		Mix::Vector3 pivotB;

		to = Mix::Unproject( viewMat, projMat, Mix::Vector3( mousePos.x, mousePos.y, 1.0f ), screenSize );

		from = eyePos;
		dir = ( to - from ).ToNormalize() * m_PrePickDist;
		pivotB = ( from + dir );

		m_pPick->SetPivotB( pivotB );
	}
}

void Room::MouseReleased( void )
{
	if( m_pPick != NULL )
	{
		m_pWorld->RemoveJoint( m_pPick );
		MIX_RELEASE( m_pPick );

		m_pPickRigidBody->SetAlwaysActive( False );
		m_pPickRigidBody = NULL;
	}
}

UInt32 Room::GetObjectCount( void ) const
{
	return MIX_UIT_TO_UI32( m_RigidBodyList.size() );
}

void Room::Explosion( const Mix::Vector3& pos, Float32 impulse )
{
	if( m_pExploser != NULL )
	{
		m_pExplosionSensor->SetWorldPosition( pos );
		m_pExploser->Apply( impulse );
	}
}

void Room::Refresh( void )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ワールド更新後のモデルの更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Room::RigidBodyList::iterator it_rb = m_RigidBodyList.begin();

	while( it_rb != m_RigidBodyList.end() )
	{
		Mix::Dynamics::IRigidBody* pRigidBody = ( *it_rb );

		if( m_Abyss > pRigidBody->GetWorldPosition().y )
		{
			m_pWorld->RemoveObject( pRigidBody );
			MIX_RELEASE( pRigidBody );
			
			it_rb = m_RigidBodyList.erase( it_rb );
		}
		else
		{
			it_rb++;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ワールド更新後のモデルの更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pExploser->Refresh();

}
