namespace ConeCalorimeter.Services;

internal static class ModbusFloatSelector
{
    private static readonly ModbusFloatByteOrder[] FloatByteOrders =
    [
        ModbusFloatByteOrder.Abcd,
        ModbusFloatByteOrder.Cdab,
        ModbusFloatByteOrder.Badc,
        ModbusFloatByteOrder.Dcba
    ];

    private static readonly ModbusFloatByteOrder[] AlternateFloatByteOrders =
    [
        ModbusFloatByteOrder.Cdab,
        ModbusFloatByteOrder.Badc,
        ModbusFloatByteOrder.Dcba
    ];

    public static bool TrySelectRangedFloat(
        ModbusFloatReadResult result,
        double minimum,
        double maximum,
        out ModbusFloatByteOrder byteOrder,
        out double value,
        out int matchCount,
        ModbusFloatByteOrder? preferredByteOrder = null)
    {
        var matches = FloatByteOrders
            .Where(candidate => IsInRange(result.GetValue(candidate), minimum, maximum))
            .ToArray();

        matchCount = matches.Length;

        if (preferredByteOrder is { } preferred && matches.Contains(preferred))
        {
            byteOrder = preferred;
            value = result.GetValue(byteOrder);
            return true;
        }

        if (matches.Contains(ModbusFloatByteOrder.Abcd))
        {
            byteOrder = ModbusFloatByteOrder.Abcd;
            value = result.Abcd;
            return true;
        }

        var alternateMatches = matches
            .Where(candidate => candidate != ModbusFloatByteOrder.Abcd)
            .ToArray();

        if (alternateMatches.Length != 1)
        {
            byteOrder = default;
            value = double.NaN;
            return false;
        }

        byteOrder = alternateMatches[0];
        value = result.GetValue(byteOrder);
        return true;
    }

    public static bool IsInRange(double value, double minimum, double maximum)
    {
        return double.IsFinite(value) && value >= minimum && value <= maximum;
    }
}
