using RioJoy.Core.Calibration;

namespace RioJoy.Core.Output;

/// <summary>
/// Where a calibrated axis lands on the virtual Xbox 360 pad. Our own enum (no
/// ViGEm types — these are shared config types, serialized into profiles and
/// compiled for net40 too); <see cref="ViGEmJoystickSink"/> translates to the
/// ViGEm equivalents. <see cref="None"/> = the axis is not emitted at all.
/// </summary>
public enum PadTarget
{
    LeftThumbX,
    LeftThumbY,
    RightThumbX,
    RightThumbY,
    LeftTrigger,
    RightTrigger,
    None,
}

/// <summary>
/// How a calibrated axis value (<c>0..32766</c>, center 16383) converts to a
/// thumb-stick value. Meaningless for trigger targets (triggers always use the
/// legacy <c>value*255/32766</c> byte conversion).
/// </summary>
public enum AxisOutputMode
{
    /// <summary>Legacy bipolar mapping: <c>(value - 16383) * 2</c> → center 16383 = thumb 0.</summary>
    Centered,

    /// <summary>
    /// Unipolar mapping for axes whose calibrated rest is 0 (the ratcheted
    /// throttle): <c>clamp(value, 0, 32767)</c> → calibrated 0 = thumb center 0,
    /// 32766 = thumb max. Only the upper half of the thumb range is used.
    /// </summary>
    UnipolarPositive,
}

/// <summary>One axis route: which pad control it drives, and how the value converts.</summary>
public sealed record AxisRoute
{
    public PadTarget Target { get; init; } = PadTarget.None;

    public AxisOutputMode Mode { get; init; } = AxisOutputMode.Centered;
}

/// <summary>
/// Per-profile routing of the six calibrated axes onto the ViGEm Xbox 360 pad
/// (<see cref="RioJoy.Core.Profiles.RioProfile.AxisRouting"/>; null there = this
/// default). The defaults reproduce the historical hardcoded sink routing
/// exactly — X→LeftThumbX, Y→LeftThumbY, Rx→RightThumbX, Ry→RightThumbY,
/// Z→LeftTrigger, Rz→RightTrigger, all <see cref="AxisOutputMode.Centered"/> —
/// so existing profiles behave identically. Two axes routed to the same target
/// are not arbitrated: the last <c>SetAxis</c> write wins.
/// </summary>
public sealed record AxisRoutingConfig
{
    public AxisRoute X { get; init; } = new() { Target = PadTarget.LeftThumbX };

    public AxisRoute Y { get; init; } = new() { Target = PadTarget.LeftThumbY };

    public AxisRoute Z { get; init; } = new() { Target = PadTarget.LeftTrigger };

    public AxisRoute Rx { get; init; } = new() { Target = PadTarget.RightThumbX };

    public AxisRoute Ry { get; init; } = new() { Target = PadTarget.RightThumbY };

    public AxisRoute Rz { get; init; } = new() { Target = PadTarget.RightTrigger };

    /// <summary>The route for <paramref name="axis"/>.</summary>
    public AxisRoute RouteFor(JoyAxis axis) => axis switch
    {
        JoyAxis.X => X,
        JoyAxis.Y => Y,
        JoyAxis.Z => Z,
        JoyAxis.Rx => Rx,
        JoyAxis.Ry => Ry,
        JoyAxis.Rz => Rz,
        _ => new AxisRoute(), // unknown axis → None (not emitted)
    };
}
