using UnityEngine;

using System;

namespace com.spacepuppy.Tween
{
    /// <summary>
    /// Represents an eased interpolation w/ respect to time.
    /// 
    /// float t, float b, float c, float d
    /// </summary>
    /// <param name="current">how long into the ease are we</param>
    /// <param name="initialValue">starting value if current were 0</param>
    /// <param name="totalChange">total change in the value (not the end value, but the end - start)</param>
    /// <param name="duration">the total amount of time (when current == duration, the returned value will == initial + totalChange)</param>
    /// <returns></returns>
    public delegate float Ease(float current, float initialValue, float totalChange, float duration);

    public enum EaseStyle : int
    {
        Linear = 0,
        LinearEaseIn = 1,
        LinearEaseOut = 2,
        LinearEaseInOut = 3,
        BackEaseIn = 4,
        BackEaseOut = 5,
        BackEaseInOut = 6,
        BounceEaseIn = 7,
        BounceEaseOut = 8,
        BounceEaseInOut = 9,
        CircleEaseIn = 10,
        CircleEaseOut = 11,
        CircleEaseInOut = 12,
        CubicEaseIn = 13,
        CubicEaseOut = 14,
        CubicEaseInOut = 15,
        ElasticEaseIn = 16,
        ElasticEaseOut = 17,
        ElasticEaseInOut = 18,
        ExpoEaseIn = 19,
        ExpoEaseOut = 20,
        ExpoEaseInOut = 21,
        QuadEaseIn = 22,
        QuadEaseOut = 23,
        QuadEaseInOut = 24,
        QuartEaseIn = 25,
        QuartEaseOut = 26,
        QuartEaseInOut = 27,
        QuintEaseIn = 28,
        QuintEaseOut = 29,
        QuintEaseInOut = 30,
        SineEaseIn = 31,
        SineEaseOut = 32,
        SineEaseInOut = 33,
        StrongEaseIn = 34,
        StrongEaseOut = 35,
        StrongEaseInOut = 36,
    }

    /// <summary>
    /// A set of easing methods, to see a visual representation you can check out:
    /// https://msdn.microsoft.com/en-us/library/vstudio/Ee308751%28v=VS.100%29.aspx
    /// </summary>
    public static class ConcreteEaseMethods
    {

        private const float _2PI = 6.28318530717959f;
        private const float _HALF_PI = 1.5707963267949f;

        #region Back Ease
        public static float BackEaseIn(float t, float b, float c, float d)
        {
            return BackEaseInFull(t, b, c, d);
        }

        public static float BackEaseOut(float t, float b, float c, float d)
        {
            return BackEaseOutFull(t, b, c, d);
        }
        public static float BackEaseInOut(float t, float b, float c, float d)
        {
            return BackEaseInOutFull(t, b, c, d);
        }

        public static float BackEaseInFull(float t, float b, float c, float d, float s = 1.70158f)
        {
            return c * (t /= d) * t * ((s + 1) * t - s) + b;
        }

        public static float BackEaseOutFull(float t, float b, float c, float d, float s = 1.70158f)
        {
            return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
        }
        public static float BackEaseInOutFull(float t, float b, float c, float d, float s = 1.70158f)
        {
            if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525f)) + 1) * t - s)) + b;
            return c / 2 * ((t -= 2) * t * (((s *= (1.525f)) + 1) * t + s) + 2) + b;
        }
        #endregion

        #region Bounce Ease
        public static float BounceEaseOut(float t, float b, float c, float d)
        {
            if ((t /= d) < (1 / 2.75f))
            {
                return c * (7.5625f * t * t) + b;
            }
            else if (t < (2 / 2.75))
            {
                return c * (7.5625f * (t -= (1.5f / 2.75f)) * t + .75f) + b;
            }
            else if (t < (2.5f / 2.75f))
            {
                return c * (7.5625f * (t -= (2.25f / 2.75f)) * t + .9375f) + b;
            }
            else
            {
                return c * (7.5625f * (t -= (2.625f / 2.75f)) * t + .984375f) + b;
            }
        }
        public static float BounceEaseIn(float t, float b, float c, float d)
        {
            return c - BounceEaseOut(d - t, 0, c, d) + b;
        }
        public static float BounceEaseInOut(float t, float b, float c, float d)
        {
            if (t < d / 2) return BounceEaseIn(t * 2, 0, c, d) * .5f + b;
            else return BounceEaseOut(t * 2 - d, 0, c, d) * .5f + c * .5f + b;
        }
        #endregion

        #region Circle Ease
        public static float CircleEaseIn(float t, float b, float c, float d)
        {
            return -c * ((float)Math.Sqrt(1 - (t /= d) * t) - 1) + b;
        }
        public static float CircleEaseOut(float t, float b, float c, float d)
        {
            return c * (float)Math.Sqrt(1 - (t = t / d - 1) * t) + b;
        }
        public static float CircleEaseInOut(float t, float b, float c, float d)
        {
            if ((t /= d / 2) < 1) return -c / 2 * ((float)Math.Sqrt(1 - t * t) - 1) + b;
            return c / 2 * ((float)Math.Sqrt(1 - (t -= 2) * t) + 1) + b;
        }
        #endregion

        #region Cubic Ease

        public static float CubicEaseIn(float t, float b, float c, float d)
        {
            return c * (t /= d) * t * t + b;
        }
        public static float CubicEaseOut(float t, float b, float c, float d)
        {
            return c * ((t = t / d - 1) * t * t + 1) + b;
        }
        public static float CubicEaseInOut(float t, float b, float c, float d)
        {
            if ((t /= d / 2) < 1) return c / 2 * t * t * t + b;
            return c / 2 * ((t -= 2) * t * t + 2) + b;
        }

        #endregion

        #region Elastic Ease
        public static float ElasticEaseIn(float t, float b, float c, float d)
        {
            return ElasticEaseInFull(t, b, c, d, 0, 0);
        }

        public static float ElasticEaseOut(float t, float b, float c, float d)
        {
            return ElasticEaseOutFull(t, b, c, d, 0, 0);
        }

        public static float ElasticEaseInOut(float t, float b, float c, float d)
        {
            return ElasticEaseInOutFull(t, b, c, d, 0, 0);
        }

        public static float ElasticEaseInFull(float t, float b, float c, float d, float a, float p)
        {
            float s;
            if (t == 0) return b; if ((t /= d) == 1) return b + c;
            if (p != 0) p = d * 0.3f;
            if (a != 0 || a < Math.Abs(c)) { a = c; s = p / 4; }
            else s = p / _2PI * (float)Math.Asin(c / a);
            return -(a * (float)Math.Pow(2, 10 * (t -= 1)) * (float)Math.Sin((t * d - s) * _2PI / p)) + b;
        }
        public static float ElasticEaseOutFull(float t, float b, float c, float d, float a = 0, float p = 0)
        {
            float s;
            if (t == 0) return b;
            if ((t /= d) == 1) return b + c;
            if (p != 0) p = d * 0.3f;
            if (a != 0 || a < Math.Abs(c)) { a = c; s = p / 4; }
            else s = p / _2PI * (float)Math.Asin(c / a);
            return (a * (float)Math.Pow(2, -10 * t) * (float)Math.Sin((t * d - s) * _2PI / p) + c + b);
        }
        public static float ElasticEaseInOutFull(float t, float b, float c, float d, float a = 0, float p = 0)
        {
            float s;
            if (t == 0) return b; if ((t /= d / 2) == 2) return b + c;
            if (p != 0) p = d * (0.3f * 1.5f);
            if (a != 0 || a < Math.Abs(c)) { a = c; s = p / 4; }
            else s = p / _2PI * (float)Math.Asin(c / a);
            if (t < 1) return -.5f * (a * (float)Math.Pow(2, 10 * (t -= 1)) * (float)Math.Sin((t * d - s) * _2PI / p)) + b;
            return a * (float)Math.Pow(2, -10 * (t -= 1)) * (float)Math.Sin((t * d - s) * _2PI / p) * .5f + c + b;
        }
        #endregion

        #region Expo Ease
        public static float ExpoEaseIn(float t, float b, float c, float d)
        {
            return (t == 0) ? b : c * (float)Math.Pow(2, 10 * (t / d - 1)) + b - c * 0.001f;
        }
        public static float ExpoEaseOut(float t, float b, float c, float d)
        {
            return (t == d) ? b + c : c * (-(float)Math.Pow(2, -10 * t / d) + 1) + b;
        }
        public static float ExpoEaseInOut(float t, float b, float c, float d)
        {
            if (t == 0) return b;
            if (t == d) return b + c;
            if ((t /= d / 2) < 1) return c / 2 * (float)Math.Pow(2, 10 * (t - 1)) + b;
            return c / 2 * (-(float)Math.Pow(2, -10 * --t) + 2) + b;
        }
        #endregion

        #region Linear Ease
        public static float LinearEaseNone(float t, float b, float c, float d)
        {
            return c * t / d + b;
        }
        public static float LinearEaseIn(float t, float b, float c, float d)
        {
            return c * t / d + b;
        }
        public static float LinearEaseOut(float t, float b, float c, float d)
        {
            return c * t / d + b;
        }
        public static float LinearEaseInOut(float t, float b, float c, float d)
        {
            return c * t / d + b;
        }
        #endregion

        #region Quad Ease
        public static float QuadEaseIn(float t, float b, float c, float d)
        {
            return c * (t /= d) * t + b;
        }
        public static float QuadEaseOut(float t, float b, float c, float d)
        {
            return -c * (t /= d) * (t - 2) + b;
        }
        public static float QuadEaseInOut(float t, float b, float c, float d)
        {
            if ((t /= d / 2) < 1) return c / 2 * t * t + b;
            return -c / 2 * ((--t) * (t - 2) - 1) + b;
        }
        #endregion

        #region Quart Ease
        public static float QuartEaseIn(float t, float b, float c, float d)
        {
            return c * (t /= d) * t * t * t + b;
        }
        public static float QuartEaseOut(float t, float b, float c, float d)
        {
            return -c * ((t = t / d - 1) * t * t * t - 1) + b;
        }
        public static float QuartEaseInOut(float t, float b, float c, float d)
        {
            if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
            return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
        }
        #endregion

        #region Quint Ease
        public static float QuintEaseIn(float t, float b, float c, float d)
        {
            return c * (t /= d) * t * t * t * t + b;
        }
        public static float QuintEaseOut(float t, float b, float c, float d)
        {
            return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
        }
        public static float QuintEaseInOut(float t, float b, float c, float d)
        {
            if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
            return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
        }
        #endregion

        #region Sine Ease
        public static float SineEaseIn(float t, float b, float c, float d)
        {
            return -c * (float)Math.Cos(t / d * _HALF_PI) + c + b;
        }
        public static float SineEaseOut(float t, float b, float c, float d)
        {
            return c * (float)Math.Sin(t / d * _HALF_PI) + b;
        }
        public static float SineEaseInOut(float t, float b, float c, float d)
        {
            return -c / 2 * ((float)Math.Cos(Math.PI * t / d) - 1) + b;
        }
        #endregion

        #region Strong Ease
        public static float StrongEaseIn(float t, float b, float c, float d)
        {
            return c * (t /= d) * t * t * t * t + b;
        }
        public static float StrongEaseOut(float t, float b, float c, float d)
        {
            return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
        }
        public static float StrongEaseInOut(float t, float b, float c, float d)
        {
            if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
            return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
        }
        #endregion
        
    }

    /// <summary>
    /// References to Ease delegates for use, this avoids the garbage of the ease delegate.
    /// </summary>
    public static class EaseMethods
    {

        public static Ease GetEase(EaseStyle style)
        {
            switch (style)
            {
                case EaseStyle.Linear: return EaseMethods.LinearEaseNone;
                case EaseStyle.LinearEaseIn: return EaseMethods.LinearEaseIn;
                case EaseStyle.LinearEaseOut: return EaseMethods.LinearEaseOut;
                case EaseStyle.LinearEaseInOut: return EaseMethods.LinearEaseInOut;

                case EaseStyle.BackEaseIn: return EaseMethods.BackEaseIn;
                case EaseStyle.BackEaseOut: return EaseMethods.BackEaseOut;
                case EaseStyle.BackEaseInOut: return EaseMethods.BackEaseInOut;

                case EaseStyle.BounceEaseIn: return EaseMethods.BounceEaseIn;
                case EaseStyle.BounceEaseOut: return EaseMethods.BounceEaseOut;
                case EaseStyle.BounceEaseInOut: return EaseMethods.BounceEaseInOut;

                case EaseStyle.CircleEaseIn: return EaseMethods.CircleEaseIn;
                case EaseStyle.CircleEaseOut: return EaseMethods.CircleEaseOut;
                case EaseStyle.CircleEaseInOut: return EaseMethods.CircleEaseInOut;

                case EaseStyle.CubicEaseIn: return EaseMethods.CubicEaseIn;
                case EaseStyle.CubicEaseOut: return EaseMethods.CubicEaseOut;
                case EaseStyle.CubicEaseInOut: return EaseMethods.CubicEaseInOut;

                case EaseStyle.ElasticEaseIn: return EaseMethods.ElasticEaseIn;
                case EaseStyle.ElasticEaseOut: return EaseMethods.ElasticEaseOut;
                case EaseStyle.ElasticEaseInOut: return EaseMethods.ElasticEaseInOut;

                case EaseStyle.ExpoEaseIn: return EaseMethods.ExpoEaseIn;
                case EaseStyle.ExpoEaseOut: return EaseMethods.ExpoEaseOut;
                case EaseStyle.ExpoEaseInOut: return EaseMethods.ExpoEaseInOut;

                case EaseStyle.QuadEaseIn: return EaseMethods.QuadEaseIn;
                case EaseStyle.QuadEaseOut: return EaseMethods.QuadEaseOut;
                case EaseStyle.QuadEaseInOut: return EaseMethods.QuadEaseInOut;

                case EaseStyle.QuartEaseIn: return EaseMethods.QuartEaseIn;
                case EaseStyle.QuartEaseOut: return EaseMethods.QuartEaseOut;
                case EaseStyle.QuartEaseInOut: return EaseMethods.QuartEaseInOut;

                case EaseStyle.QuintEaseIn: return EaseMethods.QuintEaseIn;
                case EaseStyle.QuintEaseOut: return EaseMethods.QuintEaseOut;
                case EaseStyle.QuintEaseInOut: return EaseMethods.QuintEaseInOut;

                case EaseStyle.SineEaseIn: return EaseMethods.SineEaseIn;
                case EaseStyle.SineEaseOut: return EaseMethods.SineEaseOut;
                case EaseStyle.SineEaseInOut: return EaseMethods.SineEaseInOut;

                case EaseStyle.StrongEaseIn: return EaseMethods.StrongEaseIn;
                case EaseStyle.StrongEaseOut: return EaseMethods.StrongEaseOut;
                case EaseStyle.StrongEaseInOut: return EaseMethods.StrongEaseInOut;
            }

            return null;
        }

        public static float EasedLerp(Ease ease, float from, float to, float t)
        {
            return ease(t, from, to - from, 1f);
        }



        #region Back Ease

        private static Ease _backEaseIn;
        public static Ease BackEaseIn
        {
            get
            {
                if (_backEaseIn == null) _backEaseIn = ConcreteEaseMethods.BackEaseIn;
                return _backEaseIn;
            }
        }

        private static Ease _backEaseOut;
        public static Ease BackEaseOut
        {
            get
            {
                if (_backEaseOut == null) _backEaseOut = ConcreteEaseMethods.BackEaseOut;
                return _backEaseOut;
            }
        }

        private static Ease _backEaseInOut;
        public static Ease BackEaseInOut
        {
            get
            {
                if (_backEaseInOut == null) _backEaseInOut = ConcreteEaseMethods.BackEaseInOut;
                return _backEaseInOut;
            }
        }

        #endregion

        #region Bounce Ease

        private static Ease _bounceEaseIn;
        public static Ease BounceEaseIn
        {
            get
            {
                if (_bounceEaseIn == null) _bounceEaseIn = ConcreteEaseMethods.BounceEaseIn;
                return _bounceEaseIn;
            }
        }

        private static Ease _bounceEaseOut;
        public static Ease BounceEaseOut
        {
            get
            {
                if (_bounceEaseOut == null) _bounceEaseOut = ConcreteEaseMethods.BounceEaseOut;
                return _bounceEaseOut;
            }
        }

        private static Ease _bounceEaseInOut;
        public static Ease BounceEaseInOut
        {
            get
            {
                if (_bounceEaseInOut == null) _bounceEaseInOut = ConcreteEaseMethods.BounceEaseInOut;
                return _bounceEaseInOut;
            }
        }

        #endregion

        #region Circle Ease

        private static Ease _circleEaseIn;
        public static Ease CircleEaseIn
        {
            get
            {
                if (_circleEaseIn == null) _circleEaseIn = ConcreteEaseMethods.CircleEaseIn;
                return _circleEaseIn;
            }
        }

        private static Ease _circleEaseOut;
        public static Ease CircleEaseOut
        {
            get
            {
                if (_circleEaseOut == null) _circleEaseOut = ConcreteEaseMethods.CircleEaseOut;
                return _circleEaseOut;
            }
        }

        private static Ease _circleEaseInOut;
        public static Ease CircleEaseInOut
        {
            get
            {
                if (_circleEaseInOut == null) _circleEaseInOut = ConcreteEaseMethods.CircleEaseInOut;
                return _circleEaseInOut;
            }
        }

        #endregion

        #region Cubic Ease

        private static Ease _cubicEaseIn;
        public static Ease CubicEaseIn
        {
            get
            {
                if (_cubicEaseIn == null) _cubicEaseIn = ConcreteEaseMethods.CubicEaseIn;
                return _cubicEaseIn;
            }
        }

        private static Ease _cubicEaseOut;
        public static Ease CubicEaseOut
        {
            get
            {
                if (_cubicEaseOut == null) _cubicEaseOut = ConcreteEaseMethods.CubicEaseOut;
                return _cubicEaseOut;
            }
        }

        private static Ease _cubicEaseInOut;
        public static Ease CubicEaseInOut
        {
            get
            {
                if (_cubicEaseInOut == null) _cubicEaseInOut = ConcreteEaseMethods.CubicEaseInOut;
                return _cubicEaseInOut;
            }
        }

        #endregion

        #region Elastic Ease

        private static Ease _elasticEaseIn;
        public static Ease ElasticEaseIn
        {
            get
            {
                if (_elasticEaseIn == null) _elasticEaseIn = ConcreteEaseMethods.ElasticEaseIn;
                return _elasticEaseIn;
            }
        }

        private static Ease _elasticEaseOut;
        public static Ease ElasticEaseOut
        {
            get
            {
                if (_elasticEaseOut == null) _elasticEaseOut = ConcreteEaseMethods.ElasticEaseOut;
                return _elasticEaseOut;
            }
        }

        private static Ease _elasticEaseInOut;
        public static Ease ElasticEaseInOut
        {
            get
            {
                if (_elasticEaseInOut == null) _elasticEaseInOut = ConcreteEaseMethods.ElasticEaseInOut;
                return _elasticEaseInOut;
            }
        }

        #endregion

        #region Expo Ease

        private static Ease _expoEaseIn;
        public static Ease ExpoEaseIn
        {
            get
            {
                if (_expoEaseIn == null) _expoEaseIn = ConcreteEaseMethods.ExpoEaseIn;
                return _expoEaseIn;
            }
        }

        private static Ease _expoEaseOut;
        public static Ease ExpoEaseOut
        {
            get
            {
                if (_expoEaseOut == null) _expoEaseOut = ConcreteEaseMethods.ExpoEaseOut;
                return _expoEaseOut;
            }
        }

        private static Ease _expoEaseInOut;
        public static Ease ExpoEaseInOut
        {
            get
            {
                if (_expoEaseInOut == null) _expoEaseInOut = ConcreteEaseMethods.ExpoEaseInOut;
                return _expoEaseInOut;
            }
        }

        #endregion

        #region Linear Ease

        private static Ease _linearEaseNone;
        public static Ease LinearEaseNone
        {
            get
            {
                if (_linearEaseNone == null) _linearEaseNone = ConcreteEaseMethods.LinearEaseNone;
                return _linearEaseNone;
            }
        }

        private static Ease _linearEaseIn;
        public static Ease LinearEaseIn
        {
            get
            {
                if (_linearEaseIn == null) _linearEaseIn = ConcreteEaseMethods.LinearEaseIn;
                return _linearEaseIn;
            }
        }

        private static Ease _linearEaseOut;
        public static Ease LinearEaseOut
        {
            get
            {
                if (_linearEaseOut == null) _linearEaseOut = ConcreteEaseMethods.LinearEaseOut;
                return _linearEaseOut;
            }
        }

        private static Ease _linearEaseInOut;
        public static Ease LinearEaseInOut
        {
            get
            {
                if (_linearEaseInOut == null) _linearEaseInOut = ConcreteEaseMethods.LinearEaseInOut;
                return _linearEaseInOut;
            }
        }

        #endregion

        #region Quad Ease

        private static Ease _quadEaseIn;
        public static Ease QuadEaseIn
        {
            get
            {
                if (_quadEaseIn == null) _quadEaseIn = ConcreteEaseMethods.QuadEaseIn;
                return _quadEaseIn;
            }
        }

        private static Ease _quadEaseOut;
        public static Ease QuadEaseOut
        {
            get
            {
                if (_quadEaseOut == null) _quadEaseOut = ConcreteEaseMethods.QuadEaseOut;
                return _quadEaseOut;
            }
        }

        private static Ease _quadEaseInOut;
        public static Ease QuadEaseInOut
        {
            get
            {
                if (_quadEaseInOut == null) _quadEaseInOut = ConcreteEaseMethods.QuadEaseInOut;
                return _quadEaseInOut;
            }
        }

        #endregion

        #region Quart Ease

        private static Ease _quartEaseIn;
        public static Ease QuartEaseIn
        {
            get
            {
                if (_quartEaseIn == null) _quartEaseIn = ConcreteEaseMethods.QuartEaseIn;
                return _quartEaseIn;
            }
        }

        private static Ease _quartEaseOut;
        public static Ease QuartEaseOut
        {
            get
            {
                if (_quartEaseOut == null) _quartEaseOut = ConcreteEaseMethods.QuartEaseOut;
                return _quartEaseOut;
            }
        }

        private static Ease _quartEaseInOut;
        public static Ease QuartEaseInOut
        {
            get
            {
                if (_quartEaseInOut == null) _quartEaseInOut = ConcreteEaseMethods.QuartEaseInOut;
                return _quartEaseInOut;
            }
        }

        #endregion

        #region Quint Ease

        private static Ease _quintEaseIn;
        public static Ease QuintEaseIn
        {
            get
            {
                if (_quintEaseIn == null) _quintEaseIn = ConcreteEaseMethods.QuintEaseIn;
                return _quintEaseIn;
            }
        }

        private static Ease _quintEaseOut;
        public static Ease QuintEaseOut
        {
            get
            {
                if (_quintEaseOut == null) _quintEaseOut = ConcreteEaseMethods.QuintEaseOut;
                return _quintEaseOut;
            }
        }

        private static Ease _quintEaseInOut;
        public static Ease QuintEaseInOut
        {
            get
            {
                if (_quintEaseInOut == null) _quintEaseInOut = ConcreteEaseMethods.QuintEaseInOut;
                return _quintEaseInOut;
            }
        }

        #endregion

        #region Sine Ease

        private static Ease _sineEaseIn;
        public static Ease SineEaseIn
        {
            get
            {
                if (_sineEaseIn == null) _sineEaseIn = ConcreteEaseMethods.SineEaseIn;
                return _sineEaseIn;
            }
        }

        private static Ease _sineEaseOut;
        public static Ease SineEaseOut
        {
            get
            {
                if (_sineEaseOut == null) _sineEaseOut = ConcreteEaseMethods.SineEaseOut;
                return _sineEaseOut;
            }
        }

        private static Ease _sineEaseInOut;
        public static Ease SineEaseInOut
        {
            get
            {
                if (_sineEaseInOut == null) _sineEaseInOut = ConcreteEaseMethods.SineEaseInOut;
                return _sineEaseInOut;
            }
        }

        #endregion

        #region Strong Ease

        private static Ease _strongEaseIn;
        public static Ease StrongEaseIn
        {
            get
            {
                if (_strongEaseIn == null) _strongEaseIn = ConcreteEaseMethods.StrongEaseIn;
                return _strongEaseIn;
            }
        }

        private static Ease _strongEaseOut;
        public static Ease StrongEaseOut
        {
            get
            {
                if (_strongEaseOut == null) _strongEaseOut = ConcreteEaseMethods.StrongEaseOut;
                return _strongEaseOut;
            }
        }

        private static Ease _strongEaseInOut;
        public static Ease StrongEaseInOut
        {
            get
            {
                if (_strongEaseInOut == null) _strongEaseInOut = ConcreteEaseMethods.StrongEaseInOut;
                return _strongEaseInOut;
            }
        }

        #endregion

        #region AnimationCurve

        public static Ease FromAnimationCurve(AnimationCurve curve)
        {
            return (c, s, e, d) =>
            {
                return curve.Evaluate(c);
            };
        }

        #endregion

        #region Configurable Cubic Bezier

        public static Ease CubicBezier(float p0, float p1, float p2, float p3)
        {
            return (c, s, e, d) =>
            {
                var t = c / d;
                var it = 1f - t;
                var r = (Mathf.Pow(it, 3f) * p0)
                      + (3f * Mathf.Pow(it, 2f) * t * p1)
                      + (3f * it * Mathf.Pow(t, 2f) * p2)
                      + (Mathf.Pow(t, 3f) * p3);
                return s + e * r;
            };
        }

        #endregion








        public static Vector2 EaseVector2(Ease ease, Vector2 start, Vector2 end, float t, float dur)
        {
            return (ease(t, 0, 1, dur) * (end - start)) + start;

            //return new Vector2(ease(t, start.x, end.x - start.x, dur), ease(t, start.y, end.y - start.y, dur));
        }

        public static Vector3 EaseVector3(Ease ease, Vector3 start, Vector3 end, float t, float dur)
        {
            return (ease(t, 0, 1, dur) * (end - start)) + start;

            //return new Vector3(ease(t, start.x, end.x - start.x, dur), ease(t, start.y, end.y - start.y, dur), ease(t, start.z, end.z - start.z, dur));
        }

        public static Vector4 EaseVector3(Ease ease, Vector4 start, Vector4 end, float t, float dur)
        {
            return (ease(t, 0, 1, dur) * (end - start)) + start;

            //return new Vector4(ease(t, start.x, end.x - start.x, dur), ease(t, start.y, end.y - start.y, dur), ease(t, start.z, end.z - start.z, dur), ease(t, start.w, end.w - start.w, dur));
        }

        public static Quaternion EaseQuaternion(Ease ease, Quaternion start, Quaternion end, float t, float dur)
        {
            return Quaternion.Slerp(start, end, ease(t, 0, 1, dur));
        }
    }

}
