#include "Mix/Tool/CLR/Core/Dynamics/Design/Terrain.h"

#include "Mix/Tool/Win32/Core/Dynamics/Design/Terrain.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{ namespace Design{

////////////////////////////////////////////////////////////////////////////////////////////////////
// TerrainPart
////////////////////////////////////////////////////////////////////////////////////////////////////

TerrainPart::TerrainPart(	Mix::Tool::Win32::Dynamics::Design::Terrain* _implTerrain,
							int index,
							const wchar_t* pName,
							const D3DXVECTOR4& color,
							const wchar_t* pImageFilePath ) :
implTerrain( _implTerrain ),
m_Index( index ),
m_Name( gcnew System::String( ( pName != NULL )? pName : L"" ) ),
m_Color( nullptr ),
m_Image( nullptr ),
m_PhysicsMaterial( nullptr )
{
	m_Color = gcnew Mix::Tool::Math::Vector( color.x, color.y, color.z, color.w );

	if( ( pImageFilePath != NULL ) &&
		( ::wcslen( pImageFilePath ) > 0 ) )
	{
		try
		{
			m_Image = System::Drawing::Image::FromFile( gcnew System::String( pImageFilePath ) );
		}
		catch( System::Exception^ )
		{
			m_Image = nullptr;
		}
	}
}

TerrainPart::~TerrainPart( void )
{
	RemovePhysicsMaterialEvents();

	if( m_Image != nullptr )
	{
		delete m_Image;
		m_Image = nullptr;
	}

	m_PhysicsMaterial = nullptr;

	implTerrain = NULL;
}

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

Mix::Tool::Math::Vector^ TerrainPart::Color::get( void )
{
	return m_Color;
}

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

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

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

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

	RemovePhysicsMaterialEvents();

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

	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;
	}

	if( implTerrain->Part_SetMaterial( m_Index, w32Material ) == false )
	{
		throw gcnew System::Exception( L"Part_SetMaterial がエラーを返しました" );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 新規の物理マテリアルの設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_PhysicsMaterial = value;

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

void TerrainPart::RemovePhysicsMaterialEvents( void )
{
	if( m_PhysicsMaterial != nullptr )
	{
		m_PhysicsMaterial->ValueChanged -= gcnew System::EventHandler( this, &TerrainPart::PhysicsMaterial_ValueChanged );
		m_PhysicsMaterial->Disposing -= gcnew System::EventHandler( this, &TerrainPart::PhysicsMaterial_Disposing );
	}
}

void TerrainPart::PhysicsMaterial_ValueChanged( System::Object^ sender, System::EventArgs^ e )
{
	if( implTerrain != 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;

		if( implTerrain->Part_SetMaterial( m_Index, w32Material ) == false )
		{
			throw gcnew System::Exception( L"Part_SetMaterial がエラーを返しました" );
		}
	}
}

void TerrainPart::PhysicsMaterial_Disposing( System::Object^ sender, System::EventArgs^ e )
{
	RemovePhysicsMaterialEvents();
	PhysicsMaterialDiscarded( this, gcnew System::EventArgs() );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// TerrainPartCollection
////////////////////////////////////////////////////////////////////////////////////////////////////

TerrainPartCollection::TerrainPartCollection( void ) :
m_List( gcnew System::Collections::Generic::List<Mix::Tool::Dynamics::Design::TerrainPart^>() )
{
}

TerrainPartCollection::~TerrainPartCollection( void )
{
	if( m_List->Count > 0 )
	{
		for each ( Mix::Tool::Dynamics::Design::TerrainPart^ part in m_List )
		{
			delete part;
		}

		m_List->Clear();
	}
}

void TerrainPartCollection::Add(	Mix::Tool::Win32::Dynamics::Design::Terrain* implTerrain,
									int index,
									const wchar_t* pName,
									const D3DXVECTOR4& color,
									const wchar_t* pImageFilePath )
{
	m_List->Add( gcnew Mix::Tool::Dynamics::Design::TerrainPart( implTerrain, index, pName, color, pImageFilePath ) );
}

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

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

int TerrainPartCollection::IndexOfName( System::String^ name )
{
	if( name == nullptr )
	{
		throw gcnew System::ArgumentNullException( L"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^ TerrainPartCollection::GetEnumerator( void )
{
	return m_List->GetEnumerator();
}

System::Collections::IList^ TerrainPartCollection::DataSource::get( void )
{
	return m_List;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Terrain
////////////////////////////////////////////////////////////////////////////////////////////////////

Terrain::Terrain( Mix::Tool::Win32::Dynamics::Design::Terrain* _implTerrain ) :
implTerrain( _implTerrain ),
m_StatusLock( gcnew System::Object() ),
m_Status( Mix::Tool::Dynamics::Design::TerrainStatus::Destroyed ),
m_FilePath( System::String::Empty ),
m_Parts( gcnew Mix::Tool::Dynamics::Design::TerrainPartCollection() )
{
	if( _implTerrain == NULL )
	{
		throw gcnew System::ArgumentNullException( L"_implTerrain" );
	}
}

Terrain::~Terrain( void )
{
	if( m_Parts != nullptr )
	{
		delete m_Parts;
	}

	Status = Mix::Tool::Dynamics::Design::TerrainStatus::Destroyed;

	implTerrain = NULL;
}

Mix::Tool::Dynamics::Design::TerrainStatus Terrain::Status::get( void )
{
	Mix::Tool::Dynamics::Design::TerrainStatus status;

	System::Threading::Monitor::Enter( m_StatusLock );
	status = m_Status;
	System::Threading::Monitor::Exit( m_StatusLock );

	return status;
}

void Terrain::Status::set( Mix::Tool::Dynamics::Design::TerrainStatus value )
{
	System::Threading::Monitor::Enter( m_StatusLock );
	m_Status = value;
	System::Threading::Monitor::Exit( m_StatusLock );
}

System::String^ Terrain::FilePath::get( void )
{
	return m_FilePath;
}

Mix::Tool::Dynamics::Design::TerrainPartCollection^ Terrain::Parts::get( void )
{
	return m_Parts;
}

void Terrain::Initialize( void )
{
	if( Status != Mix::Tool::Dynamics::Design::TerrainStatus::Destroyed )
	{
		throw gcnew System::Exception( "破棄されていません" );
	}

	Status = Mix::Tool::Dynamics::Design::TerrainStatus::Importing;
}

void Terrain::Import( System::String^ filePath, int serializeCapacity )
{
	if( implTerrain == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

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

	if( filePath->Length == 0 )
	{
		throw gcnew System::ArgumentException( L"ファイルパスの長さが 0 です", L"filePath" );
	}

	if( Status != Mix::Tool::Dynamics::Design::TerrainStatus::Importing )
	{
		throw gcnew System::Exception( "インポートが初期化されていません" );
	}

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

	pin_ptr<const wchar_t> wpFilePath = PtrToStringChars( filePath );

	m_FilePath = filePath; //とりあえず設定しておく( 失敗した場合は Empty )

	if( implTerrain->Import( wpFilePath, serializeCapacity ) == false )
	{
		m_FilePath = System::String::Empty;
		throw gcnew System::Exception( L"マップコリジョンのインポートに失敗しました" );
	}
}

void Terrain::ImportFinalize( void )
{
	if( implTerrain == NULL )
	{
		m_FilePath = System::String::Empty;
		throw gcnew System::InvalidOperationException();
	}

	if( implTerrain->ImportFinalize() == true )
	{
		int partNum = implTerrain->GetPartNum();

		for( int i = 0; i < partNum; i++ )
		{
			const Mix::Tool::Win32::Dynamics::Design::Terrain::PART* pPart = implTerrain->Part_Get( i );
			m_Parts->Add( implTerrain, i, pPart->name.c_str(), pPart->color, pPart->imageFilePath.c_str() );
		}

		Status = Mix::Tool::Dynamics::Design::TerrainStatus::Available;
	}
	else
	{
		m_FilePath = System::String::Empty;
		Status = Mix::Tool::Dynamics::Design::TerrainStatus::Destroyed;
		throw gcnew System::Exception( L"マップコリジョンのインポートのファイナライズに失敗しました" );
	}
}

void Terrain::Destroy( void )
{
	if( implTerrain == NULL )
	{
		throw gcnew System::InvalidOperationException();
	}

	if( Status == Mix::Tool::Dynamics::Design::TerrainStatus::Importing )
	{
		throw gcnew System::Exception( "インポート中は破棄できません" );
	}

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

	if( m_Parts != nullptr )
	{
		delete m_Parts;
	}

	implTerrain->Destroy();

	Status = Mix::Tool::Dynamics::Design::TerrainStatus::Destroyed;
}

}}}}
