using RioJoy.Core.Calibration;

namespace RioJoy.Core.Output;

/// <summary>
/// A resolved axis write: which pad control to drive and the converted value.
/// <see cref="Value"/> is in the thumb range (<c>-32768..32767</c>) for thumb
/// targets, the trigger range (<c>0..255</c>) for trigger targets, and 0 for
/// <see cref="PadTarget.None"/> (nothing is written).
/// </summary>
public readonly struct ResolvedAxis
{
    public PadTarget Target { get; }

    public int Value { get; }

    public ResolvedAxis(PadTarget target, int value)
    {
        Target = target;
        Value = value;
    }

    public override string ToString() => $"{Target}:{Value}";
}

/// <summary>
/// Pure route-resolution + value-conversion for the ViGEm sink — no ViGEm types,
/// so the decision logic is unit-testable without the bus driver.
/// <see cref="ViGEmJoystickSink"/> translates the result into ViGEm calls.
/// </summary>
public static class AxisRouter
{
    /// <summary>
    /// Resolve where the calibrated <paramref name="value"/> (<c>0..32766</c>,
    /// center 16383) of <paramref name="axis"/> lands under
    /// <paramref name="config"/>, and convert it for that target.
    /// </summary>
    public static ResolvedAxis Resolve(AxisRoutingConfig config, JoyAxis axis, int value)
    {
        if (config is null) throw new ArgumentNullException(nameof(config));

        AxisRoute route = config.RouteFor(axis);
        return route.Target switch
        {
            PadTarget.None => new ResolvedAxis(PadTarget.None, 0),
            PadTarget.LeftTrigger or PadTarget.RightTrigger =>
                new ResolvedAxis(route.Target, ToTrigger(value)),
            _ => new ResolvedAxis(
                route.Target,
                route.Mode == AxisOutputMode.UnipolarPositive
                    ? ToUnipolarThumb(value)
                    : ToCenteredThumb(value)),
        };
    }

    // RIO axis 0..32766 (centre 16383) -> Xbox thumb short -32768..32767 (legacy math).
    private static int ToCenteredThumb(int value) =>
        Compat.Net48Math.Clamp((value - AxisOutputs.Center) * 2, (int)short.MinValue, short.MaxValue);

    // Unipolar RIO axis (rest = 0) -> upper thumb half: 0 -> center 0, 32766 -> max.
    private static int ToUnipolarThumb(int value) =>
        Compat.Net48Math.Clamp(value, 0, (int)short.MaxValue);

    // RIO axis 0..32766 -> Xbox trigger byte 0..255 (legacy math).
    private static int ToTrigger(int value) =>
        Compat.Net48Math.Clamp(value * 255 / AxisOutputs.Max, 0, 255);
}
