#include "Mix/Geometry/Sphere.h"
#include "Mix/Geometry/AABB.h"

namespace Mix{ namespace Geometry{

Sphere::Sphere( void ) :
radius( 0.0f )
{
}

Sphere::Sphere( Float32 cx, Float32 cy, Float32 cz, Float32 r )
{
	center.x = cx;
	center.y = cy;
	center.z = cz;
	radius = r;
}

Sphere::Sphere( const Mix::Vector3& c, Float32 r )
{
	center = c;
	radius = r;
}

Sphere::Sphere( const Mix::Geometry::AABB& aabb )
{
	*this = aabb;
}

Mix::Geometry::Sphere& Sphere::operator = ( const Mix::Geometry::AABB& aabb )
{
	const Mix::Vector3& aabbMin = aabb.min;
	const Mix::Vector3& aabbMax = aabb.max;

	Float32 len = aabbMax.x - aabbMin.x;
	Float32 tmp;

	MIX_ASSERT( len >= 0.0f );

	tmp = aabbMax.y - aabbMin.y;
	MIX_ASSERT( tmp >= 0.0f );
	if( tmp < len ) { len = tmp; }

	tmp = aabbMax.z - aabbMin.z;
	MIX_ASSERT( tmp >= 0.0f );
	if( tmp < len ) { len = tmp; }

	center = aabb.center;
	radius = len * 0.5f;

	return *this;
}

Mix::Geometry::Sphere& Sphere::operator += ( const Mix::Geometry::Sphere& s )
{
	Mix::Vector3 v = ( s.center - center );
	Float32 d2 = ( ( v.x * v.x ) + ( v.y * v.y ) + ( v.z * v.z ) );
	Float32 r2 = ( s.radius - radius );

	r2 *= r2;

	if( d2 <= r2 )
	{
		if( radius <= s.radius )
		{
			center = s.center;
			radius = s.radius;
		}
	}
	else
	{
		Float32 d;
		Float32 r;

		d = ::sqrtf( d2 );
		r = ( ( d + radius + s.radius ) * 0.5f );

		if( MIX_FLOAT_EPSILON < d )
		{
			center += ( ( ( r - radius ) / d ) * v );
		}

		radius = r;
	}

	return *this;
}

Mix::Geometry::Sphere& Sphere::operator += ( const Mix::Geometry::AABB& aabb )
{
	*this += Mix::Geometry::Sphere( aabb );

	return *this;
}

Mix::Geometry::Sphere Sphere::operator + ( const Mix::Geometry::Sphere& sphere ) const
{
	Sphere temp( *this );

	temp += sphere;

	return temp;
}

Mix::Geometry::Sphere Sphere::operator + ( const Mix::Geometry::AABB& aabb ) const
{
	Sphere temp( *this );

	temp += aabb;

	return temp;
}

}}
