#include "Mix/Private/Scene/Common/PointLight.h"

#ifdef _DEBUG
	#include "Mix/Graphics/Utility/IPerspectiveRenderer.h"
	#include "Mix/Private/Scene/Common/Debug.h"
#endif //_DEBUG

namespace Mix{ namespace Scene{ namespace Common{

const wchar_t* PointLight::FAILED_CREATE = L"ポイントライトの作成に失敗";

const Float32 PointLight::MIN_DIFF_RADIUS = 0.001f;
const Float32 PointLight::MIN_INV_DIFF_RADIUS = MIX_FLOAT_RECIPROCAL( PointLight::MIN_DIFF_RADIUS );

PointLight* PointLight::CreateInstance( void )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, PointLight );
}

PointLight::PointLight( void ) :
m_bEnabled( True ),
m_pOctObj( NULL ),
m_InnerRadius( 0.0f )
{
	m_Param.pos.Set( 0.0f, 0.0f, 0.0f );
	m_Param.atten.x = 1.0f;
	m_Param.atten.y = 0.0f;
	m_Param.atten.z = 0.0f;
	m_Param.color.Set( 1.0f, 1.0f, 1.0f, 1.0f );
	m_Param.outerRadius = 50.0f;
	m_Param.invDiffRadius = MIX_FLOAT_RECIPROCAL( m_Param.outerRadius - m_InnerRadius );

	UpdateBounds();
}

PointLight::~PointLight( void )
{
	MIX_ASSERT( m_pOctObj == NULL );
}

void PointLight::UpdateBounds( void )
{
	m_Bounds.center = m_Param.pos;
	m_Bounds.radius = m_Param.outerRadius;
}

void PointLight::Attach( Mix::Scene::Common::LocalLightObject* pOctObj )
{
	MIX_ASSERT( pOctObj != NULL );
	MIX_ASSERT( m_pOctObj == NULL );

	m_pOctObj = pOctObj;

	RendererObject::SetRendering( True );
}

void PointLight::Detach( void )
{
	if( m_pOctObj != NULL )
	{
		m_pOctObj->Destroy();
		m_pOctObj = NULL;
	}

	RendererObject::SetRendering( False );
}

const PointLight::PARAM& PointLight::GetParam( void ) const
{
	return m_Param;
}

#ifdef _DEBUG

void PointLight::Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, UInt32 flags )
{
	Mix::Matrix4x4 oldMat = pPerspectiveRenderer->GetMatrix();
	Mix::Vector4 oldColor = pPerspectiveRenderer->GetColor();

	if( MIX_TESTBIT( flags, Mix::Scene::DDF_POINTLIGHT_SHAPE ) == Mix::Scene::DDF_POINTLIGHT_SHAPE )
	{
		pPerspectiveRenderer->SetMatrix( Mix::Matrix4x4::Identity() );
		pPerspectiveRenderer->SetColor( Mix::Scene::Common::Debug::GetDrawColor( Mix::Scene::DDC_LIGHT_SHAPE ) );
		pPerspectiveRenderer->AddSphere( m_Param.pos, m_InnerRadius );
		pPerspectiveRenderer->AddSphere( m_Param.pos, m_Param.outerRadius );
	}

	if( MIX_TESTBIT( flags, Mix::Scene::DDF_POINTLIGHT_BOUNDS ) == Mix::Scene::DDF_POINTLIGHT_BOUNDS )
	{
		pPerspectiveRenderer->SetMatrix( Mix::Matrix4x4::Identity() );
		pPerspectiveRenderer->SetColor( Mix::Scene::Common::Debug::GetDrawColor( Mix::Scene::DDC_BOUNDS ) );
		pPerspectiveRenderer->AddSphere( m_Bounds );
	}

	pPerspectiveRenderer->SetColor( oldColor );
	pPerspectiveRenderer->SetMatrix( oldMat );
}

#endif //_DEBUG

Float32 PointLight::GetInnerRadius( void ) const
{
	return m_InnerRadius;
}

Float32 PointLight::GetOuterRadius( void ) const
{
	return m_Param.outerRadius;
}

void PointLight::SetRadius( Float32 innerRadius, Float32 outerRadius )
{
	Float32 diffRadius = max( 0.0f, ( outerRadius - innerRadius ) );

	m_InnerRadius = max( 0.0f, innerRadius );

	m_Param.outerRadius = max( 0.0f, outerRadius );

	if( PointLight::MIN_DIFF_RADIUS < diffRadius )
	{
		m_Param.invDiffRadius = MIX_FLOAT_RECIPROCAL( max( 0.0f, ( m_Param.outerRadius - m_InnerRadius ) ) );
	}
	else
	{
		m_Param.invDiffRadius = PointLight::MIN_INV_DIFF_RADIUS;
	}

	UpdateBounds();
}

const Mix::Vector4& PointLight::GetColor( void ) const
{
	return m_Param.color;
}

void PointLight::SetColor( const Mix::Vector4& color )
{
	m_Param.color = color;
}

Float32 PointLight::GetLinearAttenuation( void ) const
{
	return m_Param.atten.x;
}

Float32 PointLight::GetQuadraticAttenuation( void ) const
{
	return m_Param.atten.y;
}

Float32 PointLight::GetExponentAttenuation( void ) const
{
	return m_Param.atten.z;
}

void PointLight::SetAttenuation( Float32 l, Float32 q, Float32 e )
{
	m_Param.atten.x = max( 0.0f, l );
	m_Param.atten.y = max( 0.0f, q );
	m_Param.atten.z = max( 0.0f, e );
}

const Mix::Vector3& PointLight::GetPosition( void ) const
{
	return m_Param.pos;
}

void PointLight::SetPosition( const Mix::Vector3& pos )
{
	m_Param.pos = pos;

	UpdateBounds();
}

void PointLight::Refresh( void )
{
	if( m_pOctObj != NULL )
	{
		m_pOctObj->Refresh();
	}
}

const Mix::Geometry::Sphere& PointLight::GetBounds() const
{
	return m_Bounds;
}

Boolean PointLight::IsIllegal( void ) const
{
	return ( m_pOctObj != NULL )? m_pOctObj->IsIllegal() : False;
}

Boolean PointLight::IsEnabled( void ) const
{
	return m_bEnabled;
}

void PointLight::SetEnabled( Boolean state )
{
	m_bEnabled = state;
}

Mix::Scene::IRendererObject::TYPE PointLight::GetType( void ) const
{
	return Mix::Scene::IRendererObject::POINT_LIGHT;
}

Boolean PointLight::IsRendering( void ) const
{
	return RendererObject::IsRendering();
}

}}}
