using System.Numerics;
using System.Runtime.CompilerServices;

namespace Voile
{
    public static class MathUtils
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        public static double LerpUnclamped(double a, double b, double t) => (a * (1d - t)) + (b * t);
        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        public static float LerpUnclamped(float a, float b, float t) => (a * (1f - t)) + (b * t);

        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        public static double Lerp(double a, double b, double t) => t <= 0d ? a : t >= 1d ? b : LerpUnclamped(a, b, t);
        [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
        public static float Lerp(float a, float b, float t) => t <= 0f ? a : t >= 1f ? b : LerpUnclamped(a, b, t);

        public static Size LerpSize(Size a, Size b, float t)
        {
            t = Math.Clamp(t, 0f, 1f);

            float left = Lerp(a.Left, b.Left, t);
            float right = Lerp(a.Right, b.Right, t);
            float top = Lerp(a.Top, b.Top, t);
            float bottom = Lerp(a.Bottom, b.Bottom, t);

            return new Size(left, right, top, bottom);
        }

        public static Color LerpColor(Color colorA, Color colorB, float t)
        {
            t = Math.Clamp(t, 0f, 1f);

            byte r = (byte)Lerp(colorA.R, colorB.R, t);
            byte g = (byte)Lerp(colorA.G, colorB.G, t);
            byte b = (byte)Lerp(colorA.B, colorB.B, t);
            byte a = (byte)Lerp(colorA.A, colorB.A, t);

            return new Color(r, g, b, a);
        }

        public static Vector2 RandomVector2(Vector2 min, Vector2 max)
        {
            var x = _random.NextDouble(min.X, max.X);
            var y = _random.NextDouble(min.Y, max.Y);

            return new Vector2((float)x, (float)y);
        }

        public static Color RandomColor()
        {
            var r = _random.NextDouble(0.0f, 1.0f);
            var g = _random.NextDouble(0.0f, 1.0f);
            var b = _random.NextDouble(0.0f, 1.0f);

            return new Color((float)r, (float)g, (float)b);
        }

        public static float EaseOutBack(float x)
        {
            var c1 = 1.70158f;
            var c3 = c1 + 1f;

            return 1f + c3 * (float)Math.Pow(x - 1f, 3f) + c1 * (float)Math.Pow(x - 1, 2);
        }

        public static float EaseOutElastic(float x)
        {
            var c4 = 2f * (float)Math.PI / 3f;

            return x == 0
            ? 0
            : x == 1
            ? 1
            : (float)Math.Pow(2, -10 * x) * (float)Math.Sin((x * 10 - 0.75f) * c4) + 1;
        }

        private static LehmerRandom _random = new();
    }
}