#include <sys/types.h>
#include <string>
#include <cmath>

class Param
{
	std::string m_name;
	std::string m_unit;
	typedef struct _sparam_info_t
	{
		double pmin, pmax, pvalue, pinterval, pcenter, scenter, base, kexp;

	} param_info_t;
	
	param_info_t m_param_info;

	public:


	Param(
		std::string name,
		std::string unit,
		double pmin,
		double pmax,
		double pvalue,
		double pinterval=1.0,
		double pcenter=-1.0,
		double scenter=0.0,
		double base=1.0,
		double kexp=1.0
	)
	: m_name(name)
	, m_param_info({pmin, pmax, pvalue, pinterval, pcenter, scenter, base, kexp})
	{

	}

	std::string name()
	{
		return m_name;
	}

	std::string unit()
	{
		return m_unit;
	}

	double getParameter()
	{
		return m_param_info.pvalue;
	}

	void setParameter(double p)
	{
		m_param_info.pvalue = p;
	}

	double getSlider()
	{
		return toSlider();
	}
	
	// --------------------------------------------------------------
	// Exported functions
	// --------------------------------------------------------------
	double quantizeParam(double p)
	{
		double k, pq;
		if (m_param_info.pinterval == 0)
		{
			return p;
		}

		k = (double)1/(m_param_info.pinterval - 0);

		if (p < 0)
		{
			pq = ((int)(p*k - 0.5))*m_param_info.pinterval;
		}
		else
		{
			pq = ((int)(p*k + 0.5))*m_param_info.pinterval;
		}

		return pq;
	}

	double clampParam(double p)
	{
		return (double)std::min(m_param_info.pmax, std::max(m_param_info.pmin, quantizeParam(p)));
	}

	double fromSlider(double s)
	{
		double p, pcenter;
		double pmax, pmin, base, kexp, scenter;

		pmax = m_param_info.pmax;
		pmin = m_param_info.pmin;
		if (pmin == pmax)
		{
			pmax += 1E-9;
		}
		base = m_param_info.base;
		kexp = m_param_info.kexp;
		if (kexp == 0)
		{
			kexp = 1E-9;
		}
		scenter = m_param_info.scenter;
		pcenter = m_param_info.pcenter;

		if ((pcenter < pmin) || (pcenter > pmax))
			pcenter = pmax*scenter + pmin*(1-scenter);

		s = (double)std::min(1.0, std::max(0.0, s));
		if (s < scenter)
		{
			if (base == 1)
			{
				// pcenter-(pcenter-pmin)*(scenter-s)/scenter;
				p = pcenter-(pcenter-pmin)*(scenter-s)/scenter;
			}
			else
			{
				// pcenter-(pcenter-pmin)*(B^(k*(scenter-s)/scenter)-1)/(B^k-1);
				p = pcenter-(pcenter-pmin)*(pow(base,kexp*(scenter-s)/scenter)-1)/(pow(base,kexp)-1);
			}
		}
		else
		{
			if (base == 1)
			{
				// pcenter+(pmax-pcenter)*(-scenter+s)/(1-scenter);
				p = pcenter+(pmax-pcenter)*(-scenter+s)/(1-scenter);
			}
			else
			{
				// pcenter+(pmax-pcenter)*(B^(k*(-scenter+s)/(1-scenter))-1)/(B^k-1);
				p = pcenter+(pmax-pcenter)*(pow(base,kexp*(-scenter+s)/(1-scenter))-1)/(pow(base,kexp)-1);
			}
		}

		m_param_info.pvalue = p;
		return quantizeParam(p);
	}

	double toSlider()
	{
		double p = m_param_info.pvalue;

		double s, pcenter;
		double pmax, pmin, base, kexp, scenter;

		pmax = m_param_info.pmax;
		pmin = m_param_info.pmin;
		if (pmin == pmax)
		{
			pmax += 1E-9;
		}
		base = m_param_info.base;
		kexp = m_param_info.kexp;
		if (kexp == 0)
		{
			kexp = 1E-9;
		}
		scenter = m_param_info.scenter;
		pcenter = m_param_info.pcenter;

		if ((pcenter < pmin) || (pcenter > pmax))
		{
			pcenter = pmax*scenter + pmin*(1-scenter);
		}

		p = clampParam(p);
		if (p < pcenter)
		{
			if (base == 1)
			{
				// (-pmin+p)*scenter/(pcenter-pmin);
				s = (-pmin+p)*scenter/(pcenter-pmin);
			}
			else
			{
				// scenter*(log(B)*k-log(-(-pcenter*B^k+pmin+p*B^k-p)/(pcenter-pmin)))/log(B)/k;
				s = scenter*(log(base)*kexp-log(-(-pcenter*pow(base,kexp)+pmin+p*pow(base,kexp)-p)/(pcenter-pmin)))/log(base)/kexp;
			}
		}
		else
		{
			if (base == 1)
			{
				// (pcenter-scenter*pmax-p+scenter*p)/(-pmax+pcenter);
				s = (pcenter-scenter*pmax-p+scenter*p)/(-pmax+pcenter);
			}
			else
			{
				// (k*log(B)*scenter+log(-(-pcenter*B^k+pmax+p*B^k-p)/(-pmax+pcenter))-log(-(-pcenter*B^k+pmax+p*B^k-p)/(-pmax+pcenter))*scenter)/log(B)/k;
				s = (kexp*log(base)*scenter+log(-(-pcenter*pow(base,kexp)+pmax+p*pow(base,kexp)-p)/(-pmax+pcenter))-log(-(-pcenter*pow(base,kexp)+pmax+p*pow(base,kexp)-p)/(-pmax+pcenter))*scenter)/log(base)/kexp;
			}
		}

		return s;
	}
};
