namespace RioJoy.Core.Editing;

/// <summary>Shape of a panel group.</summary>
public enum PanelGroupKind
{
    /// <summary>A 4×2 MFD button cluster (lamp buttons).</summary>
    Mfd,

    /// <summary>A vertical 1×8 board column (lamp buttons).</summary>
    Column,

    /// <summary>A 4×4 keypad (added later; no lamps).</summary>
    Keypad,
}

/// <summary>
/// One functional group on the cockpit control panel: a titled block of address
/// buttons laid out in <see cref="Rows"/> × <see cref="Cols"/> (row-major, null =
/// empty slot). <see cref="OriginCol"/>/<see cref="OriginRow"/> place the block on
/// the panel in button-cell units (the renderer scales cells to pixels).
/// </summary>
public sealed record PanelGroup(
    string Title,
    PanelGroupKind Kind,
    int Cols,
    int Rows,
    bool LampCapable,
    int OriginCol,
    int OriginRow,
    IList<int?> Addresses);

/// <summary>One address button placed on the panel (absolute cell coordinates).</summary>
public sealed record PanelButton(int Address, PanelGroup Group, int Col, int Row, bool LampCapable);

/// <summary>
/// The cockpit control panel's logical layout — the functional grouping from the
/// original Win32 RIO design (docs/Win32RIO/control-panel-mockup.png): five MFD
/// clusters, four board columns, and the two later-added 4×4 keypads. Every RIO
/// address 0x00–0x47 / 0x50–0x6F is placed exactly once. Pure data so the mapping
/// is unit-tested; the WinForms editor renders these groups and binds each button.
/// </summary>
public static class CockpitPanel
{
    // Helper: 4×2 MFD address block, top row then bottom row (addresses descend).
    private static int?[] Mfd(int hi) => new int?[]
    {
        hi, hi - 1, hi - 2, hi - 3,
        hi - 4, hi - 5, hi - 6, hi - 7,
    };

    // Helper: vertical 1×8 column from a base address upward.
    private static int?[] Col8(int baseAddr) => new int?[]
    {
        baseAddr, baseAddr + 1, baseAddr + 2, baseAddr + 3,
        baseAddr + 4, baseAddr + 5, baseAddr + 6, baseAddr + 7,
    };

    // Helper: 4×4 keypad. Physical keys 1 2 3 C / 4 5 6 D / 7 8 9 E / A 0 B F map to
    // address = base + that hex digit.
    private static int?[] Keypad(int baseAddr)
    {
        int[] digits = { 0x1, 0x2, 0x3, 0xC, 0x4, 0x5, 0x6, 0xD, 0x7, 0x8, 0x9, 0xE, 0xA, 0x0, 0xB, 0xF };
        var a = new int?[16];
        for (int i = 0; i < 16; i++) a[i] = baseAddr + digits[i];
        return a;
    }

    /// <summary>All panel groups, positioned for rendering.</summary>
    public static IList<PanelGroup> Groups { get; } = new[]
    {
        // Upper MFD row.
        new PanelGroup("Upper Left MFD", PanelGroupKind.Mfd, 4, 2, true, 0, 1, Mfd(0x2F)),
        new PanelGroup("Upper Middle MFD", PanelGroupKind.Mfd, 4, 2, true, 4, 1, Mfd(0x27)),
        new PanelGroup("Upper Right MFD", PanelGroupKind.Mfd, 4, 2, true, 8, 1, Mfd(0x37)),

        // Lower MFD row.
        new PanelGroup("Lower Left MFD", PanelGroupKind.Mfd, 4, 2, true, 0, 5, Mfd(0x0F)),
        new PanelGroup("Lower Right MFD", PanelGroupKind.Mfd, 4, 2, true, 8, 5, Mfd(0x07)),

        // Board columns.
        new PanelGroup("Throttle", PanelGroupKind.Column, 1, 8, true, 0, 9, Col8(0x38)),
        new PanelGroup("Secondary", PanelGroupKind.Column, 1, 8, true, 4, 9, Col8(0x10)),
        new PanelGroup("Screen", PanelGroupKind.Column, 1, 8, true, 6, 9, Col8(0x18)),
        new PanelGroup("Joystick", PanelGroupKind.Column, 1, 8, true, 11, 9, Col8(0x40)),

        // Keypads (no lamps): internal in the middle (below Upper Middle MFD, above
        // the Secondary/Screen columns); external between those columns and Joystick.
        new PanelGroup("Internal Keypad", PanelGroupKind.Keypad, 4, 4, false, 4, 4, Keypad(0x50)),
        new PanelGroup("External Keypad", PanelGroupKind.Keypad, 4, 4, false, 7, 9, Keypad(0x60)),
    };

    /// <summary>Flatten the groups to positioned <see cref="PanelButton"/>s (absolute cells).</summary>
    public static IList<PanelButton> Buttons()
    {
        var buttons = new List<PanelButton>();
        foreach (PanelGroup g in Groups)
        {
            for (int i = 0; i < g.Addresses.Count; i++)
            {
                if (g.Addresses[i] is not { } addr) continue;
                int localCol = i % g.Cols;
                int localRow = i / g.Cols;
                // +1 row leaves space under the title.
                buttons.Add(new PanelButton(addr, g, g.OriginCol + localCol, g.OriginRow + 1 + localRow, g.LampCapable));
            }
        }
        return buttons;
    }
}
