/*
	Copyright 2016, 2017 Michele "King_DuckZ" Santullo

	This file is part of MyCurry.

	MyCurry is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	MyCurry 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
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with MyCurry.  If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <boost/call_traits.hpp>
#include <boost/algorithm/clamp.hpp>
#include <cassert>

namespace curry {
	template <typename T>
	class ScalarConstrainer {
	public:
		typename boost::call_traits<T>::const_reference operator() (
				typename boost::call_traits<T>::param_type parMin,
				typename boost::call_traits<T>::param_type parMax,
				typename boost::call_traits<T>::param_type parValue
		) {
			return boost::algorithm::clamp(parValue, parMin, parMax);
		}
	};

	template <typename T, typename Constrainer=ScalarConstrainer<T>>
	class ConstrainedValue {
		using T_param = typename boost::call_traits<T>::param_type;
	public:
		ConstrainedValue (T_param parMin, T_param parMax, T_param parValue);
		virtual ~ConstrainedValue() noexcept = default;

		void set (T_param parNew);
		typename boost::call_traits<T>::const_reference get() const;
		typename boost::call_traits<T>::const_reference get_min() const;
		typename boost::call_traits<T>::const_reference get_max() const;
		void change_minmax (T_param parMin, T_param parMax);

		ConstrainedValue& operator= (T_param parValue);

	private:
		T m_min;
		T m_max;
		T m_value;
	};

	template <typename T, typename Constrainer>
	ConstrainedValue<T, Constrainer>::ConstrainedValue (
		T_param parMin,
		T_param parMax,
		T_param parValue
	) :
		m_min(parMin),
		m_max(parMax),
		m_value(parValue)
	{
		assert(parMin <= parMax);
	}

	template <typename T, typename Constrainer>
	void ConstrainedValue<T, Constrainer>::set (T_param parNew) {
		m_value = Constrainer()(m_min, m_max, parNew);
	}

	template <typename T, typename Constrainer>
	typename boost::call_traits<T>::const_reference ConstrainedValue<T, Constrainer>::get() const {
		return m_value;
	}

	template <typename T, typename Constrainer>
	typename boost::call_traits<T>::const_reference ConstrainedValue<T, Constrainer>::get_min() const {
		return m_min;
	}

	template <typename T, typename Constrainer>
	typename boost::call_traits<T>::const_reference ConstrainedValue<T, Constrainer>::get_max() const {
		return m_max;
	}

	template <typename T, typename Constrainer>
	void ConstrainedValue<T, Constrainer>::change_minmax (
		T_param parMin,
		T_param parMax
	) {
		assert(parMin <= parMax);
		m_min = parMin;
		m_max = parMax;
		this->set(m_value);
	}

	template <typename T, typename Constrainer>
	ConstrainedValue<T, Constrainer>& ConstrainedValue<T, Constrainer>::operator= (T_param parValue) {
		this->set(parValue);
		return *this;
	}
} //namespace curry
