using RioJoy.Core.Mapping;
using Xunit;

namespace RioJoy.Core.Tests.Mapping;

public class ButtonBindingTests
{
    [Theory]
    [InlineData(RioRouteKind.Keyboard, 0x47, 0x0047)]
    [InlineData(RioRouteKind.Joystick, 0x58, 0x1058)]
    [InlineData(RioRouteKind.Hat, 0x01, 0x2001)]
    [InlineData(RioRouteKind.Mouse, 0x10, 0x4010)]
    [InlineData(RioRouteKind.RioCommand, 0x60, 0x7060)]
    public void Create_PacksRoutingFlagsAndValue(RioRouteKind kind, byte value, ushort expected)
    {
        RioMapEntry e = RioMapEntry.Create(kind, value);
        Assert.Equal(expected, e.Raw);
        Assert.Equal(kind, e.Kind);
        Assert.Equal(value, e.Value);
    }

    [Fact]
    public void Create_SetsHighByteBits()
    {
        RioMapEntry e = RioMapEntry.Create(
            RioRouteKind.Keyboard, 0x41, lit: true, shift: true, ctrl: true, alt: true, extended: true);

        Assert.True(e.HasLamp);
        Assert.True(e.Shift);
        Assert.True(e.Ctrl);
        Assert.True(e.Alt);
        Assert.True(e.Extended);
        Assert.Equal(0x41, e.Value);
        // 0x8000|0x0800|0x0400|0x0200|0x0100|0x41
        Assert.Equal(0x8F41, e.Raw);
    }

    [Theory]
    [InlineData(RioRouteKind.Keyboard)]
    [InlineData(RioRouteKind.Joystick)]
    [InlineData(RioRouteKind.Hat)]
    [InlineData(RioRouteKind.Mouse)]
    [InlineData(RioRouteKind.RioCommand)]
    public void Binding_RoundTripsThroughWord(RioRouteKind kind)
    {
        var b = new ButtonBinding
        {
            Kind = kind,
            Value = 0x2A,
            IsLit = true,
            Shift = true,
            Alt = true,
        };

        ushort word = b.ToWord();
        ButtonBinding back = ButtonBinding.FromWord(word);

        Assert.Equal(b.Kind, back.Kind);
        Assert.Equal(b.Value, back.Value);
        Assert.Equal(b.IsLit, back.IsLit);
        Assert.Equal(b.Shift, back.Shift);
        Assert.Equal(b.Ctrl, back.Ctrl);
        Assert.Equal(b.Alt, back.Alt);
        Assert.Equal(b.Extended, back.Extended);
        Assert.Equal(word, back.ToWord());
    }

    [Fact]
    public void Binding_FromWord_DecodesLegacyValue()
    {
        // 0x8F41 from the encode test -> lit keyboard 'A' with all modifiers.
        ButtonBinding b = ButtonBinding.FromWord(0x8F41);
        Assert.Equal(RioRouteKind.Keyboard, b.Kind);
        Assert.Equal(0x41, b.Value);
        Assert.True(b.IsLit && b.Shift && b.Ctrl && b.Alt && b.Extended);
    }

    [Fact]
    public void Binding_Default_IsUnmapped()
    {
        Assert.True(new ButtonBinding().IsUnmapped);
        Assert.False(new ButtonBinding { Value = 0x41 }.IsUnmapped);
    }

    [Fact]
    public void Describe_FormatsEachKind()
    {
        Assert.Equal("", new ButtonBinding().Describe());
        Assert.Equal("K", new ButtonBinding { Value = 0x4B }.Describe());
        Assert.Equal("CTL-N", new ButtonBinding { Value = 0x4E, Ctrl = true }.Describe());
        Assert.Equal("CTL-SFT-K", new ButtonBinding { Value = 0x4B, Ctrl = true, Shift = true }.Describe());
        Assert.Equal("JOY1", new ButtonBinding { Kind = RioRouteKind.Joystick, Value = 1 }.Describe());
        Assert.Equal("POV-U", new ButtonBinding { Kind = RioRouteKind.Hat, Value = (byte)(int)RioHat.Up }.Describe());
        Assert.Equal("Mse LB", new ButtonBinding { Kind = RioRouteKind.Mouse, Value = (byte)MouseAction.LeftClick }.Describe());
        Assert.Equal("ResetThrottle", new ButtonBinding { Kind = RioRouteKind.RioCommand, Value = (byte)RioCommandCode.ResetThrottle }.Describe());
    }
}
