//DESCRIPTION
//	This file contains some basic math functions which STDLib cannot
//	fully satisfy.

//REQUIRES:
//	---

//LICENSE
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the Lesser GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//	Lesser GNU General Public License for more details.
//
//	You should have received a copy of the Lesser GNU General Public License
//	along with this program. If not, see < http://www.gnu.org/licenses/ >.

//COPYRIGHT
//	Cem Kalyoncu, DarkGaze.Org (cemkalyoncu[at]gmail[dot]com)

#pragma once

#include <cmath>
#include <cstdlib>
#include <type_traits>
#include "Iterator.h"


namespace gge { namespace utils {

	template <class T_>
	inline T_ Round(T_ num) {
		return std::floor(num+0.5f); 
	}

	template <class T_>
	inline T_ Round(const T_ &num, int digits) {
		T_ d=std::pow(10.0f, digits);
		return std::floor(num*d+0.5f)/d;
	}

	//accepts both terminals as in range
	template <class T_, class U_, class V_>
	bool InRange(const T_ &num, const U_ &n1, const V_ &n2) {
		if(n1<n2)
			return num>=n1 && num<=n2;
		else
			return num>=n2 && num<=n1;
	}

	template <class T_, class U_>
	typename std::enable_if<!is_iterator<T_>::value, T_>::type 
	Max(const T_ &num1, const U_ &num2) { return num1>num2 ? num1 : num2; }

	template <class T_, class U_>
	typename std::enable_if<!is_iterator<T_>::value, T_>::type 
	Min(const T_ &num1, const U_ &num2) { return num1<num2 ? num1 : num2; }

	template <class T_, class P_>
	typename std::enable_if<is_iterator<T_>::value, typename T_::value_type>::type 
	Max(const T_ &num1, const T_ &num2, P_ pred) { 
		if(num1==num2)
			return typename T_::value_type();

		typename T_::value_type m=*num1;
		T_ it=num1;
		it++;
		for(;it!=num2;++it) {
			if(pred(*it,m))
				m=*it;
		}

		return m;
	}
	
	template <class T_, class P_>
	typename std::enable_if<is_iterator<T_>::value, typename T_::value_type>::type 
	Min(const T_ &num1, const T_ &num2, P_ pred) { 
		if(num1==num2)
			return typename T_::value_type();

		typename T_::value_type m=*num1;
		T_ it=num1;
		it++;
		for(;it!=num2;++it) {
			if(pred(*it,m))
				m=*it;
		}

		return m;
	}

	template <class T_>
	typename std::enable_if<is_iterator<T_>::value, typename T_::value_type>::type 
	Min(const T_ &num1, const T_ &num2) { 
	  return Min(num1, num2, std::less<typename T_::value_type>());
	}

	template <class T_>
	typename std::enable_if<is_iterator<T_>::value, typename T_::value_type>::type 
	Max(const T_ &num1, const T_ &num2) { 
	  return Max(num1, num2, std::greater<typename T_::value_type>());
	}


	template <class T_>
	inline T_ PositiveMod(const T_ &num, const T_ &mod) {
		return num<0 ? (mod + (num%mod))%mod : num%mod;
	}

	template <class T_>
	T_ StrToNumber(const std::string &value, int base=10) {
		char *n;
		return std::strtol(value.c_str(), &n, base);
	}

	template <>
	inline float StrToNumber<float>(const std::string &value, int base) {
		return (float)std::atof(value.c_str());
	}

	template <>
	inline double StrToNumber<double>(const std::string &value, int base) {
		return std::atof(value.c_str());
	}

	template<class T_>
	T_ Limit(const T_ &value, const T_ &min, const T_ &max) {
		if(value<min) return min;
		if(value>max) return max;
		return value;
	}

	template<class T_>
	T_ Sign(const T_ &value) {
		if(value==0)
			return 0;
		else
			return value > 0 ? 1 : -1;
	}

}}

