namespace RioJoy.Core.Mapping;

/// <summary>A named keyboard key and its Windows virtual-key code.</summary>
public readonly record struct KeyName(string Name, byte Value);

/// <summary>
/// Friendly Windows virtual-key names ↔ VK codes, for the profile editor's key
/// picker so a keyboard action is chosen by name instead of a raw hex byte. The
/// keyboard <c>iRIO</c> payload is a VK code (see <see cref="InputRouter"/> /
/// <c>SendInputSink</c>); modifiers are separate flags, so they are not listed here
/// as keys.
/// </summary>
public static class KeyCatalog
{
    /// <summary>All catalogued keys, in menu order (letters, digits, F-keys, …).</summary>
    public static IList<KeyName> Entries { get; } = Build();

    private static readonly Dictionary<byte, string> ByValue =
        Entries.GroupBy(e => e.Value).ToDictionary(g => g.Key, g => g.First().Name);

    private static readonly Dictionary<string, byte> ByName =
        Entries.ToDictionary(e => e.Name, e => e.Value, StringComparer.OrdinalIgnoreCase);

    /// <summary>Friendly name for a VK code, or <c>"0xHH"</c> when uncatalogued.</summary>
    public static string NameFor(byte vk) => ByValue.TryGetValue(vk, out string? n) ? n : $"0x{vk:X2}";

    /// <summary>Look up a VK code by friendly name.</summary>
    public static bool TryGetValue(string name, out byte vk) => ByName.TryGetValue(name, out vk);

    private static List<KeyName> Build()
    {
        var list = new List<KeyName>();
        void Add(string name, int vk) => list.Add(new KeyName(name, (byte)vk));

        for (char c = 'A'; c <= 'Z'; c++) Add(c.ToString(), c);            // 0x41–0x5A
        for (int d = 0; d <= 9; d++) Add(d.ToString(), 0x30 + d);          // 0x30–0x39
        for (int f = 1; f <= 12; f++) Add($"F{f}", 0x6F + f);              // F1–F12 = 0x70–0x7B

        Add("Space", 0x20);
        Add("Enter", 0x0D);
        Add("Escape", 0x1B);
        Add("Tab", 0x09);
        Add("Backspace", 0x08);
        Add("Delete", 0x2E);
        Add("Insert", 0x2D);
        Add("Home", 0x24);
        Add("End", 0x23);
        Add("Page Up", 0x21);
        Add("Page Down", 0x22);

        Add("Left", 0x25);
        Add("Up", 0x26);
        Add("Right", 0x27);
        Add("Down", 0x28);

        for (int n = 0; n <= 9; n++) Add($"NumPad {n}", 0x60 + n);         // 0x60–0x69
        Add("NumPad *", 0x6A);
        Add("NumPad +", 0x6B);
        Add("NumPad -", 0x6D);
        Add("NumPad .", 0x6E);
        Add("NumPad /", 0x6F);

        Add("; :", 0xBA);
        Add("= +", 0xBB);
        Add(", <", 0xBC);
        Add("- _", 0xBD);
        Add(". >", 0xBE);
        Add("/ ?", 0xBF);
        Add("` ~", 0xC0);
        Add("[ {", 0xDB);
        Add("\\ |", 0xDC);
        Add("] }", 0xDD);
        Add("' \"", 0xDE);

        Add("Caps Lock", 0x14);
        Add("Print Screen", 0x2C);
        Add("Scroll Lock", 0x91);
        Add("Pause", 0x13);
        Add("Num Lock", 0x90);

        return list;
    }
}
