using System;
using UnityEngine;

namespace Davanci.Nexus
{
    // Procedural textures for the splash, generated once at runtime so the package ships no
    // extra art and every shape stays crisp at any resolution.
    static class NexusSplashTextures
    {
        public static Texture2D SoftDot { get; private set; }
        public static Texture2D Disc { get; private set; }
        public static Texture2D Ring { get; private set; }
        public static Texture2D Shine { get; private set; }
        public static Texture2D Grid { get; private set; }
        public static Texture2D Vignette { get; private set; }
        public static Sprite Pill { get; private set; }

        // Pill cap radius in texture pixels; the sprite is 9-sliced on it.
        public const float PillRadius = 16f;

        public static void Ensure()
        {
            if (SoftDot != null) return;

            SoftDot = Radial(64, r => Mathf.Pow(Mathf.Clamp01(1f - r), 2.2f));
            Disc = Radial(64, r => Mathf.Clamp01((1f - r) * 32f));
            Ring = Radial(256, r => Gauss(r, 0.86f, 0.035f) + 0.35f * Gauss(r, 0.84f, 0.12f));
            Vignette = Radial(128, r => Mathf.Pow(1f - Mathf.SmoothStep(0f, 1f, Mathf.Clamp01(r)), 1.6f));
            Shine = Paint(64, 8, TextureWrapMode.Clamp, (x, y) => Gauss((x + 0.5f) / 64f, 0.5f, 0.2f));
            Grid = Paint(32, 32, TextureWrapMode.Repeat, (x, y) =>
            {
                float dx = x + 0.5f - 16f, dy = y + 0.5f - 16f;
                return Mathf.Clamp01(2.1f - Mathf.Sqrt(dx * dx + dy * dy));
            });

            var pill = Paint(64, 32, TextureWrapMode.Clamp, (x, y) =>
            {
                float px = Mathf.Clamp(x + 0.5f, PillRadius, 64f - PillRadius);
                float dx = x + 0.5f - px, dy = y + 0.5f - PillRadius;
                return Mathf.Clamp01(PillRadius - Mathf.Sqrt(dx * dx + dy * dy) + 0.5f);
            });
            Pill = Sprite.Create(pill, new Rect(0, 0, 64, 32), new Vector2(0.5f, 0.5f), 100f, 0,
                SpriteMeshType.FullRect, new Vector4(PillRadius, 0, PillRadius, 0));
            Pill.hideFlags = HideFlags.DontSave;
        }

        public static void Release()
        {
            if (Pill != null) { Destroy(Pill.texture); Destroy(Pill); }
            Destroy(SoftDot); Destroy(Disc); Destroy(Ring); Destroy(Shine); Destroy(Grid); Destroy(Vignette);
            SoftDot = Disc = Ring = Shine = Grid = Vignette = null;
            Pill = null;
        }

        static float Gauss(float x, float centre, float width)
        {
            float d = (x - centre) / width;
            return Mathf.Exp(-d * d);
        }

        // Alpha as a function of normalised distance from the centre (0 centre, 1 edge).
        static Texture2D Radial(int size, Func<float, float> alpha)
        {
            float half = size * 0.5f;
            return Paint(size, size, TextureWrapMode.Clamp, (x, y) =>
            {
                float dx = (x + 0.5f - half) / half, dy = (y + 0.5f - half) / half;
                return alpha(Mathf.Sqrt(dx * dx + dy * dy));
            });
        }

        static Texture2D Paint(int width, int height, TextureWrapMode wrap, Func<int, int, float> alpha)
        {
            var tex = new Texture2D(width, height, TextureFormat.RGBA32, false)
            {
                wrapMode = wrap,
                filterMode = FilterMode.Bilinear,
                hideFlags = HideFlags.DontSave,
            };

            var pixels = new Color32[width * height];
            for (int y = 0; y < height; y++)
                for (int x = 0; x < width; x++)
                    pixels[y * width + x] = new Color32(255, 255, 255, (byte)(Mathf.Clamp01(alpha(x, y)) * 255f));

            tex.SetPixels32(pixels);
            tex.Apply(false, true);
            return tex;
        }

        static void Destroy(UnityEngine.Object o)
        {
            if (o != null) UnityEngine.Object.Destroy(o);
        }
    }
}
