#include "Mix/Tool/Win32/Core/Graphics/MaterialSlot.h"

#include <shlwapi.h>

#include "Mix/Tool/Win32/Core/Graphics/Manager.h"
#include "Mix/Tool/Win32/Core/Graphics/DrawObject.h"
#include "Mix/Tool/Win32/Core/Graphics/Material.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Graphics{

const wchar_t* MaterialSlot::ADD_NAME = L"Default";
const unsigned int MaterialSlot::MAX_ADD_NUMBER = 999;

MaterialSlot::MaterialSlot( Mix::Tool::Win32::Graphics::DrawObject* pDrawObject, unsigned int index, const wchar_t* pName ) :
m_pDrawObject( pDrawObject ),
m_Index( index ),
m_DefaultName( pName ),
m_Name( pName ),
m_pDefaultMaterial( NULL ),
m_pActiveMaterial( NULL ),
m_Flags( 0 )
{
}

MaterialSlot::~MaterialSlot( void )
{
	for( std::vector<Mix::Tool::Win32::Graphics::Material*>::iterator it = m_List.begin(); it != m_List.end(); ++it )
	{
		( *it )->Destroy();
		MIX_DELETE( ( *it ) );
	}
}

void MaterialSlot::Destroy( void )
{
	m_pDrawObject = NULL;
}

const wchar_t* MaterialSlot::GetDefaultName( void ) const
{
	return m_DefaultName.c_str();
}

void MaterialSlot::SetName( const wchar_t* pName )
{
	if( ( m_pDrawObject == NULL ) ||
		( pName == NULL ) ||
		( ::wcslen( pName ) == 0 ) )
	{
		return;
	}

	m_pDrawObject->CreateMaterialSlotName( this, pName, m_Name );
}

const wchar_t* MaterialSlot::GetName( void ) const
{
	return m_Name.c_str();
}

Mix::Tool::Win32::Graphics::Material* MaterialSlot::Add( void )
{
	Mix::Tool::Win32::Graphics::Material* pMaterial = NULL;

	pMaterial = new Mix::Tool::Win32::Graphics::Material( this, ( m_List.size() == 0 ) );

	if( pMaterial != NULL )
	{
		pMaterial->SetName( MaterialSlot::ADD_NAME );
		pMaterial->SetDefaultName( pMaterial->GetName() );
		pMaterial->SetSaveFilePath( NULL );

		if( pMaterial->GetDefault() == true )
		{
			m_pDefaultMaterial = m_pActiveMaterial = pMaterial;
			m_pDefaultMaterial->OnActivate( true );
		}

		m_List.push_back( pMaterial );
	}
	else
	{
		return NULL;
	}

	return pMaterial;
}

void MaterialSlot::Remove( const wchar_t* pName )
{
	if( m_List.size() == 1 )
	{
		return;
	}

	Mix::Tool::Win32::Graphics::Material* pMaterial;
	std::vector<Mix::Tool::Win32::Graphics::Material*>::const_iterator it;
	std::vector<Mix::Tool::Win32::Graphics::Material*>::const_iterator it_next;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// マテリアルを取得
	////////////////////////////////////////////////////////////////////////////////////////////////////

	it = GetIterator( pName );

	if( it == m_List.end() )
	{
		return;
	}

	pMaterial = ( *it );

	MIX_ASSERT( m_pDefaultMaterial != pMaterial );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// リストから削除する
	////////////////////////////////////////////////////////////////////////////////////////////////////

	it_next = m_List.erase( it );

	if( m_pActiveMaterial == pMaterial )
	{
		if( it_next == m_List.end() )
		{
			it_next = it_next - 1;
		}

		m_pActiveMaterial = ( *it_next );
		m_pActiveMaterial->OnActivate( true );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 解放
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_DELETE( pMaterial );
}

Mix::Tool::Win32::Graphics::Material* MaterialSlot::GetDefault( void ) const
{
	return m_pDefaultMaterial;
}

Mix::Tool::Win32::Graphics::Material* MaterialSlot::GetActive( void ) const
{
	return m_pActiveMaterial;
}

Mix::Tool::Win32::Graphics::Material* MaterialSlot::GetByIndex( unsigned int index ) const
{
	if( m_List.size() <= index )
	{
		return NULL;
	}

	return m_List[index];
}

Mix::Tool::Win32::Graphics::Material* MaterialSlot::GetByName( const wchar_t* pName ) const
{
	std::vector<Mix::Tool::Win32::Graphics::Material*>::const_iterator it = GetIterator( pName );
	if( it == m_List.end() )
	{
		return NULL;
	}

	return ( *it );
}

unsigned int MaterialSlot::GetCount( void ) const
{
	return m_List.size();
}

void MaterialSlot::Update( Mix::Tool::Win32::Graphics::Material* pMaterial, bool bNotifyDrawObject )
{
	unsigned int oldFlags = m_Flags;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コンパイルフラグを更新( 入力シグネチャに関するものだけ )
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_Flags = 0;

	/*
		法線、接線空間、テクスチャ座標
	*/

	for( std::vector<Mix::Tool::Win32::Graphics::Material*>::iterator it = m_List.begin(); it != m_List.end(); ++it )
	{
		Mix::Tool::Win32::Graphics::Material* pTemp = ( *it );

		//法線
		if( pTemp->GetShaderType() != Mix::Tool::Win32::Graphics::SHADER_SHADELESS )
		{
			MIX_SET_BIT( m_Flags, MCF_NORMAL );
		}

		//接線空間
		if( pTemp->GetBumpTexture() != NULL )
		{
			MIX_SET_BIT( m_Flags, MCF_TANGENT_SPACE );
		}

		//テクスチャ座標
		if( ( pTemp->GetDiffuseTexture() != NULL ) ||
			( pTemp->GetSpecularTexture() != NULL ) ||
			( pTemp->GetEmissiveTexture() != NULL ) ||
			( pTemp->GetBumpTexture() != NULL ) )
		{
			MIX_SET_BIT( m_Flags, MCF_TEXTURE );
		}
	}

	/*
		スキニングの有無にかかわらず、マテリアルを共有することができるため、両方用意する
	*/

	MIX_SET_BIT( m_Flags, MCF_SIMPLE_VERTEX );
	MIX_SET_BIT( m_Flags, MCF_BLEND_VERTEX );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// スロット内のマテリアルに更新を通知
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( ( pMaterial == NULL ) ||
		( m_Flags != oldFlags ) )
	{
		//前回と入力シグネチャが異なっていた場合は、すべてのマテリアルに通知
		for( std::vector<Mix::Tool::Win32::Graphics::Material*>::iterator it = m_List.begin(); it != m_List.end(); ++it )
		{
			( *it )->OnUpdateShader( m_Flags );
		}

		//描画オブジェクトに通知
		if( ( m_pDrawObject != NULL ) &&
			( bNotifyDrawObject == true ) )
		{
			m_pDrawObject->OnRefreshMesh( m_Index );
		}
	}
	else
	{
		//前回と入力シグネチャが同じなので、更新しようとしているマテリアルにだけ通知
		pMaterial->OnUpdateShader( m_Flags );
	}
}

Mix::Tool::Win32::Object::TYPE MaterialSlot::GetType( void ) const
{
	return Mix::Tool::Win32::Object::GRAPHICS__MATERIAL_SLOT;
}

std::vector<Mix::Tool::Win32::Graphics::Material*>::const_iterator MaterialSlot::GetIterator( const wchar_t* pName ) const
{
	if( ( pName == NULL ) ||
		( ::wcslen( pName ) == 0 ) )
	{
		return m_List.end();
	}

	for( std::vector<Mix::Tool::Win32::Graphics::Material*>::const_iterator it = m_List.begin(); it != m_List.end(); ++it )
	{
		if( ::wcscmp( ( *it )->GetName(), pName ) == 0 )
		{
			return it;
		}
	}

	return m_List.end();
}

bool MaterialSlot::CreateMaterialName( Mix::Tool::Win32::Graphics::Material* pMaterial, const wchar_t* pRequestName, std::wstring& name )
{
	return CreateUniqueName( pMaterial, m_List.begin(), m_List.end(), pRequestName, name );
}

void MaterialSlot::SetActiveMaterial( Mix::Tool::Win32::Graphics::Material* pMaterial )
{
	MIX_ASSERT( GetIterator( pMaterial->GetName() ) != m_List.end() );
	MIX_ASSERT( m_pActiveMaterial != NULL );

	if( m_pActiveMaterial != pMaterial )
	{
		//直前のものを非アクティブにする
		m_pActiveMaterial->OnActivate( false );

		//新しいものをアクティブにする
		m_pActiveMaterial = pMaterial;
		m_pActiveMaterial->OnActivate( true );
	}
}

bool MaterialSlot::WriteDefault( const wchar_t* pFilePath, Mix::Tool::Win32::File::OutputStream& output )
{
	MIX_ASSERT( m_pDefaultMaterial != NULL );

	wchar_t name[Mix::Tool::Win32::Graphics::MAX_NAME_SIZE];

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 名前を書き込む
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_Name.size() < Mix::Tool::Win32::Graphics::_MAX_NAME_SIZE )
	{
		::ZeroMemory( name, sizeof( name ) );
		::wcscpy_s( name, sizeof( name ) >> 1, m_Name.c_str() );
	}
	else
	{
		LogPrint(	LT_ERROR, L"マテリアルの保存に失敗 : 名前は%d文字以下にしてください : SlotName[%s] FilePath[%s]",
					_MAX_NAME_SIZE,
					m_Name.c_str(),
					pFilePath );

		return false;
	}

	if( output.Write( name, sizeof( name ) ) == false )
	{
		LogPrint(	LT_ERROR, L"マテリアルの保存に失敗 : 書き込み中にエラーが発生しました : SlotName[%s] FilePath[%s]",
					m_Name.c_str(),
					pFilePath );

		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// デフォルトのマテリアルを書き込む
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_pDefaultMaterial->Write( pFilePath, output ) == false )
	{
		return false;
	}

	return true;
}

bool MaterialSlot::SaveFile( void )
{
	std::vector<Mix::Tool::Win32::Graphics::Material*>::iterator it_begin = m_List.begin();
	std::vector<Mix::Tool::Win32::Graphics::Material*>::iterator it_end = m_List.end();
	std::vector<Mix::Tool::Win32::Graphics::Material*>::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		Mix::Tool::Win32::Graphics::Material* pMaterial = ( *it );

		if( pMaterial->GetDefault() == true )
		{
			continue;
		}

		if( pMaterial->Save() == false )
		{
			return false;
		}
	}

	return true;
}

}}}}
