#pragma once
#include <cmath>
#include <functional>
#include <algorithm>

class AudioMath
{
public:
    AudioMath() = delete;       // we are only static
    static const double Pi;
    static const double Pi_2;       // Pi / 2
    static const double Ln2;
    static const double Ln10;
    static const double E;

    static bool closeTo(double x, double y, double tolerance)
    {
        const bool ret = std::abs(x - y) < tolerance;
        return ret;
    }

    static double db(double g)
    {
        return 20 * log(g) / Ln10;
    }

    static double gainFromDb(double db)
    {
        return std::exp(Ln10 * db / 20.0);
    }

    /**
     * Returns a function that generates one period of sin for x = {0..1}.
     * Range (output) is -1 to 1.
     */
    static std::function<double(double)> makeFunc_Sin();

    /*
     * Returns a function that generates an exponential defined by two points
     * At input = xMin, output will be yMin.
     * At input = xMax, output will be yMax.
     */
    static std::function<double(double)> makeFunc_Exp(double xMin, double xMax, double yMin, double yMax);

    /**
     * Returns a function for an "audio taper" attenuator gain.
     * function is pure exponential for x > .25, linear for x < .25
     * At input 1, output is 1
     * At input .25, output is the gain corresponding to adAtten
     * At input 0, the output is zero.
     */
    static std::function<double(double)> makeFunc_AudioTaper(double dbAtten);

    /**
     * ScaleFun is a function the combines CV, knob, and trim into a voltage.
     * Typically a ScaleFun is like an "attenuverter"
     */
    template <typename T>
    using ScaleFun = std::function<T(T cv, T knob, T trim)>;

    /**
     * Create a ScaleFun with the following properties:
     * 1) The values are combined with the typical formula: x = cv * trim + knob;
     * 2) x is clipped between -5 and 5
     * 3) range is then interpolated between y0, and y1.
     *
     * This particular function is used when knobs are -5..5,
     * and CV range is -5..5.
     */
    template <typename T>
    static ScaleFun<T> makeLinearScaler(T y0, T y1)
    {
        const T x0 = -5;
        const T x1 = 5;
        const T a = (y1 - y0) / (x1 - x0);
        const T b = y0 - a * x0;
        return [a, b](T cv, T knob, T trim) {
            T x = cv * trim + knob;
            x = std::max<T>(-5.0f, x);
            x = std::min(5.0f, x);
            return a * x + b;
        };
    }


    static ScaleFun<float> makeBipolarAudioScaler(float y0, float y1);
   

};
