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

#include "Mix/Tool/CLR/Core/Utility/NamingService.h"
#include "Mix/Tool/CLR/Core/Dynamics/FlagsGenerator.h"
#include "Mix/Tool/CLR/Core/Dynamics/IdentifierGenerator.h"

namespace Mix{ namespace Tool{ namespace Dynamics{

////////////////////////////////////////////////////////////////////////////////////////////////////
// PhysicsMaterial
////////////////////////////////////////////////////////////////////////////////////////////////////

PhysicsMaterial::PhysicsMaterial(	Mix::Tool::Utility::NamingService^ namingService,
									Mix::Tool::Utility::BindingItemCollection<int>^ identList,
									Mix::Tool::Utility::BindingItemCollection<int>^ attrList ) :
m_NamingService( namingService ),
m_Name( gcnew System::String( L"" ) ),
m_ImageFilePath( gcnew System::String( L"" ) ),
m_Image( nullptr ),
m_Friction( 0.5f ),
m_Restitution( 0.0f ),
m_bIsDisposed( false )
{
	if( namingService == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"namingService" );
	}

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

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

	m_Ident = gcnew Mix::Tool::Dynamics::IdentifierGenerator( identList );
	m_Ident->ValueChanged += gcnew System::EventHandler( this, &PhysicsMaterial::IdentifierChanged );

	m_Attr = gcnew Mix::Tool::Dynamics::FlagsGenerator( attrList );
	m_Attr->ValueChanged += gcnew System::EventHandler( this, &PhysicsMaterial::AttributesChanged );
}

PhysicsMaterial::PhysicsMaterial(	Mix::Tool::Utility::NamingService^ namingService,
									Mix::Tool::Utility::BindingItemCollection<int>^ identList,
									Mix::Tool::Utility::BindingItemCollection<int>^ attrList,
									System::String^ name ) :
m_NamingService( namingService ),
m_ImageFilePath( gcnew System::String( L"" ) ),
m_Image( nullptr ),
m_Friction( 0.5f ),
m_Restitution( 0.0f ),
m_bIsDisposed( false )
{
	if( namingService == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"namingService" );
	}

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

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

	m_Name = m_NamingService->Add( ( name != nullptr )? name : L"" );

	m_Ident = gcnew Mix::Tool::Dynamics::IdentifierGenerator( identList );
	m_Ident->ValueChanged += gcnew System::EventHandler( this, &PhysicsMaterial::IdentifierChanged );

	m_Attr = gcnew Mix::Tool::Dynamics::FlagsGenerator( attrList );
	m_Attr->ValueChanged += gcnew System::EventHandler( this, &PhysicsMaterial::AttributesChanged );
}

PhysicsMaterial::~PhysicsMaterial( void )
{
	m_NamingService->Remove( m_Name );

	m_Ident->ValueChanged -= gcnew System::EventHandler( this, &PhysicsMaterial::IdentifierChanged );
	m_Attr->ValueChanged -= gcnew System::EventHandler( this, &PhysicsMaterial::AttributesChanged );

	UnloadImage();

	m_bIsDisposed = true;
}

void PhysicsMaterial::NotifyDisposing( void )
{
	Disposing( this, gcnew System::EventArgs() );
}

System::String^ PhysicsMaterial::Name::get( void )
{
	return m_Name;
}

void PhysicsMaterial::Name::set( System::String^ value )
{
	m_NamingService->Remove( m_Name );

	try
	{
		m_Name = m_NamingService->Add( ( value != nullptr )? value : gcnew System::String( L"" ) );
		NotifyPropertyChanged( gcnew System::String( L"Name" ) );
	}
	catch( System::Exception^ )
	{
		m_Name = m_NamingService->Add( m_Name );
	}
}

System::String^ PhysicsMaterial::ImageFilePath::get( void )
{
	return m_ImageFilePath;
}

void PhysicsMaterial::ImageFilePath::set( System::String^ value )
{
	if( ( value == nullptr ) ||
		( value->Length == 0 ) )
	{
		m_ImageFilePath = gcnew System::String( L"" );
		UnloadImage();
	}
	else
	{
		m_ImageFilePath = value;
		LoadImage();
	}

	NotifyPropertyChanged( gcnew System::String( L"Image" ) );
	NotifyPropertyChanged( gcnew System::String( L"ImageFilePath" ) );
}

System::Drawing::Image^ PhysicsMaterial::Image::get( void )
{
	return m_Image;
}

Mix::Tool::Dynamics::IdentifierGenerator^ PhysicsMaterial::Identifier::get( void )
{
	return m_Ident;
}

Mix::Tool::Dynamics::FlagsGenerator^ PhysicsMaterial::Attributes::get( void )
{
	return m_Attr;
}

float PhysicsMaterial::Friction::get( void )
{
	return m_Friction;
}

void PhysicsMaterial::Friction::set( float value )
{
	if( m_Friction != value )
	{
		m_Friction = MIX_CLAMP( value, 0.0f, 1.0f );
		NotifyPropertyChanged( gcnew System::String( L"Friction" ) );
		NotifyValueChanged();
	}
}

float PhysicsMaterial::Restitution::get( void )
{
	return m_Restitution;
}

void PhysicsMaterial::Restitution::set( float value )
{
	if( m_Restitution != value )
	{
		m_Restitution = MIX_CLAMP( value, 0.0f, 1.0f );
		NotifyPropertyChanged( gcnew System::String( L"Restitution" ) );
		NotifyValueChanged();
	}
}
/*
Mix::Tool::Dynamics::PhysicsMaterial^ PhysicsMaterial::Value::get( void )
{
	return this;
}
*/
bool PhysicsMaterial::IsDisposed::get( void )
{
	return m_bIsDisposed;
}

void PhysicsMaterial::LoadImage( void )
{
	UnloadImage();

	try
	{
		m_Image = System::Drawing::Image::FromFile( m_ImageFilePath );
	}
	catch( System::Exception^ )
	{
		m_Image = nullptr;
	}
}

void PhysicsMaterial::UnloadImage( void )
{
	if( m_Image != nullptr )
	{
		delete m_Image;
		m_Image = nullptr;
	}
}

void PhysicsMaterial::IdentifierChanged( System::Object^ sender, System::EventArgs^ e )
{
	NotifyValueChanged();
}

void PhysicsMaterial::AttributesChanged( System::Object^ sender, System::EventArgs^ e )
{
	NotifyValueChanged();
}

void PhysicsMaterial::NotifyValueChanged( void )
{
	ValueChanged( this, gcnew System::EventArgs() );
}

void PhysicsMaterial::NotifyPropertyChanged( System::String^ propertyName )
{
	PropertyChanged( this, gcnew System::ComponentModel::PropertyChangedEventArgs( propertyName ) );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// PhysicsMaterialCollection
////////////////////////////////////////////////////////////////////////////////////////////////////

PhysicsMaterialCollection::PhysicsMaterialCollection(	Mix::Tool::Utility::BindingItemCollection<int>^ identList,
														Mix::Tool::Utility::BindingItemCollection<int>^ attrList ) :
m_NamingService( gcnew Mix::Tool::Utility::NamingService() ),
m_IdentList( identList ),
m_AttrList( attrList ),
m_List( gcnew System::ComponentModel::BindingList<Mix::Tool::Dynamics::PhysicsMaterial^>() )
{
	if( identList == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"identList" );
	}

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

PhysicsMaterialCollection::~PhysicsMaterialCollection( void )
{
	Clear();
}

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

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

Mix::Tool::Dynamics::PhysicsMaterial^ PhysicsMaterialCollection::default::get( System::String^ name )
{
	int index = IndexOf( name );
	if( index < 0 )
	{
		return nullptr;
	}

	return m_List[index];
}

System::ComponentModel::BindingList<Mix::Tool::Dynamics::PhysicsMaterial^>^ PhysicsMaterialCollection::DataSource::get( void )
{
	return m_List;
}

Mix::Tool::Dynamics::PhysicsMaterial^ PhysicsMaterialCollection::Add( void )
{
	try
	{
		Mix::Tool::Dynamics::PhysicsMaterial^ physicsMaterial = gcnew Mix::Tool::Dynamics::PhysicsMaterial( m_NamingService,
																											m_IdentList,
																											m_AttrList );

		m_List->Add( physicsMaterial );

		return physicsMaterial;
	}
	catch( System::Exception^ )
	{
		throw;
	}
}

Mix::Tool::Dynamics::PhysicsMaterial^ PhysicsMaterialCollection::Add( System::String^ name )
{
	try
	{
		Mix::Tool::Dynamics::PhysicsMaterial^ physicsMaterial = gcnew Mix::Tool::Dynamics::PhysicsMaterial( m_NamingService,
																											m_IdentList,
																											m_AttrList,
																											name );

		m_List->Add( physicsMaterial );

		return physicsMaterial;
	}
	catch( System::Exception^ )
	{
		throw;
	}
}

bool PhysicsMaterialCollection::Remove( Mix::Tool::Dynamics::PhysicsMaterial^ physicsMaterial )
{
	if( physicsMaterial == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"physicsMaterial" );
	}

	if( m_List->Contains( physicsMaterial ) == true )
	{
		//データソースとして渡した場合のListChangedイベントのタイミングの問題で先に Disposing イベントを発生させる
		physicsMaterial->NotifyDisposing();

		if( m_List->Remove( physicsMaterial ) == true )
		{
			delete physicsMaterial;
		}
		else
		{
			return false;
		}
	}

	return true;
}

void PhysicsMaterialCollection::Clear( void )
{
	for each ( Mix::Tool::Dynamics::PhysicsMaterial^ physicsMaterial in m_List )
	{
		delete physicsMaterial;
	}

	m_List->Clear();
}

int PhysicsMaterialCollection::IndexOf( System::String^ name )
{
	int count = m_List->Count;

	for( int i = 0; i < count; i++ )
	{
		if( m_List[i]->Name->Equals( name ) == true )
		{
			return i;
		}
	}

	return -1;
}

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

System::Collections::Generic::IEnumerator<Mix::Tool::Dynamics::PhysicsMaterial^>^ PhysicsMaterialCollection::GetEnumerator_Generic( void )
{
	return m_List->GetEnumerator();
}

}}}
