#include "Mix/Private/Scene/Standard/DX11/DefaultMaterial.h"

#include "Mix/Graphics/IManager.h"
#include "Mix/Graphics/IDevice.h"
#include "Mix/Graphics/IShaderConstant.h"

namespace Mix{ namespace Scene{ namespace Standard{ namespace DX11{

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::Standard::DX11::DefaultMaterial
////////////////////////////////////////////////////////////////////////////////////////////////////

DefaultMaterial* DefaultMaterial::CreateInstance(	Mix::Graphics::IDevice* pDevice,
													Mix::Scene::Common::EffectPackage* pEffectPackage,
													Mix::IO::IReader* pReader,
													Boolean bWithMagicNumber,
													const wchar_t* pNameLabel,
													const wchar_t* pName )
{
	Mix::Scene::Standard::DX11::DefaultMaterial* pMaterial;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インターフェースの作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pMaterial = MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, Mix::Scene::Standard::DX11::DefaultMaterial );
	if( pMaterial == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", DefaultMaterial::FAILED_CREATE, Mix::STR_OUTOFMEMORY, pNameLabel, pName );
		return NULL;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// インターフェースの初期化
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( pMaterial->Initialize( pName, pDevice ) == False )
	{
		MIX_RELEASE( pMaterial );
		return NULL;
	}

	if( Mix::Scene::Standard::Common::DefaultMaterial::Initialize(	pDevice,
																	pEffectPackage,
																	pReader,
																	bWithMagicNumber,
																	pNameLabel,
																	pName,
																	pMaterial ) == False )
	{
		MIX_RELEASE( pMaterial );
		return NULL;
	}

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

	return pMaterial;
}

DefaultMaterial::DefaultMaterial( void ) :
m_pConstantPS( NULL ),
m_bValidConstantPS( True )
{
}

DefaultMaterial::~DefaultMaterial( void )
{
	MIX_RELEASE( m_pConstantPS );
}

Boolean DefaultMaterial::Initialize( const wchar_t* pName, Mix::Graphics::IDevice* pDevice )
{
	MIX_ASSERT( m_pConstantPS == NULL );

	return pDevice->CreateShaderConstant( sizeof( DefaultMaterial::PS ), True, NULL, &m_pConstantPS, pName );
}

void DefaultMaterial::UpdateConstantPS( void )
{
	MIX_ASSERT( m_pConstantPS != NULL );

	if( ( m_bValidConstantPS == True ) &&
		( m_pConstantPS->Lock() == True ) )
	{
		m_pConstantPS->Write( &m_PS, sizeof( m_PS ) );
		m_pConstantPS->Unlock();
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IDefaultMaterial
////////////////////////////////////////////////////////////////////////////////////////////////////

const Mix::Vector4& DefaultMaterial::GetAmbientColor( void ) const
{
	return m_PS.ambientColor;
}

void DefaultMaterial::SetAmbientColor( const Mix::Vector4& color )
{
	m_PS.ambientColor = color;
	UpdateConstantPS();
}

const Mix::Vector4& DefaultMaterial::GetDiffuseColor( void ) const
{
	return m_PS.diffuseColor;
}

void DefaultMaterial::SetDiffuseColor( const Mix::Vector4& color )
{
	m_PS.diffuseColor = color;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetDiffuseFresnelIntensity( void ) const
{
	return m_PS.diffuseParam.data[DefaultMaterial::DIFFUSE_PI_FI];
}

void DefaultMaterial::SetDiffuseFresnelIntensity( Float32 intensity )
{
	m_PS.diffuseParam.data[DefaultMaterial::DIFFUSE_PI_FI] = intensity;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetDiffuseRLHardness( void ) const
{
	return m_PS.diffuseParam.data[DefaultMaterial::DIFFUSE_PI_RL_H];
}

void DefaultMaterial::SetDiffuseRLHardness( Float32 hardness )
{
	m_PS.diffuseParam.data[DefaultMaterial::DIFFUSE_PI_RL_H] = hardness;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetDiffuseRLScale( void ) const
{
	return m_PS.diffuseParam.data[DefaultMaterial::DIFFUSE_PI_RL_S];
}

void DefaultMaterial::SetDiffuseRLScale( Float32 scale )
{
	m_PS.diffuseParam.data[DefaultMaterial::DIFFUSE_PI_RL_S] = scale;
	UpdateConstantPS();
}

const Mix::Vector4& DefaultMaterial::GetSpecularColor( void ) const
{
	return m_PS.specularColor;
}

void DefaultMaterial::SetSpecularColor( const Mix::Vector4& color )
{
	m_PS.specularColor = color;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetSpecularHardness( void ) const
{
	return m_PS.specularParam0.data[DefaultMaterial::SPECULAR_PI0_H];
}

void DefaultMaterial::SetSpecularHardness( Float32 hardness )
{
	m_PS.specularParam0.data[DefaultMaterial::SPECULAR_PI0_H] = hardness;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetSpecularScale( void ) const
{
	return m_PS.specularParam0.data[DefaultMaterial::SPECULAR_PI0_S];
}

void DefaultMaterial::SetSpecularScale( Float32 scale )
{
	m_PS.specularParam0.data[DefaultMaterial::SPECULAR_PI0_S] = scale;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetSpecularCTFresnel( void ) const
{
	return m_PS.specularParam1.data[DefaultMaterial::SPECULAR_PI1_CT_F];
}

void DefaultMaterial::SetSpecularCTFresnel( Float32 fresnel )
{
	m_PS.specularParam1.data[DefaultMaterial::SPECULAR_PI1_CT_F] = fresnel;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetSpecularCTRoughness( void ) const
{
	return m_PS.specularParam1.data[DefaultMaterial::SPECULAR_PI1_CT_R2];
}

void DefaultMaterial::SetSpecularCTRoughness( Float32 roughness )
{
	m_SpecularCTRoughness = roughness;
	m_PS.specularParam1.data[DefaultMaterial::SPECULAR_PI1_CT_RI] = MIX_FLOAT_RECIPROCAL( roughness );
	m_PS.specularParam1.data[DefaultMaterial::SPECULAR_PI1_CT_R2] = roughness * roughness;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetReflectScale( void ) const
{
	return m_PS.reflectParam.data[DefaultMaterial::REFLECT_PI_S];
}

void DefaultMaterial::SetReflectScale( Float32 scale )
{
	m_PS.reflectParam.data[DefaultMaterial::REFLECT_PI_S] = scale;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetReflectIntensity( void ) const
{
	return m_PS.reflectParam.data[DefaultMaterial::REFLECT_PI_I];
}

void DefaultMaterial::SetReflectIntensity( Float32 intensity )
{
	m_PS.reflectParam.data[DefaultMaterial::REFLECT_PI_I] = intensity;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetReflectBias( void ) const
{
	return m_PS.reflectParam.data[DefaultMaterial::REFLECT_PI_B];
}

void DefaultMaterial::SetReflectBias( Float32 bias )
{
	m_PS.reflectParam.data[DefaultMaterial::REFLECT_PI_B] = bias;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetReflectExprosure( void ) const
{
	return m_PS.reflectParam.data[DefaultMaterial::REFLECT_PI_E];
}

void DefaultMaterial::SetReflectExprosure( Float32 exprosure )
{
	m_PS.reflectParam.data[DefaultMaterial::REFLECT_PI_E] = exprosure;
	UpdateConstantPS();
}

const Mix::Vector4& DefaultMaterial::GetEmissiveColor( void ) const
{
	return m_PS.emissiveColor;
}

void DefaultMaterial::SetEmissiveColor( const Mix::Vector4& color )
{
	m_PS.emissiveColor = color;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetEmissiveScale( void ) const
{
	return m_PS.emissiveParam.data[DefaultMaterial::EMISSIVE_PI_S];
}

void DefaultMaterial::SetEmissiveScale( Float32 scale )
{
	m_PS.emissiveParam.data[DefaultMaterial::EMISSIVE_PI_S] = scale;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetBumpHeightScale( void ) const
{
	return m_PS.bumpParam0.data[DefaultMaterial::BUMP_PI0_HS];
}

void DefaultMaterial::SetBumpHeightScale( Float32 scale )
{
	m_PS.bumpParam0.data[DefaultMaterial::BUMP_PI0_HS] = scale;
	UpdateConstantPS();
}

Int32 DefaultMaterial::GetBumpSampleCount( void ) const
{
	return static_cast<Int32>( m_PS.bumpParam0.data[DefaultMaterial::BUMP_PI0_SC] );
}

void DefaultMaterial::SetBumpSampleCount( Int32 sampleCount )
{
	m_PS.bumpParam0.data[DefaultMaterial::BUMP_PI0_SC] = static_cast<Float32>( sampleCount );
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetBumpReflectFactor( void ) const
{
	return m_PS.bumpParam0.data[DefaultMaterial::BUMP_PI0_RL_F];
}

void DefaultMaterial::SetBumpReflectFactor( Float32 factor )
{
	m_PS.bumpParam0.data[DefaultMaterial::BUMP_PI0_RL_F] = factor;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetWaveUVOffset( void ) const
{
	return m_PS.bumpParam0.data[DefaultMaterial::BUMP_PI0_WAVE_O];
}

void DefaultMaterial::SetWaveUVOffset( Float32 uvOffset )
{
	m_PS.bumpParam0.data[DefaultMaterial::BUMP_PI0_WAVE_O] = uvOffset;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetWaveGap( void ) const
{
	return m_PS.bumpParam1.data[DefaultMaterial::BUMP_PI1_WAVE_G];
}

void DefaultMaterial::SetWaveGap( Float32 gap )
{
	m_PS.bumpParam1.data[DefaultMaterial::BUMP_PI1_WAVE_G] = gap;
}

Float32 DefaultMaterial::GetWaveRefractiveIndex( void ) const
{
	return m_PS.bumpParam1.data[DefaultMaterial::BUMP_PI1_RR_I];
}

void DefaultMaterial::SetWaveRefractiveIndex( Float32 index )
{
	m_PS.bumpParam1.data[DefaultMaterial::BUMP_PI1_RR_I] = index;
	UpdateConstantPS();
}

Float32 DefaultMaterial::GetSoftParticleThickness( void ) const
{
	return m_PS.particleParam.data[DefaultMaterial::PARTICLE_PI_T];
}

void DefaultMaterial::SetSoftParticleThickness( Float32 tickness )
{
	m_PS.particleParam.data[DefaultMaterial::PARTICLE_PI_T] = tickness;
	UpdateConstantPS();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IMaterial
////////////////////////////////////////////////////////////////////////////////////////////////////

void DefaultMaterial::Update( Float32 dt )
{
	Float32* waveData = m_PS.bumpParam1.data;
	Mix::Vector2 waveUV = UpdateWave( dt );

	waveData[DefaultMaterial::BUMP_PI1_WAVE_U] = waveUV.u;
	waveData[DefaultMaterial::BUMP_PI1_WAVE_V] = waveUV.v;

	UpdateConstantPS();
}

void DefaultMaterial::RenderShadow( Mix::Graphics::IDevice* pDevice, Mix::Scene::IMaterial::TRANSFORM_TYPE trType )
{
	pDevice->SetBlendState( Mix::Graphics::BLEND_COPY );
	pDevice->SetDepthState( True, ( m_bTransparency == False ) );
	pDevice->SetRasterizerState( Mix::Graphics::FILL_SOLID, ( m_CullType == Mix::Graphics::CULL_NONE )? Mix::Graphics::CULL_NONE : Mix::Graphics::CULL_BACK, False );

	pDevice->SetTexture( DefaultMaterial::TS_DIFFUSE, m_TextureFilter, m_TextureAddress, m_pDiffuseTexture );

	pDevice->SetPixelShaderConstant( DefaultMaterial::PSCB_SLOT, m_pConstantPS ); 

	pDevice->SetVertexLayout( m_pVertexLayout[trType] );
	pDevice->SetVertexShader( m_pVertexShader[trType][DefaultMaterial::RE_SHADOW] );
	pDevice->SetPixelShader( m_pPixelShader[DefaultMaterial::RE_SHADOW] );
}

void DefaultMaterial::RenderColor( Mix::Graphics::IDevice* pDevice, Mix::Scene::IMaterial::TRANSFORM_TYPE trType )
{
	Mix::Graphics::BLEND_DESC blendDesc = pDevice->GetBlendState();
	Mix::Graphics::RASTERIZER_DESC rasterizerDesc = pDevice->GetRasterizerState();
	Mix::Graphics::DEPTH_DESC depthDesc = pDevice->GetDepthState();

	blendDesc.type = m_BlendType;
	rasterizerDesc.cullMode = m_CullType;
	depthDesc.bWrite = m_bZWrite;

	pDevice->SetBlendState( blendDesc );
	pDevice->SetRasterizerState( rasterizerDesc );
	pDevice->SetDepthState( depthDesc );

	pDevice->SetTexture( DefaultMaterial::TS_DIFFUSE, m_TextureFilter, m_TextureAddress, m_pDiffuseTexture );
	pDevice->SetTexture( DefaultMaterial::TS_SPECULAR, m_TextureFilter, m_TextureAddress, m_pSpecularTexture );
	pDevice->SetTexture( DefaultMaterial::TS_EMISSIVE, m_TextureFilter, m_TextureAddress, m_pEmissiveTexture );
	pDevice->SetTexture( DefaultMaterial::TS_BUMP, Mix::Graphics::TEXTURE_FILTER_POINT, m_TextureAddress, m_pBumpTexture );

	pDevice->SetPixelShaderConstant( DefaultMaterial::PSCB_SLOT, m_pConstantPS ); 

	pDevice->SetVertexLayout( m_pVertexLayout[trType] );
	pDevice->SetVertexShader( m_pVertexShader[trType][DefaultMaterial::RE_COLOR] );
	pDevice->SetPixelShader( m_pPixelShader[DefaultMaterial::RE_COLOR] );
}

Boolean DefaultMaterial::BeginConstant( void )
{
	if( m_bValidConstantPS == False )
	{
		return False;
	}

	m_bValidConstantPS = False;

	return True;
}

Boolean DefaultMaterial::EndConstant( void )
{
	if( m_bValidConstantPS == True )
	{
		return False;
	}

	m_bValidConstantPS = True;

	UpdateConstantPS();

	return True;
}

Boolean DefaultMaterial::Clone( Mix::Scene::IMaterial** ppMaterial )
{
	MIX_ASSERT( ppMaterial != NULL );

	Mix::Graphics::IManager* pGraphicsMgr;
	Mix::Graphics::IDevice* pGraphicsDev;
	Mix::Scene::Standard::DX11::DefaultMaterial* pMaterial;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ログ : 開始
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_LOG_INFO_SECT_START( L"デフォルトマテリアルの複製 : Name[%s] Type[Standard( Default )]", m_Name.GetConstPtr() );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 作成 〜 初期化
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pGraphicsMgr = Mix::Graphics::GetManagerPtr();
	MIX_ASSERT( pGraphicsMgr != NULL );
	pGraphicsMgr->GetDevice( &pGraphicsDev );

	pMaterial = MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, Mix::Scene::Standard::DX11::DefaultMaterial );
	if( pMaterial == NULL )
	{
		MIX_LOG_ERROR( Mix::STR_OUTOFMEMORY );
		MIX_LOG_INFO_SECT_END();
		MIX_RELEASE( pGraphicsDev );
		return False;
	}

	if( pMaterial->Initialize( m_Name.GetConstPtr(), pGraphicsDev ) == False )
	{
		MIX_LOG_INFO_SECT_END();
		MIX_RELEASE( pGraphicsDev );
		return False;
	}

	if( Mix::Scene::Standard::Common::DefaultMaterial::InternalClone( pMaterial ) == False )
	{
		MIX_LOG_INFO_SECT_END();
		MIX_RELEASE( pGraphicsDev );
		return False;
	}

	MIX_RELEASE( pGraphicsDev );

	( *ppMaterial ) = pMaterial;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ログ : 終了
	////////////////////////////////////////////////////////////////////////////////////////////////////

	MIX_LOG_INFO_SECT_END();

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

	return True;
}

}}}}
