using System.Text;

namespace RioJoy.Core.Plasma;

/// <summary>A glyph cell size in pixels (width × height).</summary>
public readonly record struct FontSize(int Width, int Height);

/// <summary>
/// Builds the ESC-based command byte sequences for the plasma / VFD text display
/// on the secondary COM port. Pure port of the <c>CPlasma</c> command methods
/// (riovjoy2.cpp#L2146); see docs/PROTOCOL.md §7. Text is encoded as Latin-1
/// (each char → one byte), matching the legacy 8-bit <c>char*</c> packets.
/// </summary>
public static class PlasmaCommands
{
    /// <summary>The ESC lead byte (legacy <c>#define ESC 27</c>).</summary>
    public const byte Esc = 27;

    /// <summary>Clear the display (<c>ESC @</c>).</summary>
    public static byte[] Clear() => new[] { Esc, (byte)'@' };

    /// <summary>Move the cursor home (<c>ESC L</c>).</summary>
    public static byte[] CursorHome() => new[] { Esc, (byte)'L' };

    /// <summary>Set cursor cell (<c>ESC G n</c>).</summary>
    public static byte[] Cursor(byte n) => new[] { Esc, (byte)'G', n };

    /// <summary>Set cursor X (<c>ESC R x</c>).</summary>
    public static byte[] CursorX(byte x) => new[] { Esc, (byte)'R', x };

    /// <summary>Set cursor Y (<c>ESC Q y</c>).</summary>
    public static byte[] CursorY(byte y) => new[] { Esc, (byte)'Q', y };

    /// <summary>Set font attribute (<c>ESC H attr</c>).</summary>
    public static byte[] FontAttr(byte attr) => new[] { Esc, (byte)'H', attr };

    /// <summary>Select font (<c>ESC K font</c>).</summary>
    public static byte[] Font(byte font) => new[] { Esc, (byte)'K', font };

    /// <summary>Draw a box outline (<c>ESC X l t r b</c>).</summary>
    public static byte[] BoxDraw(byte left, byte top, byte right, byte bottom) =>
        new[] { Esc, (byte)'X', left, top, right, bottom };

    /// <summary>Fill a box (<c>ESC x 0 l t r b</c>).</summary>
    public static byte[] BoxFill(byte left, byte top, byte right, byte bottom) =>
        new[] { Esc, (byte)'x', (byte)0, left, top, right, bottom };

    /// <summary>Encode display text as raw bytes (Latin-1, one byte per char).</summary>
    public static byte[] Text(string text)
    {
        if (text is null) throw new ArgumentNullException(nameof(text));
        // net48 has no Encoding.Latin1; codepage 28591 is ISO-8859-1 (Latin-1).
        return Encoding.GetEncoding(28591).GetBytes(text);
    }

    /// <summary>
    /// Glyph cell size for a font id. Port of <c>GetFontSize</c> (riovjoy2.cpp#L2198):
    /// fonts 0–3 and 6–7 are 5×7, fonts 4–5 are 10×14.
    /// </summary>
    public static FontSize GetFontSize(int font) => font switch
    {
        4 or 5 => new FontSize(10, 14),
        _ => new FontSize(5, 7),
    };

    /// <summary>
    /// Compute the auto-fit font and centered (x, y) for positioned text, porting
    /// the <c>PlasmaPosText</c> layout logic (riovjoy2.cpp#L2235). For non-score
    /// text (<paramref name="font"/> ≠ 2), the font is chosen from the length
    /// (≤9 → font 5, else font 2, capping length at 20). When the caller passes
    /// (0, 0), the text is centered around cell (56, 15) for the chosen font.
    /// </summary>
    public static (byte x, byte y, byte font, int length) ResolvePosText(
        string text, byte x, byte y, byte font)
    {
        if (text is null) throw new ArgumentNullException(nameof(text));
        int len = text.Length;

        if (font != 2) // not the Score font
        {
            if (len <= 9) font = 5;
            else { font = 2; if (len > 20) len = 20; }
        }
        else if (len > 20)
        {
            len = 20;
        }

        if (x == 0 && y == 0)
        {
            FontSize size = GetFontSize(font);
            x = (byte)(56 - (len * size.Width / 2));
            y = (byte)(15 - (size.Height / 2));
        }

        return (x, y, font, len);
    }
}
