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

namespace Mix{ namespace Geometry{

AABB::AABB( void )
{
	min.Set( +MIX_FLOAT_MAX, +MIX_FLOAT_MAX, +MIX_FLOAT_MAX );
	max.Set( -MIX_FLOAT_MAX, -MIX_FLOAT_MAX, -MIX_FLOAT_MAX );
	center.Set( 0.0f, 0.0f, 0.0f );
}

AABB::AABB( const Mix::Vector3& _min, const Mix::Vector3& _max )
{
	min = _min;
	max = _max;
	center = ( min + max ) * 0.5f;
}

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

AABB& AABB::ComputeMinMax( void )
{
	UInt32 i;

	min = max = points[0];

	//最小値、最大値
	for( i = 1; i < 8; i++ )
	{
		const Mix::Vector3& p = points[i];

		if( min.x > p.x ) min.x = p.x;
		if( min.y > p.y ) min.y = p.y;
		if( min.z > p.z ) min.z = p.z;

		if( max.x < p.x ) max.x = p.x;
		if( max.y < p.y ) max.y = p.y;
		if( max.z < p.z ) max.z = p.z;
	}

	//中心
	center = ( min + max ) * 0.5f;

	return *this;
}

AABB& AABB::ComputeMinMax( const Mix::Matrix4x4& mat )
{
	UInt32 i;
	Mix::Vector3 point;

	min = max = mat * points[0];

	//最小値、最大値
	for( i = 1; i < 8; i++ )
	{
		point = mat * points[i];

		if( min.x > point.x ) { min.x = point.x; }
		if( min.y > point.y ) { min.y = point.y; }
		if( min.z > point.z ) { min.z = point.z; }

		if( max.x < point.x ) { max.x = point.x; }
		if( max.y < point.y ) { max.y = point.y; }
		if( max.z < point.z ) { max.z = point.z; }
	}

	//中心
	center = ( min + max ) * 0.5f;

	return *this;
}

AABB& AABB::ComputePoints( void )
{
	//上平面
	points[0].x = min.x;
	points[0].y = max.y;
	points[0].z = max.z;
	points[1].x = max.x;
	points[1].y = max.y;
	points[1].z = max.z;
	points[2].x = max.x;
	points[2].y = max.y;
	points[2].z = min.z;
	points[3].x = min.x;
	points[3].y = max.y;
	points[3].z = min.z;

	//下平面
	points[4].x = min.x;
	points[4].y = min.y;
	points[4].z = max.z;
	points[5].x = max.x;
	points[5].y = min.y;
	points[5].z = max.z;
	points[6].x = max.x;
	points[6].y = min.y;
	points[6].z = min.z;
	points[7].x = min.x;
	points[7].y = min.y;
	points[7].z = min.z;

	//中心
	center = ( min + max ) * 0.5f;

	return *this;
}

AABB& AABB::ComputePoints( const Mix::Matrix4x4& mat )
{
	UInt32 i;

	ComputePoints();

	//点
	for( i = 0; i < 8; i++ )
	{
		points[i] = mat * points[i];
	}

	//中心
	center = ( min + max ) * 0.5f;

	return *this;
}

AABB& AABB::operator = ( const Mix::Geometry::Sphere& sphere )
{
	const Mix::Vector3& sc = sphere.center;
	const Float32& sr = sphere.radius;

	min.x = sc.x - sr;
	min.y = sc.y - sr;
	min.z = sc.z - sr;

	max.x = sc.x + sr;
	max.y = sc.y + sr;
	max.z = sc.z + sr;

	center = ( min + max ) * 0.5f;

	return *this;
}

AABB& AABB::operator += ( const AABB& aabb )
{
	UInt32 i;

	const Float32* srcMin = aabb.min.data;
	const Float32* srcMax = aabb.max.data;

	Float32* dstMin = min.data;
	Float32* dstMax = max.data;

	for( i = 0; i < 3; i++ )
	{
		if( dstMin[i] > srcMin[i] ) { dstMin[i] = srcMin[i]; }
		if( dstMax[i] < srcMax[i] ) { dstMax[i] = srcMax[i]; }
	}

	center = ( min + max ) * 0.5f;

	return *this;
}

AABB AABB::operator + ( const AABB& aabb ) const
{
	AABB tmp( *this );

	tmp += aabb;

	return tmp;
}

AABB& AABB::operator += ( const Sphere& sphere )
{
	Float32 sr = sphere.radius;
	const Mix::Vector3& sc = sphere.center;

	Float32 srcMin[3] = { sc.x - sr, sc.y - sr, sc.z - sr };
	Float32 srcMax[3] = { sc.x + sr, sc.y + sr, sc.z + sr };

	Float32* dstMin = min.data;
	Float32* dstMax = max.data;

	UInt32 i;

	for( i = 0; i < 3; i++ )
	{
		if( dstMin[i] > srcMin[i] ) { dstMin[i] = srcMin[i]; }
		if( dstMax[i] < srcMax[i] ) { dstMax[i] = srcMax[i]; }
	}

	center = ( min + max ) * 0.5f;

	return *this;
}

AABB AABB::operator + ( const Sphere& sphere ) const
{
	AABB tmp( *this );

	tmp += sphere;

	return tmp;
}

AABB& AABB::operator += ( const Mix::Vector3& v )
{
	min = Mix::Vector3::Min( min, v );
	max = Mix::Vector3::Max( max, v );

	return *this;
}

AABB AABB::operator + ( const Mix::Vector3& v ) const
{
	AABB tmp( *this );

	tmp += v;

	return tmp;
}

}}
