using FontStashSharp;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Theriapolis.Game.CodexUI.Core;

namespace Theriapolis.Game.CodexUI.Widgets;

/// <summary>
/// Single- or multi-line text widget. Wraps to <see cref="MaxWidth"/> if set;
/// otherwise to its arrange width. Color and font are explicit so the same
/// widget can render the codex header (DisplayLarge / Ink), an eyebrow
/// (MonoTag / InkMute), or a body paragraph (SerifBody / InkSoft).
/// </summary>
public sealed class CodexLabel : CodexWidget
{
    public string Text { get; set; } = "";
    public SpriteFontBase Font { get; set; }
    public Color Color { get; set; } = CodexColors.Ink;
    public HAlign HAlign { get; set; } = HAlign.Left;

    /// <summary>If set, text wraps when its measured width would exceed this.</summary>
    public int? MaxWidth { get; set; }

    public CodexLabel(string text, SpriteFontBase font, Color? color = null, HAlign hAlign = HAlign.Left)
    {
        Text = text;
        Font = font;
        if (color.HasValue) Color = color.Value;
        HAlign = hAlign;
    }

    private string[] _wrappedLines = System.Array.Empty<string>();

    protected override Point MeasureCore(Point available)
    {
        int wrapW = MaxWidth ?? available.X;
        _wrappedLines = WrapText(Text, Font, wrapW);
        int width = 0;
        foreach (var line in _wrappedLines)
        {
            var s = Font.MeasureString(line);
            if (s.X > width) width = (int)System.MathF.Ceiling(s.X);
        }
        int height = (int)System.MathF.Ceiling(Font.LineHeight * (_wrappedLines.Length == 0 ? 1 : _wrappedLines.Length));
        return new Point(System.Math.Min(width, wrapW), height);
    }

    protected override void ArrangeCore(Rectangle bounds) { }

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        float y = Bounds.Y;
        foreach (var line in _wrappedLines)
        {
            var s = Font.MeasureString(line);
            float x = HAlign switch
            {
                HAlign.Center => Bounds.X + (Bounds.Width - s.X) / 2f,
                HAlign.Right  => Bounds.X +  Bounds.Width - s.X,
                _             => Bounds.X,
            };
            Font.DrawText(sb, line, new Vector2(x, y), Color);
            y += Font.LineHeight;
        }
    }

    /// <summary>
    /// Greedy word wrap. Splits on spaces, fits as many words as possible per
    /// line, hard-breaks oversize words. Honours embedded \n.
    /// </summary>
    public static string[] WrapText(string text, SpriteFontBase font, int maxWidth)
    {
        if (string.IsNullOrEmpty(text)) return new[] { "" };
        var lines = new System.Collections.Generic.List<string>();
        foreach (var paragraph in text.Split('\n'))
        {
            var words = paragraph.Split(' ');
            var current = new System.Text.StringBuilder();
            foreach (var word in words)
            {
                string trial = current.Length == 0 ? word : current + " " + word;
                if (font.MeasureString(trial).X > maxWidth && current.Length > 0)
                {
                    lines.Add(current.ToString());
                    current.Clear();
                    current.Append(word);
                }
                else
                {
                    current.Clear();
                    current.Append(trial);
                }
            }
            lines.Add(current.ToString());
        }
        return lines.ToArray();
    }
}

/// <summary>
/// Decorative horizontal rule with a small central diamond glyph. Mirrors
/// the React design's <c>.divider</c> / <c>.clade-group-label::after</c>
/// hairline + ornament pattern.
/// </summary>
public sealed class CodexOrnamentRule : CodexWidget
{
    public Color RuleColor { get; set; } = CodexColors.Rule;
    public string? Label { get; set; }
    public SpriteFontBase Font { get; set; }

    private readonly CodexAtlas _atlas;

    public CodexOrnamentRule(CodexAtlas atlas, SpriteFontBase font, string? label = null)
    {
        _atlas = atlas;
        Font = font;
        Label = label;
    }

    protected override Point MeasureCore(Point available) => new(available.X, 16);
    protected override void ArrangeCore(Rectangle bounds) { }

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        int midY = Bounds.Y + Bounds.Height / 2;

        if (!string.IsNullOrEmpty(Label))
        {
            var s = Font.MeasureString(Label);
            int padX = 12;
            int textX = Bounds.X + 0;
            // Left rule before the text.
            sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, midY, 16, 1), RuleColor);
            int textXStart = Bounds.X + 16 + padX / 2;
            Font.DrawText(sb, Label, new Vector2(textXStart, midY - Font.LineHeight / 2f), CodexColors.InkMute);
            int afterText = textXStart + (int)s.X + padX / 2;
            sb.Draw(_atlas.Pixel, new Rectangle(afterText, midY, Bounds.Right - afterText, 1), RuleColor);
        }
        else
        {
            int half = (Bounds.Width - 16) / 2;
            sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, midY, half, 1), RuleColor);
            sb.Draw(_atlas.OrnamentDiamond, new Rectangle(Bounds.X + half, midY - 8, 16, 16), CodexColors.Gild);
            sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X + half + 16, midY, half, 1), RuleColor);
        }
    }
}
