namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Wire;

/// <summary>
///     PMC address-letter → FOCAS <c>ADR_*</c> numeric code. Values are the FOCAS/2 wire
///     constants passed as the <c>area</c> argument on <c>pmc_rdpmcrng</c>
///     (G=0, F=1, Y=2, X=3, A=4, R=5, T=6, K=7, C=8, D=9, E=10).
/// </summary>
public enum FocasPmcArea : short
{
    G = 0,
    F = 1,
    Y = 2,
    X = 3,
    A = 4,
    R = 5,
    T = 6,
    K = 7,
    C = 8,
    D = 9,
    E = 10,
}

/// <summary>
///     PMC data-type numeric codes per FOCAS/2: <c>Byte=0</c>, <c>Word=1</c>, <c>Long=2</c>,
///     <c>Real=4</c>, <c>Double=5</c>. Passed as the <c>data_type</c> argument on
///     <c>pmc_rdpmcrng</c>.
/// </summary>
public enum FocasPmcDataType : short
{
    Byte = 0,
    Word = 1,
    Long = 2,
    Real = 4,
    Double = 5,
}

/// <summary>
///     CNC operation mode as reported by <c>cnc_rdopmode</c>. Values are the FOCAS-defined
///     mode codes; see <see cref="FocasOperationModeExtensions.ToText"/> for the canonical
///     operator-facing labels.
/// </summary>
public enum FocasOperationMode : short
{
    Mdi = 0,
    Auto = 1,
    TJog = 2,
    Edit = 3,
    Handle = 4,
    Jog = 5,
    TeachInHandle = 6,
    Reference = 7,
    Remote = 8,
    Test = 9,
}

/// <summary>Extension helpers over <see cref="FocasOperationMode"/>.</summary>
public static class FocasOperationModeExtensions
{
    /// <summary>
    ///     Gets the canonical operator-facing label for an operation mode (e.g. <c>"AUTO"</c>,
    ///     <c>"EDIT"</c>). Delegates to <see cref="FocasOpMode.ToText"/> so the wire layer
    ///     and the fixed-tree projection render identical labels — historically these two
    ///     surfaces diverged ("TJOG" vs "T-JOG", "TEACH_IN_HANDLE" vs "TEACH-IN-HANDLE",
    ///     and different unknown-code fallbacks).
    /// </summary>
    /// <param name="mode">The operation mode.</param>
    /// <returns>The operator-facing label.</returns>
    public static string ToText(this FocasOperationMode mode) =>
        FocasOpMode.ToText((short)mode);
}

/// <summary>
///     Letter → <see cref="FocasPmcArea"/> lookup. Used by <see cref="WireFocasClient"/> to
///     translate a parsed <see cref="FocasAddress.PmcLetter"/> into the wire code expected by
///     <c>pmc_rdpmcrng</c>.
/// </summary>
internal static class FocasPmcAreaLookup
{
    /// <summary>Looks up a PMC area code by letter.</summary>
    /// <param name="letter">The PMC area letter (case-insensitive).</param>
    /// <returns>The corresponding FocasPmcArea, or null if not recognized.</returns>
    public static FocasPmcArea? FromLetter(string letter) => letter.ToUpperInvariant() switch
    {
        "G" => FocasPmcArea.G,
        "F" => FocasPmcArea.F,
        "Y" => FocasPmcArea.Y,
        "X" => FocasPmcArea.X,
        "A" => FocasPmcArea.A,
        "R" => FocasPmcArea.R,
        "T" => FocasPmcArea.T,
        "K" => FocasPmcArea.K,
        "C" => FocasPmcArea.C,
        "D" => FocasPmcArea.D,
        "E" => FocasPmcArea.E,
        _ => null,
    };
}

/// <summary>
///     <see cref="FocasDataType"/> → <see cref="FocasPmcDataType"/> mapping for wire PMC
///     reads. Bit reads collapse to byte — the caller extracts the bit from the returned
///     value.
/// </summary>
internal static class FocasPmcDataTypeLookup
{
    /// <summary>Maps a FocasDataType to the corresponding PMC wire data type.</summary>
    /// <param name="t">The FOCAS data type.</param>
    /// <returns>The PMC data type for wire communication.</returns>
    public static FocasPmcDataType FromFocasDataType(FocasDataType t) => t switch
    {
        FocasDataType.Bit or FocasDataType.Byte => FocasPmcDataType.Byte,
        FocasDataType.Int16 => FocasPmcDataType.Word,
        FocasDataType.Int32 => FocasPmcDataType.Long,
        FocasDataType.Float32 => FocasPmcDataType.Real,
        FocasDataType.Float64 => FocasPmcDataType.Double,
        _ => FocasPmcDataType.Byte,
    };
}
