#pragma once

#include "vector2.h"
#include "math_util.h"

#include <numeric>
#include <limits>


namespace lm
{


//! axis aligned rect
template<typename T>
class range2
{
public:
	range2(void)
	{
		// 引数なしの場合は min > max となるように設定する
		T v_max = (std::numeric_limits<T>::max)();
		T v_min = -v_max;

		p0.set( v_max , v_max );
		p1.set( v_min , v_min );
	}

	range2( const T& x0 , const T& y0 , const T& x1 , const T& y1 )
		: p0( x0 , y0 ) , p1( x1 , y1 )
	{}
	range2( const vector2<T>& i_p0 , const vector2<T>& i_p1 )
		: p0(i_p0) , p1(i_p1)
	{}

	void clear(void);

	//! 有効な範囲が設定されているか(面積が0以上か)確かめる
	bool is_valid(void) const;

	//! 範囲を広げる
	void expand( const vector2<T>& v );
	void expand( const range2<T>& i_range );

	//! 指定した座標を含む場合はtrue
	bool contains( const vector2<T>& v ) const;
	//! 指定したrangeを交差する範囲があればtrue
	bool intersect( const range2<T>& i_range ) const;

	T length_x(void) const { return p1.x - p0.x; }
	T length_y(void) const { return p1.y - p0.y; }

	T min_length(void) const { return (lm::min)( length_x() , length_y() ); }
	T max_length(void) const { return (lm::max)( length_x() , length_y() ); }

	T&       min_x(void)       { return p0.x; }
	T&       min_y(void)       { return p0.y; }
	T&       max_x(void)       { return p1.x; }
	T&       max_y(void)       { return p1.y; }
	const T& min_x(void) const { return p0.x; }
	const T& min_y(void) const { return p0.y; }
	const T& max_x(void) const { return p1.x; }
	const T& max_y(void) const { return p1.y; }

	T mid_x(void) const { return ( p1.x + p0.x ) / T(2); }
	T mid_y(void) const { return ( p1.y + p0.y ) / T(2); }

	vector2<T> center(void) const { return ( p1 + p0 ) / T(2); }

	vector2<T>&       min_point(void)       { return p0; }
	vector2<T>&       max_point(void)       { return p1; }
	const vector2<T>& min_point(void) const { return p0; }
	const vector2<T>& max_point(void) const { return p1; }

protected:
	// p0 <= p1
	vector2<T> p0 , p1;
};


typedef range2<float>  range2f;
typedef range2<double> range2d;



// ----------------------------------------------------------------------------------------------------------------------------------------------------------------


template<typename T> inline
void range2<T>::clear(void)
{
	T v_max = (std::numeric_limits<T>::max)();
	T v_min = -v_max;

	p0.set( v_max , v_max );
	p1.set( v_min , v_min );
}

	//! 有効な範囲が設定されているか(面積が0以上か)確かめる
template<typename T> inline
bool range2<T>::is_valid(void) const
{
	if( p0.x > p1.x ) return false;
	if( p0.y > p1.y ) return false;
	return true;
}

template<typename T> inline
void range2<T>::expand( const vector2<T>& v )
{
	p0.x = (std::min)( v.x , p0.x );
	p0.y = (std::min)( v.y , p0.y );
	p1.x = (std::max)( v.x , p1.x );
	p1.y = (std::max)( v.y , p1.y );
}

template<typename T> inline
void range2<T>::expand( const range2<T>& i_range )
{
	if( !i_range.is_valid() )
		return;

	expand( i_range.min_point() );
	expand( i_range.max_point() );
}

template<typename T> inline
bool range2<T>::contains( const vector2<T>& v ) const
{
	if( v.x < p0.x || p1.x < v.x ) return false;
	if( v.y < p0.y || p1.y < v.y ) return false;

	return true;
}

template<typename T> inline
bool range2<T>::intersect( const range2<T>& i_range ) const
{
	if( this->max_x() < i_range.min_x() || this->min_x() > i_range.max_x() ) return false;
	if( this->max_y() < i_range.min_y() || this->min_y() > i_range.max_y() ) return false;
	return true;
}


}
