namespace RioJoy.Core.Mapping;

/// <summary>
/// A mutable, editor-friendly view of one <c>iRIO</c> map word: the routing
/// <see cref="Kind"/>, payload <see cref="Value"/>, lamp flag, and keyboard
/// modifiers. Two-way binds cleanly to UI controls and converts to/from the packed
/// 16-bit word via <see cref="ToWord"/> / <see cref="FromWord"/> (which round-trip
/// through <see cref="RioMapEntry"/>). The button's display label is stored
/// separately (the profile's overlay labels), not here.
/// </summary>
public sealed class ButtonBinding
{
    /// <summary>How the input is routed (keyboard / joystick / hat / mouse / RIO command).</summary>
    public RioRouteKind Kind { get; set; } = RioRouteKind.Keyboard;

    /// <summary>Payload byte: VK code, joystick button #, hat direction, or mouse action.</summary>
    public byte Value { get; set; }

    /// <summary>Lighted button (lamp feedback on press/release).</summary>
    public bool IsLit { get; set; }

    /// <summary>Keyboard modifier: SHIFT.</summary>
    public bool Shift { get; set; }

    /// <summary>Keyboard modifier: CTRL.</summary>
    public bool Ctrl { get; set; }

    /// <summary>Keyboard modifier: ALT.</summary>
    public bool Alt { get; set; }

    /// <summary>Apply <c>KEYEVENTF_EXTENDEDKEY</c> to the key.</summary>
    public bool Extended { get; set; }

    /// <summary>True when this binding does nothing (an empty slot).</summary>
    public bool IsUnmapped => ToWord() == 0;

    /// <summary>
    /// A short human-readable description of the assigned function — e.g.
    /// <c>CTL-N</c>, <c>JOY1</c>, <c>POV-U</c>, <c>Mse LB</c>, or a RIO command name.
    /// Empty when unmapped. Used as the editor cell caption.
    /// </summary>
    public string Describe() => Kind switch
    {
        _ when IsUnmapped => string.Empty,
        RioRouteKind.Keyboard =>
            (Ctrl ? "CTL-" : "") + (Shift ? "SFT-" : "") + (Alt ? "ALT-" : "") + KeyCatalog.NameFor(Value),
        RioRouteKind.Joystick => $"JOY{Value}",
        RioRouteKind.Hat => (RioHat)Value switch
        {
            RioHat.Up => "POV-U",
            RioHat.Right => "POV-R",
            RioHat.Down => "POV-D",
            RioHat.Left => "POV-L",
            _ => "POV",
        },
        RioRouteKind.Mouse => "Mse " + (MouseAction)Value switch
        {
            MouseAction.MoveUp => "Up",
            MouseAction.MoveDown => "Dn",
            MouseAction.MoveLeft => "Lt",
            MouseAction.MoveRight => "Rt",
            MouseAction.LeftClick => "LB",
            MouseAction.RightClick => "RB",
            _ => Value.ToString(),
        },
        RioRouteKind.RioCommand => ((RioCommandCode)Value).ToString(),
        _ => string.Empty,
    };

    /// <summary>Pack to the 16-bit <c>iRIO</c> map word.</summary>
    public ushort ToWord() =>
        RioMapEntry.Create(Kind, Value, IsLit, Shift, Ctrl, Alt, Extended).Raw;

    /// <summary>Build an editable binding from a packed map word.</summary>
    public static ButtonBinding FromWord(ushort raw)
    {
        var e = new RioMapEntry(raw);
        return new ButtonBinding
        {
            Kind = e.Kind,
            Value = e.Value,
            IsLit = e.HasLamp,
            Shift = e.Shift,
            Ctrl = e.Ctrl,
            Alt = e.Alt,
            Extended = e.Extended,
        };
    }
}
