using VPlasma.Core.Protocol;

namespace VPlasma.Core.Device;

/// <summary>
/// Canned wire streams for the UI's <i>Self test</i> button: each page is a
/// byte sequence exactly as a host would send it over COM2, fed through the
/// same parser as live traffic — so the button doubles as an end-to-end
/// exercise of the command set without a host attached.
/// </summary>
public static class PlasmaSelfTest
{
    public const int PageCount = 3;

    /// <summary>The wire bytes for self-test page <paramref name="page"/> (0-based).</summary>
    public static byte[] BuildPage(int page) => page switch
    {
        0 => BuildBanner(),
        1 => BuildCharset(),
        2 => BuildGraphics(),
        _ => throw new ArgumentOutOfRangeException(nameof(page)),
    };

    private static void Esc(List<byte> b, byte cmd, params byte[] operands)
    {
        b.Add(PlasmaProtocol.Esc);
        b.Add(cmd);
        b.AddRange(operands);
    }

    private static void Text(List<byte> b, string s)
    {
        foreach (char c in s)
            b.Add((byte)c);
    }

    /// <summary>Big-font banner over small-font attribute samples.</summary>
    private static byte[] BuildBanner()
    {
        var b = new List<byte>();
        Esc(b, PlasmaProtocol.CmdClearScreen);
        Esc(b, PlasmaProtocol.CmdCursorMode, 0x00);

        Esc(b, PlasmaProtocol.CmdFontSelect, 0x04);   // 12×16 cells: 10 × 2
        Text(b, " vPLASMA");

        Esc(b, PlasmaProtocol.CmdFontSelect, 0x00);   // 6×8 cells: 21 × 4
        Esc(b, PlasmaProtocol.CmdHomeCursor);
        b.Add(PlasmaProtocol.LineFeed);
        b.Add(PlasmaProtocol.LineFeed);
        Text(b, "128x32 PLASMA DISPLAY");              // exactly one 21-cell row

        Esc(b, PlasmaProtocol.CmdAttributes, 1); Text(b, "HALF ");
        Esc(b, PlasmaProtocol.CmdAttributes, 2); Text(b, "UNDER ");
        Esc(b, PlasmaProtocol.CmdAttributes, 3); Text(b, "REV");
        Esc(b, PlasmaProtocol.CmdAttributes, 4); Text(b, " FLASH");
        Esc(b, PlasmaProtocol.CmdAttributes, PlasmaProtocol.OperandDefault);
        return b.ToArray();
    }

    /// <summary>The printable set 0x20..0x6F — a full 21×4 grid, minus a cell.</summary>
    private static byte[] BuildCharset()
    {
        var b = new List<byte>();
        Esc(b, PlasmaProtocol.CmdClearScreen);
        Esc(b, PlasmaProtocol.CmdCursorMode, 0x00);
        for (byte c = 0x20; c <= 0x6F; ++c)
            b.Add(c);
        return b.ToArray();
    }

    /// <summary>The rest of the charset plus an ESC P pattern block.</summary>
    private static byte[] BuildGraphics()
    {
        var b = new List<byte>();
        Esc(b, PlasmaProtocol.CmdClearScreen);
        Esc(b, PlasmaProtocol.CmdCursorMode, 0x00);
        for (byte c = 0x70; c <= 0x7E; ++c)
            b.Add(c);

        // A framed checkerboard sent the way the game sends everything:
        // full-width ESC P rows (screen 0, xbyte 0, 16 bytes, 1 row each).
        for (int y = 10; y < VPlasmaDevice.Height; ++y)
        {
            Esc(b, PlasmaProtocol.CmdGraphicsWrite,
                0, (byte)y, 0, VPlasmaDevice.WidthBytes, 1);
            bool edge = y is 10 or VPlasmaDevice.Height - 1;
            for (int x = 0; x < VPlasmaDevice.WidthBytes; ++x)
            {
                byte fill = edge ? (byte)0xFF : (y & 2) == 0 ? (byte)0xAA : (byte)0x55;
                if (!edge)
                {
                    if (x == 0) fill |= 0x80;                          // left frame edge
                    if (x == VPlasmaDevice.WidthBytes - 1) fill |= 0x01; // right frame edge
                }
                b.Add(fill);
            }
        }
        return b.ToArray();
    }
}
