#pragma once

#include "math_util.h"

#include <numeric>
#include <limits>


namespace lm
{


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

		p0 = v_max;
		p1 = v_min;
	}

	range1( const T& x0 , const T& x1 )
		: p0( x0 ) , p1( x1 )
	{}

	void clear(void);

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

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

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

	T length(void) const { return p1 - p0; }

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

	T&       min_val(void)       { return p0; }
	T&       max_val(void)       { return p1; }
	const T& min_val(void) const { return p0; }
	const T& max_val(void) const { return p1; }

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


typedef range1<float>  range1f;
typedef range1<double> range1d;



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


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

	p0 = v_max;
	p1 = v_min;
}

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

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

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

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

template<typename T> inline
bool range1<T>::contains( const T& v ) const
{
	if( v < p0 || p1 < v ) return false;
	return true;
}

template<typename T> inline
bool range1<T>::intersect( const range1<T>& i_range ) const
{
	if( this->max_val() < i_range.min_val() || this->min_val() > i_range.max_val() ) return false;
	return true;
}


}
