#include "Mix/Private/Scene/Common/SimpleParticleGenerator.h"
#include "Mix/Geometry/Sphere.h"

#ifdef _DEBUG
	#include "Mix/Graphics/Utility/IPerspectiveRenderer.h"
#endif //_DEBUG

namespace Mix{ namespace Scene{ namespace Common{

const Mix::Vector3 SimpleParticleGenerator::MIN_EMIT_POS( -1.0f, -1.0f, -1.0f );
const Mix::Vector3 SimpleParticleGenerator::MAX_EMIT_POS( +1.0f, +1.0f, +1.0f );

SimpleParticleGenerator::SimpleParticleGenerator( void ) :
m_EmitRadius( 0.2f, 0.2f, 0.2f ),
m_EmitOffset( 0.5f ),
m_EmitInnerRadius( m_EmitRadius * m_EmitOffset ),
m_MinLife( 1.0f ),
m_MaxLife( 2.0f ),
m_DiffLife( m_MaxLife - m_MinLife ),
m_MinMass( 0.8f ),
m_MaxMass( 1.0f ),
m_DiffMass( m_MaxMass - m_MinMass ),
m_Gravity( 0.0f, -9.8f, 0.0f ),
m_ConstantLinearVelocity( 0.0f, 0.0f, 0.0f ),
m_MinLinearImpulse( -2.0f, +10.0f, -2.0f ),
m_MaxLinearImpulse( +2.0f,  +9.0f, +2.0f ),
m_DiffLinearImpulse( m_MaxLinearImpulse - m_MinLinearImpulse ),
m_MinLinearAcc( 0.0f, 0.0f, 0.0f ),
m_MaxLinearAcc( 0.0f, 0.0f, 0.0f ),
m_DiffLinearAcc( m_MaxLinearAcc - m_MinLinearAcc ),
m_MinLinearVelDamp( 0.0f ),
m_MaxLinearVelDamp( 0.0f ),
m_DiffLinearVelDamp( m_MaxLinearVelDamp - m_MinLinearVelDamp ),
m_MinAngularVelDamp( 0.0f ),
m_MaxAngularVelDamp( 0.0f ),
m_DiffAngularVelDamp( m_MaxAngularVelDamp - m_MinAngularVelDamp ),
m_InitalSize( 1.0f, 1.0f ),
m_LastSize( 0.1f, 0.1f ),
m_MinSizeRnd( 0.5 ),
m_MaxSizeRnd( 1.0f ),
m_DiffSizeRnd( m_MaxSizeRnd - m_MinSizeRnd ),
m_InitalColor( 1.0f, 1.0f, 1.0f, 1.0f ),
m_MiddleColor( 1.0f, 1.0f, 1.0f, 0.5f ),
m_LastColor( 1.0f, 1.0f, 1.0f, 0.0f ),
m_ColorCtrlPoint1( 0.3f ),
m_ColorCtrlPoint2( 0.7f ),
m_TexTL( 0.0f, 0.0f ),
m_TexBR( 1.0f, 1.0f )
{
}

SimpleParticleGenerator::~SimpleParticleGenerator( void )
{
}

const Mix::Vector3& SimpleParticleGenerator::GetEmitRadius( void ) const
{
	return m_EmitRadius;
}

void SimpleParticleGenerator::SetEmitRadius( const Mix::Vector3& radius )
{
	m_EmitRadius.x = max( 0.0f, radius.x );
	m_EmitRadius.y = max( 0.0f, radius.y );
	m_EmitRadius.z = max( 0.0f, radius.z );

	m_EmitInnerRadius = m_EmitRadius * m_EmitOffset;
}

Float32 SimpleParticleGenerator::GetEmitOffset( void ) const
{
	return m_EmitOffset;
}

void SimpleParticleGenerator::SetEmitOffset( Float32 offset )
{
	m_EmitOffset = MIX_CLAMP( offset, 0.0f, 1.0f );

	m_EmitInnerRadius = m_EmitRadius * m_EmitOffset;
}

Float32 SimpleParticleGenerator::GetMinLife( void ) const
{
	return m_MinLife;
}

Float32 SimpleParticleGenerator::GetMaxLife( void ) const
{
	return m_MaxLife;
}

void SimpleParticleGenerator::SetLife( Float32 minLife, Float32 maxLife )
{
	m_MinLife = max( 0.0f, minLife );
	m_MaxLife = max( m_MinLife, maxLife );
	m_DiffLife = m_MaxLife - m_MinLife;
}

Float32 SimpleParticleGenerator::GetMinMass( void ) const
{
	return m_MinMass;
}

Float32 SimpleParticleGenerator::GetMaxMass( void ) const
{
	return m_MaxMass;
}

void SimpleParticleGenerator::SetMass( Float32 minMass, Float32 maxMass )
{
	m_MinMass = max( 0.0f, minMass );
	m_MaxMass = max( m_MinMass, maxMass );
	m_DiffMass = m_MaxMass - m_MinMass;
}

const Mix::Vector3& SimpleParticleGenerator::GetGravity( void ) const
{
	return m_Gravity;
}

void SimpleParticleGenerator::SetGravity( const Mix::Vector3& gravity )
{
	m_Gravity = gravity;
}

const Mix::Vector3& SimpleParticleGenerator::GetConstantLinearVelocity( void ) const
{
	return m_ConstantLinearVelocity;
}

void SimpleParticleGenerator::SetConstantLinearVelocity( const Mix::Vector3& vel )
{
	m_ConstantLinearVelocity = vel;
}

const Mix::Vector3& SimpleParticleGenerator::GetMinLinearImpulse( void ) const
{
	return m_MinLinearImpulse;
}

const Mix::Vector3& SimpleParticleGenerator::GetMaxLinearImpulse( void ) const
{
	return m_MaxLinearImpulse;
}

void SimpleParticleGenerator::SetLinearImpulse( const Mix::Vector3& minImpulse, const Mix::Vector3& maxImpulse )
{
	m_MinLinearImpulse = minImpulse;
	m_MaxLinearImpulse = Mix::Vector3::Max( m_MinLinearImpulse, maxImpulse );
	m_DiffLinearImpulse = m_MaxLinearImpulse - m_MinLinearImpulse;
}

const Mix::Vector3& SimpleParticleGenerator::GetMinLinearAcceleration( void ) const
{
	return m_MinLinearAcc;
}

const Mix::Vector3& SimpleParticleGenerator::GetMaxLinearAcceleration( void ) const
{
	return m_MaxLinearAcc;
}

void SimpleParticleGenerator::SetLinearAcceleration( const Mix::Vector3& minAcc, const Mix::Vector3& maxAcc )
{
	m_MinLinearAcc = minAcc;
	m_MaxLinearAcc = Mix::Vector3::Max( m_MinLinearAcc, maxAcc );
	m_DiffLinearAcc = m_MaxLinearAcc - m_MinLinearAcc;
}

Float32 SimpleParticleGenerator::GetMinLinearVelocityDamping( void ) const
{
	return m_MinLinearVelDamp;
}

Float32 SimpleParticleGenerator::GetMaxLinearVelocityDamping( void ) const
{
	return m_MaxLinearVelDamp;
}

void SimpleParticleGenerator::SetLinearVelocityDamping( Float32 minDamping, Float32 maxDamping )
{
	m_MinLinearVelDamp = max( 0.0f, minDamping );
	m_MaxLinearVelDamp = max( m_MinLinearVelDamp, maxDamping );
	m_DiffLinearVelDamp = m_MaxLinearVelDamp - m_MinLinearVelDamp;
}

Float32 SimpleParticleGenerator::GetMinAngularVelocityDamping( void ) const
{
	return m_MinAngularVelDamp;
}

Float32 SimpleParticleGenerator::GetMaxAngularVelocityDamping( void ) const
{
	return m_MaxAngularVelDamp;
}

void SimpleParticleGenerator::SetAngularVelocityDamping( Float32 minDamping, Float32 maxDamping )
{
	m_MinAngularVelDamp = max( 0.0f, minDamping );
	m_MaxAngularVelDamp = max( m_MinAngularVelDamp, maxDamping );
	m_DiffAngularVelDamp = m_MaxAngularVelDamp - m_MinAngularVelDamp;
}

const Mix::Vector2& SimpleParticleGenerator::GetInitalSize( void ) const
{
	return m_InitalSize;
}

const Mix::Vector2& SimpleParticleGenerator::GetLastSize( void ) const
{
	return m_LastSize;
}

Float32 SimpleParticleGenerator::GetMinSizeRand( void ) const
{
	return m_MinSizeRnd;
}

Float32 SimpleParticleGenerator::GetMaxSizeRand( void ) const
{
	return m_MaxSizeRnd;
}

void SimpleParticleGenerator::SetSize( const Mix::Vector2& initalSize, const Mix::Vector2& lastSize, Float32 minSizeRnd, Float32 maxSizeRnd )
{
	m_InitalSize = Mix::Vector2::Max( Mix::Vector2::Zero(), initalSize );
	m_LastSize = Mix::Vector2::Max( Mix::Vector2::Zero(), lastSize );

	m_MinSizeRnd = max( 0.0f, minSizeRnd );
	m_MaxSizeRnd = max( m_MinSizeRnd, maxSizeRnd );
	m_DiffSizeRnd = m_MaxSizeRnd - m_MinSizeRnd;
}

const Mix::Vector4& SimpleParticleGenerator::GetInitalColor( void ) const
{
	return m_InitalColor;
}

const Mix::Vector4& SimpleParticleGenerator::GetMiddleColor( void ) const
{
	return m_MiddleColor;
}

const Mix::Vector4& SimpleParticleGenerator::GetLastColor( void ) const
{
	return m_LastColor;
}

void SimpleParticleGenerator::SetColor( const Mix::Vector4& initalColor, const Mix::Vector4& middleColor, const Mix::Vector4& lastColor )
{
	m_InitalColor = initalColor.ToSaturate();
	m_MiddleColor = middleColor.ToSaturate();
	m_LastColor = lastColor.ToSaturate();
}

Float32 SimpleParticleGenerator::GetColorControlPoint1( void ) const
{
	return m_ColorCtrlPoint1;
}

Float32 SimpleParticleGenerator::GetColorControlPoint2( void ) const
{
	return m_ColorCtrlPoint2;
}

void SimpleParticleGenerator::SetColorControlPoints( Float32 p1, Float32 p2 )
{
	m_ColorCtrlPoint1 = max( 0.0f, p1 );
	m_ColorCtrlPoint2 = max( m_ColorCtrlPoint1, p2 );
}

const Mix::Vector2& SimpleParticleGenerator::GetTexTL( void ) const
{
	return m_TexTL;
}

const Mix::Vector2& SimpleParticleGenerator::GetTexBR( void ) const
{
	return m_TexBR;
}

void SimpleParticleGenerator::SetTexCoords( const Mix::Vector2& tl, const Mix::Vector2& br )
{
	m_TexTL = tl;
	m_TexBR = br;
}

void SimpleParticleGenerator::GetData(	Float32& mass,
										Mix::Vector3& pos,
										Float32& lifeMax,
										Float32& invLifeMax,
										Mix::Vector3& constLinearAcc,
										Mix::Vector3& constLinearVel,
										Mix::Vector3& linearAcc,
										Mix::Vector3& linearVel,
										Float32& linearVelDamp,
										Float32& angularVelDamp,
										Mix::Vector2& initalSize,
										Mix::Vector2& middleSize,
										Mix::Vector2& lastSize,
										Mix::Vector4& initalColor,
										Mix::Vector4& middleColor,
										Mix::Vector4& lastColor,
										Float32* colCtrlPoints,
										Mix::Vector2& texTL,
										Mix::Vector2& texBR )
{
	Float32 invMass;
	Float32 sizeRnd;

	// 質量
	mass = m_MinMass + m_DiffMass * Mix::RandF();
	invMass = MIX_FLOAT_RECIPROCAL( mass );

	// 位置
	pos = Mix::Vector3::Rand( SimpleParticleGenerator::MIN_EMIT_POS, SimpleParticleGenerator::MAX_EMIT_POS ).ToNormalize();
	pos *= Mix::Vector3::Rand( m_EmitInnerRadius, m_EmitRadius );

	// ライフ
	lifeMax = m_MinLife + m_DiffLife * Mix::RandF();
	invLifeMax = MIX_FLOAT_RECIPROCAL( lifeMax );

	// 一定の移動
	constLinearAcc = m_Gravity * mass;
	constLinearVel = m_ConstantLinearVelocity;

	// 移動
	linearAcc = m_MinLinearAcc + m_DiffLinearAcc * Mix::Vector3::Rand();
	linearVel = ( m_MinLinearImpulse + m_DiffLinearImpulse * Mix::Vector3::Rand() ) * invMass;
	linearVelDamp = m_MinLinearVelDamp + m_DiffLinearVelDamp * Mix::RandF();

	// 回転
	angularVelDamp = m_MinAngularVelDamp + m_DiffAngularVelDamp * Mix::RandF();

	// サイズ
	sizeRnd = m_MinSizeRnd + m_DiffSizeRnd * Mix::RandF();
	initalSize = m_InitalSize * sizeRnd;
	lastSize = m_LastSize * sizeRnd;
	middleSize = ( initalSize + lastSize ) * 0.5f;

	// 色
	initalColor = m_InitalColor;
	middleColor = m_MiddleColor;
	lastColor = m_LastColor;
	colCtrlPoints[0] = 0.0f;
	colCtrlPoints[1] = m_ColorCtrlPoint1;
	colCtrlPoints[2] = m_ColorCtrlPoint2;
	colCtrlPoints[3] = 1.0f;

	// テクスチャ
	texTL = m_TexTL;
	texBR = m_TexBR;
}

void SimpleParticleGenerator::GetData( Float32 mass, Float32 x, Float32 y, Float32 z, Mix::Matrix4x4& invInertia )
{
	Float32 a2 = x * x;
	Float32 b2 = y * y;
	Float32 l2 = z * z;

	invInertia = Mix::Matrix4x4::Identity();
	invInertia.m00 = mass * ( a2 + l2 );
	invInertia.m11 = mass * ( b2 + l2 );
	invInertia.m22 = mass * ( a2 + b2 );
	invInertia.Inverse();
}

#ifdef _DEBUG

void SimpleParticleGenerator::Debug_Draw( const Mix::Matrix4x4& worldMat, Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer )
{
	Mix::Matrix4x4 oldMat = pPerspectiveRenderer->GetMatrix();
	Mix::Vector4 oldColor = pPerspectiveRenderer->GetColor();

	pPerspectiveRenderer->SetMatrix( worldMat );

	pPerspectiveRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
	pPerspectiveRenderer->AddSphere( m_EmitRadius );
	pPerspectiveRenderer->AddSphere( m_EmitInnerRadius );

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

#endif //_DEBUG

}}}
