#include "Mix/Tool/CLR/Core/Dynamics/RigidBody.h"

#include "Mix/Tool/Win32/Core/Dynamics/Factory.h"
#include "Mix/Tool/Win32/Core/Dynamics/RigidBody.h"
#include "Mix/Tool/CLR/Core/Dynamics/Shape.h"
#include "Mix/Tool/CLR/Core/Dynamics/FlagsGenerator.h"
#include "Mix/Tool/CLR/Core/Dynamics/IdentifierGenerator.h"
#include "Mix/Tool/CLR/Core/Dynamics/PhysicsMaterial.h"

namespace Mix{ namespace Tool{ namespace Dynamics{

////////////////////////////////////////////////////////////////////////////////////////////////////
// RigidBodyChild
////////////////////////////////////////////////////////////////////////////////////////////////////
	
RigidBodyChild::RigidBodyChild( Mix::Tool::Win32::Dynamics::RigidBody* _implRigidBody, HANDLE _implShapeHandle, Mix::Tool::Dynamics::Shape^ _shape ) :
implRigidBody( _implRigidBody ),
implShapeHandle( _implShapeHandle ),
m_Shape( _shape )
{
	if( _implRigidBody == NULL )
	{
		throw gcnew System::ArgumentNullException( L"_implRigidBody" );
	}

	if( _implShapeHandle == NULL )
	{
		throw gcnew System::ArgumentNullException( L"_implShapeHandle" );
	}

	if( _shape == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"_shape" );
	}
}

RigidBodyChild::~RigidBodyChild( void )
{
	if( ( implRigidBody != NULL ) &&
		( implShapeHandle != NULL ) )
	{
		implRigidBody->RemoveShape( implShapeHandle );
		implShapeHandle = NULL;
	}

	implRigidBody = NULL;
	implShapeHandle = NULL;
	m_Shape = nullptr;
}

Mix::Tool::Dynamics::Shape^ RigidBodyChild::Shape::get( void )
{
	if( implRigidBody == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	return m_Shape;
}

void RigidBodyChild::Shape::set( Mix::Tool::Dynamics::Shape^ value )
{
	if( ( implRigidBody == NULL ) ||
		( implShapeHandle == NULL ) )
	{
		throw gcnew System::InvalidOperationException();
	}

	if( value == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"Shape" );
	}

	if( implRigidBody->SetShape( implShapeHandle, value->Impl ) == true )
	{
		m_Shape = value;
	}
}

Mix::Tool::Math::Quaternion^ RigidBodyChild::Rotation::get( void )
{
	if( ( implRigidBody == NULL ) ||
		( implShapeHandle == NULL ) )
	{
		throw gcnew System::InvalidOperationException();
	}

	D3DXQUATERNION rot;

	D3DXQuaternionIdentity( &rot );
	implRigidBody->GetShapeRotation( implShapeHandle, rot );

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

void RigidBodyChild::Rotation::set( Mix::Tool::Math::Quaternion^ value )
{
	if( ( implRigidBody == NULL ) ||
		( implShapeHandle == NULL ) )
	{
		throw gcnew System::InvalidOperationException();
	}

	D3DXQUATERNION rot;

	if( value != nullptr )
	{
		rot = D3DXQUATERNION( value->X, value->Y, value->Z, value->W );
	}
	else
	{
		D3DXQuaternionIdentity( &rot );
	}

	implRigidBody->GetShapeRotation( implShapeHandle, rot );
}

Mix::Tool::Math::Vector^ RigidBodyChild::Position::get( void )
{
	if( ( implRigidBody == NULL ) ||
		( implShapeHandle == NULL ) )
	{
		throw gcnew System::InvalidOperationException();
	}

	D3DXVECTOR3 pos;

	pos = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
	implRigidBody->GetShapePosition( implShapeHandle, pos );

	return gcnew Mix::Tool::Math::Vector( pos.x, pos.y, pos.z, 1.0f );
}

void RigidBodyChild::Position::set( Mix::Tool::Math::Vector^ value )
{
	if( ( implRigidBody == NULL ) ||
		( implShapeHandle == NULL ) )
	{
		throw gcnew System::InvalidOperationException();
	}

	D3DXVECTOR3 pos;

	if( value != nullptr )
	{
		pos = D3DXVECTOR3( value->X, value->Y, value->Z );
	}
	else
	{
		pos = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
	}

	implRigidBody->GetShapePosition( implShapeHandle, pos );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// RigidBodyChildCollection
////////////////////////////////////////////////////////////////////////////////////////////////////

RigidBodyChildCollection::RigidBodyChildCollection( Mix::Tool::Win32::Dynamics::RigidBody* _impl ) :
impl( _impl ),
m_List( gcnew System::Collections::Generic::List<Mix::Tool::Dynamics::RigidBodyChild^>() )
{
}

RigidBodyChildCollection::~RigidBodyChildCollection( void )
{
	if( m_List != nullptr )
	{
		if( impl != NULL )
		{
			for each ( Mix::Tool::Dynamics::RigidBodyChild^ child in m_List )
			{
				delete child;
			}
		}

		m_List->Clear();
	}

	impl = NULL;
}

int RigidBodyChildCollection::Count::get( void )
{
	return m_List->Count;
}

Mix::Tool::Dynamics::RigidBodyChild^ RigidBodyChildCollection::default::get( int index )
{
	try
	{
		return m_List[index];
	}
	catch( System::Exception^ )
	{
		throw;
	}
}

void RigidBodyChildCollection::Clear( void )
{
	for each ( Mix::Tool::Dynamics::RigidBodyChild^ child in m_List )
	{
		delete child;
	}

	m_List->Clear();
}

Mix::Tool::Dynamics::RigidBodyChild^ RigidBodyChildCollection::Add( Mix::Tool::Dynamics::Shape^ shape, Mix::Tool::Math::Quaternion^ rot, Mix::Tool::Math::Vector^ pos )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	if( shape == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"shape" );
	}

	if( pos == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"centerPos" );
	}

	if( rot == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"centerRot" );
	}

	D3DXVECTOR3 w32Pos;
	D3DXQUATERNION w32Rot;

	if( rot != nullptr )
	{
		w32Rot.x = rot->X;
		w32Rot.y = rot->Y;
		w32Rot.z = rot->Z;
		w32Rot.w = rot->W;
	}
	else
	{
		w32Rot.x = 0.0f;
		w32Rot.y = 0.0f;
		w32Rot.z = 0.0f;
		w32Rot.w = 1.0f;
	}

	if( pos != nullptr )
	{
		w32Pos.x = pos->X;
		w32Pos.y = pos->Y;
		w32Pos.z = pos->Z;
	}
	else
	{
		w32Pos.x = 0.0f;
		w32Pos.y = 0.0f;
		w32Pos.z = 0.0f;
	}

	Mix::Tool::Dynamics::RigidBodyChild^ child = nullptr;

	HANDLE hShape = impl->AddShape( shape->Impl, w32Rot, w32Pos );
	if( hShape != NULL )
	{
		child = gcnew Mix::Tool::Dynamics::RigidBodyChild( impl, hShape, shape );
		m_List->Add( child );
	}

	return child;
}

bool RigidBodyChildCollection::Remove( Mix::Tool::Dynamics::RigidBodyChild^ child )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	if( child == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"child" );
	}

	if( m_List->Contains( child ) == true )
	{
		delete child;
	}
	else
	{
		return false;
	}

	return true;
}

bool RigidBodyChildCollection::Contains( Mix::Tool::Dynamics::RigidBodyChild^ child )
{
	return m_List->Contains( child );
}

System::Collections::IEnumerator^ RigidBodyChildCollection::GetEnumerator( void )
{
	return m_List->GetEnumerator();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// RigidBody
////////////////////////////////////////////////////////////////////////////////////////////////////

RigidBody::RigidBody( Mix::Tool::Win32::Dynamics::RigidBody* _impl ) :
impl( _impl ),
m_bInternalImpl( false ),
m_Childs( nullptr ),
m_PhysicsMaterial( nullptr )
{
	m_Childs = gcnew Mix::Tool::Dynamics::RigidBodyChildCollection( impl );
}

RigidBody::RigidBody( void ) :
impl( NULL ),
m_bInternalImpl( true ),
m_Childs( nullptr ),
m_PhysicsMaterial( nullptr )
{
	impl = Mix::Tool::Win32::Dynamics::Factory::CreateRigidBody();
	m_Childs = gcnew Mix::Tool::Dynamics::RigidBodyChildCollection( impl );
}

RigidBody::~RigidBody( void )
{
	if( m_Childs != nullptr )
	{
		delete m_Childs;
	}

	m_PhysicsMaterial = nullptr;

	if( m_bInternalImpl == true )
	{
		if( impl != NULL )
		{
			delete impl;
			impl = NULL;
		}
	}
	else
	{
		impl = NULL;
	}
}

Mix::Tool::Dynamics::RigidBodyChildCollection^ RigidBody::Childs::get( void )
{
	return m_Childs;
}

Mix::Tool::Dynamics::PhysicsMaterial^ RigidBody::PhysicsMaterial::get( void )
{
	return m_PhysicsMaterial;
}

void RigidBody::PhysicsMaterial::set( Mix::Tool::Dynamics::PhysicsMaterial^ value )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 直前の物理マテリアルのイベントハンドラーを解除
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_PhysicsMaterial != nullptr )
	{
		m_PhysicsMaterial->ValueChanged -= gcnew System::EventHandler( this, &RigidBody::PhysicsMaterial_ValueChanged );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 物理マテリアルの値を設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Tool::Win32::Dynamics::MATERIAL w32Material;

	if( value != nullptr )
	{
		w32Material.id = value->Identifier->Value;
		w32Material.attr = value->Attributes->Value;
		w32Material.friction = value->Friction;
		w32Material.restitution = value->Restitution;
	}
	else
	{
		w32Material.id = Mix::Tool::Dynamics::PhysicsMaterial::DefaultIdentifier;
		w32Material.attr = Mix::Tool::Dynamics::PhysicsMaterial::DefaultAttributes;
		w32Material.friction = Mix::Tool::Dynamics::PhysicsMaterial::DefaultFriction;
		w32Material.restitution = Mix::Tool::Dynamics::PhysicsMaterial::DefaultRestitution;
	}

	impl->SetMaterial( w32Material );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 新規の物理マテリアルのイベントハンドラーを登録
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_PhysicsMaterial = value;

	if( m_PhysicsMaterial != nullptr )
	{
		m_PhysicsMaterial->ValueChanged += gcnew System::EventHandler( this, &RigidBody::PhysicsMaterial_ValueChanged );
	}
}

void RigidBody::PhysicsMaterial_ValueChanged( System::Object^ sender, System::EventArgs^ e )
{
	if( impl != NULL )
	{
		Mix::Tool::Dynamics::PhysicsMaterial^ physicsMaterial = safe_cast<Mix::Tool::Dynamics::PhysicsMaterial^>( sender );
		Mix::Tool::Win32::Dynamics::MATERIAL w32Material;

		w32Material.id = physicsMaterial->Identifier->Value;
		w32Material.attr = physicsMaterial->Attributes->Value;
		w32Material.friction = physicsMaterial->Friction;
		w32Material.restitution = physicsMaterial->Restitution;

		impl->SetMaterial( w32Material );
	}
}


Mix::Tool::Dynamics::RigidBodyStatus RigidBody::Status::get( void )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	Mix::Tool::Dynamics::RigidBodyStatus status;

	switch( impl->GetStatus() )
	{
	case Mix::Tool::Win32::Dynamics::RigidBody::DEFAULT:
		status = Mix::Tool::Dynamics::RigidBodyStatus::Default;
		break;
	case Mix::Tool::Win32::Dynamics::RigidBody::STATIC:
		status = Mix::Tool::Dynamics::RigidBodyStatus::Static;
		break;
	case Mix::Tool::Win32::Dynamics::RigidBody::KINEMATIC:
		status = Mix::Tool::Dynamics::RigidBodyStatus::Kinematic;
		break;
	case Mix::Tool::Win32::Dynamics::RigidBody::SENSOR:
		status = Mix::Tool::Dynamics::RigidBodyStatus::Sensor;
		break;

	default:
		throw gcnew System::Exception( L"リジッドボディに無効なステータスが設定されています(get)" );
	}

	return status;
}

void RigidBody::Status::set( Mix::Tool::Dynamics::RigidBodyStatus value )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	Mix::Tool::Win32::Dynamics::RigidBody::STATUS status;

	switch( value )
	{
	case Mix::Tool::Dynamics::RigidBodyStatus::Default:
		status = Mix::Tool::Win32::Dynamics::RigidBody::DEFAULT;
		break;
	case Mix::Tool::Dynamics::RigidBodyStatus::Static:
		status = Mix::Tool::Win32::Dynamics::RigidBody::STATIC;
		break;
	case Mix::Tool::Dynamics::RigidBodyStatus::Kinematic:
		status = Mix::Tool::Win32::Dynamics::RigidBody::KINEMATIC;
		break;
	case Mix::Tool::Dynamics::RigidBodyStatus::Sensor:
		status = Mix::Tool::Win32::Dynamics::RigidBody::SENSOR;
		break;

	default:
		throw gcnew System::Exception( L"リジッドボディに無効なステータスが設定されています(set)" );
	}

	impl->SetStatus( status );
}

float RigidBody::Mass::get( void )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	return impl->GetMass();
}

void RigidBody::Mass::set( float value )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	impl->SetMass( value );
}

float RigidBody::LinearDamping::get( void )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	return impl->GetLinearDamping();
}

void RigidBody::LinearDamping::set( float value )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	impl->SetLinearDamping( value );
}

float RigidBody::AngularDamping::get( void )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	return impl->GetAngularDamping();
}

void RigidBody::AngularDamping::set( float value )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	impl->SetAngularDamping( value );
}

bool RigidBody::AlwaysActive::get( void )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	return impl->GetAlwaysActive();
}

void RigidBody::AlwaysActive::set( bool value )
{
	if( impl == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	impl->SetAlwaysActive( value );
}

Mix::Tool::Win32::Dynamics::CollisionObject* RigidBody::Impl::get( void )
{
	return impl;
}

}}}
