using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Theriapolis.Game.CodexUI.Core;

/// <summary>
/// Strongly-typed asset table for the codex aesthetic. Each property is a
/// texture used by one or more widgets — see §6 of the implementation plan
/// for the full list. <see cref="LoadAll"/> looks for PNGs under
/// <c>Content/Gfx/codex/</c> first; any missing slots fall back to a
/// procedural placeholder generated in code so the screen still renders
/// before art is authored.
///
/// Procedural placeholders aim to capture *structure* (9-slice shape,
/// corner/edge sizes, broad colour) so layout work can proceed against
/// them. Final art replaces them one by one.
/// </summary>
public sealed class CodexAtlas
{
    public Texture2D Pixel             = null!;  // 1×1 white — flat-fill helper

    public Texture2D ParchmentBg       = null!;  // 256×256 tileable
    public Texture2D ParchmentCard     = null!;  // 96×96 9-slice (24 px corners)
    public Texture2D GildFrame         = null!;  // 64×64 9-slice card border
    public Texture2D GildFrameSelected = null!;  // 64×64 9-slice — heavier gild
    public Texture2D GildButtonPrimary = null!;  // 96×32 9-slice
    public Texture2D InkButtonGhost    = null!;  // 96×32 9-slice
    public Texture2D WaxSeal           = null!;  // 64×64 sprite
    public Texture2D OrnamentDiamond   = null!;  // 16×16 sprite

    public Texture2D StepperLocked    = null!;
    public Texture2D StepperActive    = null!;
    public Texture2D StepperDone      = null!;

    public Texture2D ChipTrait        = null!;
    public Texture2D ChipSkillBg      = null!;
    public Texture2D ChipSkillClass   = null!;
    public Texture2D ChipLanguage     = null!;

    public Texture2D PoolDie          = null!;
    public Texture2D SlotEmpty        = null!;
    public Texture2D SlotFilled       = null!;
    public Texture2D BarTrack         = null!;
    public Texture2D BarFill          = null!;
    public Texture2D PopoverBg        = null!;

    public Texture2D CladeSigilCanidae    = null!;
    public Texture2D CladeSigilFelidae    = null!;
    public Texture2D CladeSigilMustelidae = null!;
    public Texture2D CladeSigilUrsidae    = null!;
    public Texture2D CladeSigilCervidae   = null!;
    public Texture2D CladeSigilBovidae    = null!;
    public Texture2D CladeSigilLeporidae  = null!;

    public void LoadAll(GraphicsDevice gd, string contentRoot)
    {
        Pixel = new Texture2D(gd, 1, 1);
        Pixel.SetData(new[] { Color.White });

        string codexDir = System.IO.Path.Combine(contentRoot, "codex");

        ParchmentBg       = LoadOrFallback(gd, codexDir, "parchment_bg.png",       () => MakeParchmentTile(gd, 256, 256));
        ParchmentCard     = LoadOrFallback(gd, codexDir, "parchment_card.png",     () => MakeParchmentCard(gd, 96, 96));
        GildFrame         = LoadOrFallback(gd, codexDir, "gild_frame.png",         () => MakeGildFrame(gd, 64, 64, selected: false));
        GildFrameSelected = LoadOrFallback(gd, codexDir, "gild_frame_selected.png",() => MakeGildFrame(gd, 64, 64, selected: true));
        GildButtonPrimary = LoadOrFallback(gd, codexDir, "gild_button_primary.png",() => MakeButton(gd, 96, 32, fill: CodexColors.Seal,  border: CodexColors.Seal2));
        InkButtonGhost    = LoadOrFallback(gd, codexDir, "ink_button_ghost.png",   () => MakeButton(gd, 96, 32, fill: CodexColors.Bg,    border: CodexColors.Ink));
        WaxSeal           = LoadOrFallback(gd, codexDir, "wax_seal.png",           () => MakeWaxSeal(gd, 64, 64));
        OrnamentDiamond   = LoadOrFallback(gd, codexDir, "ornament_diamond.png",   () => MakeOrnamentDiamond(gd, 16, 16));

        StepperLocked = LoadOrFallback(gd, codexDir, "stepper_bullet_locked.png", () => MakeStepperBullet(gd, 24, 24, CodexColors.InkMute, CodexColors.Bg));
        StepperActive = LoadOrFallback(gd, codexDir, "stepper_bullet_active.png", () => MakeStepperBullet(gd, 24, 24, CodexColors.Gild,    CodexColors.Bg));
        StepperDone   = LoadOrFallback(gd, codexDir, "stepper_bullet_done.png",   () => MakeStepperBullet(gd, 24, 24, CodexColors.Seal,    CodexColors.Bg));

        ChipTrait      = LoadOrFallback(gd, codexDir, "chip_trait.png",       () => MakeChip(gd, 96, 24, CodexColors.Bg, CodexColors.Gild));
        ChipSkillBg    = LoadOrFallback(gd, codexDir, "chip_skill_bg.png",    () => MakeChip(gd, 96, 24, CodexColors.Bg, CodexColors.Gild));
        ChipSkillClass = LoadOrFallback(gd, codexDir, "chip_skill_class.png", () => MakeChip(gd, 96, 24, CodexColors.Bg, CodexColors.Seal));
        ChipLanguage   = LoadOrFallback(gd, codexDir, "chip_language.png",    () => MakeChip(gd, 96, 24, CodexColors.Bg, CodexColors.Rule));

        PoolDie     = LoadOrFallback(gd, codexDir, "pool_die.png",   () => MakeButton(gd, 56, 56, CodexColors.Bg,           CodexColors.Rule));
        SlotEmpty   = LoadOrFallback(gd, codexDir, "slot_empty.png", () => MakeSlot(gd, 64, 44, dashed: true));
        SlotFilled  = LoadOrFallback(gd, codexDir, "slot_filled.png",() => MakeSlot(gd, 64, 44, dashed: false));
        BarTrack    = LoadOrFallback(gd, codexDir, "bar_track.png",  () => MakeBar(gd, 16, 8, fill: false));
        BarFill     = LoadOrFallback(gd, codexDir, "bar_fill.png",   () => MakeBar(gd, 16, 8, fill: true));
        PopoverBg   = LoadOrFallback(gd, codexDir, "popover_bg.png", () => MakePopoverBg(gd, 96, 96));

        // Clade sigils — placeholder = circular badge with stylised initial
        CladeSigilCanidae    = LoadOrFallback(gd, codexDir, "clade_sigil_canidae.png",    () => MakeSigil(gd, 48, 'C'));
        CladeSigilFelidae    = LoadOrFallback(gd, codexDir, "clade_sigil_felidae.png",    () => MakeSigil(gd, 48, 'F'));
        CladeSigilMustelidae = LoadOrFallback(gd, codexDir, "clade_sigil_mustelidae.png", () => MakeSigil(gd, 48, 'M'));
        CladeSigilUrsidae    = LoadOrFallback(gd, codexDir, "clade_sigil_ursidae.png",    () => MakeSigil(gd, 48, 'U'));
        CladeSigilCervidae   = LoadOrFallback(gd, codexDir, "clade_sigil_cervidae.png",   () => MakeSigil(gd, 48, 'D'));
        CladeSigilBovidae    = LoadOrFallback(gd, codexDir, "clade_sigil_bovidae.png",    () => MakeSigil(gd, 48, 'B'));
        CladeSigilLeporidae  = LoadOrFallback(gd, codexDir, "clade_sigil_leporidae.png",  () => MakeSigil(gd, 48, 'L'));
    }

    public Texture2D SigilFor(string cladeId) => cladeId switch
    {
        "canidae"    => CladeSigilCanidae,
        "felidae"    => CladeSigilFelidae,
        "mustelidae" => CladeSigilMustelidae,
        "ursidae"    => CladeSigilUrsidae,
        "cervidae"   => CladeSigilCervidae,
        "bovidae"    => CladeSigilBovidae,
        "leporidae"  => CladeSigilLeporidae,
        _            => CladeSigilCanidae,
    };

    private static Texture2D LoadOrFallback(GraphicsDevice gd, string dir, string name, System.Func<Texture2D> fallback)
    {
        string path = System.IO.Path.Combine(dir, name);
        if (System.IO.File.Exists(path))
        {
            using var fs = System.IO.File.OpenRead(path);
            return Texture2D.FromStream(gd, fs);
        }
        return fallback();
    }

    // ── Procedural placeholder generators ────────────────────────────────
    // Each returns a Texture2D the matching widget can use. They aim for
    // structural correctness (right size, right 9-slice insets) rather
    // than the final illuminated-codex aesthetic.

    private static Texture2D MakeParchmentTile(GraphicsDevice gd, int w, int h)
    {
        var pixels = new Color[w * h];
        var rng = new System.Random(0xC0DE);
        for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            // Two layered radial gradients + grain noise for parchment feel.
            float dx1 = (x - w * 0.3f) / (w * 0.6f);
            float dy1 = (y - h * 0.2f) / (h * 0.4f);
            float light = MathHelper.Clamp(1f - (dx1 * dx1 + dy1 * dy1), 0f, 1f) * 0.18f;

            float dx2 = (x - w * 0.8f) / (w * 0.45f);
            float dy2 = (y - h * 0.8f) / (h * 0.35f);
            float shade = MathHelper.Clamp(1f - (dx2 * dx2 + dy2 * dy2), 0f, 1f) * 0.10f;

            float grain = (float)(rng.NextDouble() - 0.5) * 0.04f;
            float r = CodexColors.Bg.R / 255f + light - shade + grain;
            float g = CodexColors.Bg.G / 255f + light - shade + grain;
            float b = CodexColors.Bg.B / 255f + light - shade * 1.3f + grain;
            pixels[y * w + x] = new Color(MathHelper.Clamp(r, 0f, 1f), MathHelper.Clamp(g, 0f, 1f), MathHelper.Clamp(b, 0f, 1f));
        }
        var tex = new Texture2D(gd, w, h);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakeParchmentCard(GraphicsDevice gd, int w, int h)
    {
        var pixels = new Color[w * h];
        for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            // Slight top-down lighten as in `linear-gradient(180deg, rgba(255,250,235,0.05), transparent 30%)`.
            float topLift = MathHelper.Clamp(1f - y / (h * 0.3f), 0f, 1f) * 0.04f;
            var c = CodexColors.Bg2;
            pixels[y * w + x] = new Color(
                MathHelper.Clamp(c.R / 255f + topLift, 0f, 1f),
                MathHelper.Clamp(c.G / 255f + topLift, 0f, 1f),
                MathHelper.Clamp(c.B / 255f + topLift, 0f, 1f));
        }
        var tex = new Texture2D(gd, w, h);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakeGildFrame(GraphicsDevice gd, int w, int h, bool selected)
    {
        var pixels = new Color[w * h];
        var border = selected ? CodexColors.Seal : CodexColors.Rule;
        var glow   = selected ? CodexColors.CardSelectedHalo : CodexColors.CardHoverHalo;
        int thickness = selected ? 2 : 1;
        // 1-px border around the edge, plus an inner glow stripe when selected.
        for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            int distFromEdge = System.Math.Min(System.Math.Min(x, y), System.Math.Min(w - 1 - x, h - 1 - y));
            if (distFromEdge < thickness)               pixels[y * w + x] = border;
            else if (selected && distFromEdge < thickness + 2) pixels[y * w + x] = glow;
            else                                        pixels[y * w + x] = Color.Transparent;
        }
        var tex = new Texture2D(gd, w, h);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakeButton(GraphicsDevice gd, int w, int h, Color fill, Color border)
    {
        var pixels = new Color[w * h];
        for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            int distFromEdge = System.Math.Min(System.Math.Min(x, y), System.Math.Min(w - 1 - x, h - 1 - y));
            pixels[y * w + x] = distFromEdge < 1 ? border : fill;
        }
        var tex = new Texture2D(gd, w, h);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakeWaxSeal(GraphicsDevice gd, int w, int h)
    {
        var pixels = new Color[w * h];
        float cx = w / 2f, cy = h / 2f, r = w / 2f - 2;
        for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            float dx = x - cx, dy = y - cy;
            float d = (float)System.Math.Sqrt(dx * dx + dy * dy);
            if (d > r)            pixels[y * w + x] = Color.Transparent;
            else if (d > r - 1.5) pixels[y * w + x] = CodexColors.Seal2;
            else                  pixels[y * w + x] = Color.Lerp(CodexColors.Seal, CodexColors.Seal2, d / r);
        }
        var tex = new Texture2D(gd, w, h);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakeOrnamentDiamond(GraphicsDevice gd, int w, int h)
    {
        var pixels = new Color[w * h];
        float cx = w / 2f, cy = h / 2f;
        for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            float manhat = System.Math.Abs(x - cx) + System.Math.Abs(y - cy);
            pixels[y * w + x] = manhat <= cx ? CodexColors.Gild : Color.Transparent;
        }
        var tex = new Texture2D(gd, w, h);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakeStepperBullet(GraphicsDevice gd, int size, int _, Color border, Color fill)
    {
        var pixels = new Color[size * size];
        float cx = size / 2f, cy = size / 2f, r = size / 2f - 1;
        for (int y = 0; y < size; y++)
        for (int x = 0; x < size; x++)
        {
            float dx = x - cx, dy = y - cy;
            float d = (float)System.Math.Sqrt(dx * dx + dy * dy);
            if (d > r)         pixels[y * size + x] = Color.Transparent;
            else if (d > r - 1.4) pixels[y * size + x] = border;
            else               pixels[y * size + x] = fill;
        }
        var tex = new Texture2D(gd, size, size);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakeChip(GraphicsDevice gd, int w, int h, Color fill, Color border)
        => MakeButton(gd, w, h, fill, border);

    private static Texture2D MakeSlot(GraphicsDevice gd, int w, int h, bool dashed)
    {
        var pixels = new Color[w * h];
        var border = dashed ? CodexColors.InkMute : CodexColors.InkSoft;
        var fill   = dashed ? Color.Transparent : new Color(180, 138, 60, 16);
        for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            int distFromEdge = System.Math.Min(System.Math.Min(x, y), System.Math.Min(w - 1 - x, h - 1 - y));
            bool onEdge = distFromEdge < 1;
            if (onEdge)
            {
                if (dashed)
                {
                    int along = (x == 0 || x == w - 1) ? y : x;
                    pixels[y * w + x] = (along / 3) % 2 == 0 ? border : Color.Transparent;
                }
                else
                {
                    pixels[y * w + x] = border;
                }
            }
            else pixels[y * w + x] = fill;
        }
        var tex = new Texture2D(gd, w, h);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakeBar(GraphicsDevice gd, int w, int h, bool fill)
    {
        var pixels = new Color[w * h];
        for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            int distFromEdge = System.Math.Min(System.Math.Min(x, y), System.Math.Min(w - 1 - x, h - 1 - y));
            if (distFromEdge < 1) pixels[y * w + x] = CodexColors.Rule;
            else                  pixels[y * w + x] = fill ? CodexColors.Gild : CodexColors.Bg;
        }
        var tex = new Texture2D(gd, w, h);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakePopoverBg(GraphicsDevice gd, int w, int h)
    {
        var pixels = new Color[w * h];
        for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            int distFromEdge = System.Math.Min(System.Math.Min(x, y), System.Math.Min(w - 1 - x, h - 1 - y));
            if      (distFromEdge < 1) pixels[y * w + x] = CodexColors.Gild;
            else                       pixels[y * w + x] = CodexColors.Bg2;
        }
        var tex = new Texture2D(gd, w, h);
        tex.SetData(pixels);
        return tex;
    }

    private static Texture2D MakeSigil(GraphicsDevice gd, int size, char letter)
    {
        var pixels = new Color[size * size];
        float cx = size / 2f, cy = size / 2f, r = size / 2f - 1;
        for (int y = 0; y < size; y++)
        for (int x = 0; x < size; x++)
        {
            float dx = x - cx, dy = y - cy;
            float d = (float)System.Math.Sqrt(dx * dx + dy * dy);
            if (d > r)         pixels[y * size + x] = Color.Transparent;
            else if (d > r - 1.5) pixels[y * size + x] = CodexColors.Rule;
            else               pixels[y * size + x] = Color.Lerp(CodexColors.Bg, CodexColors.Bg2, d / r);
        }
        var tex = new Texture2D(gd, size, size);
        tex.SetData(pixels);
        // Note: the letter glyph itself is drawn by the widget on top of this circle (CodexCard).
        return tex;
    }
}
