using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;

/// <summary>
///     Logix atomic + string data types, plus a <see cref="Structure"/> marker used when a tag
///     references a UDT / predefined structure (Timer, Counter, Control). The concrete UDT
///     shape is resolved via the CIP Template Object at discovery time (PR 5 / PR 6).
/// </summary>
/// <remarks>
///     Mirrors the shape of <c>ModbusDataType</c>. Atomic Logix names (BOOL / SINT / INT / DINT /
///     LINT / REAL / LREAL / STRING / DT) map one-to-one; BIT + BOOL-in-DINT collapse into
///     <see cref="Bool"/> with the <c>.N</c> bit-index carried on the <see cref="AbCipTagPath"/>
///     rather than the data type itself.
/// </remarks>
public enum AbCipDataType
{
    Bool,
    SInt,   // signed 8-bit
    Int,    // signed 16-bit
    DInt,   // signed 32-bit
    LInt,   // signed 64-bit
    USInt,  // unsigned 8-bit (Logix 5000 post-V21)
    UInt,   // unsigned 16-bit
    UDInt,  // unsigned 32-bit
    ULInt,  // unsigned 64-bit
    Real,   // 32-bit IEEE-754
    LReal,  // 64-bit IEEE-754
    String, // Logix STRING (DINT Length + SINT[82] DATA — flattened to .NET string by libplctag)
    Dt,     // Date/Time — Logix DT == DINT representing seconds-since-epoch per Rockwell conventions
    /// <summary>
    ///     UDT / Predefined Structure (Timer / Counter / Control / Message / Axis). Shape is
    ///     resolved at discovery time; reads + writes fan out to member Variables unless the
    ///     caller has explicitly opted into whole-UDT decode.
    /// </summary>
    Structure,
}

/// <summary>Map a Logix atomic type to the driver-surface <see cref="DriverDataType"/>.</summary>
public static class AbCipDataTypeExtensions
{
    /// <summary>
    ///     Map to the driver-agnostic type the server's address-space builder consumes. Unsigned
    ///     Logix types widen into signed equivalents until <c>DriverDataType</c> picks up unsigned
    ///     + 64-bit variants (Modbus has the same gap — see <c>ModbusDriver.MapDataType</c>
    ///     comment re: PR 25).
    /// </summary>
    public static DriverDataType ToDriverDataType(this AbCipDataType t) => t switch
    {
        AbCipDataType.Bool => DriverDataType.Boolean,
        AbCipDataType.SInt or AbCipDataType.Int or AbCipDataType.DInt => DriverDataType.Int32,
        AbCipDataType.USInt or AbCipDataType.UInt or AbCipDataType.UDInt => DriverDataType.Int32,
        AbCipDataType.LInt or AbCipDataType.ULInt => DriverDataType.Int32, // TODO: Int64 — matches Modbus gap
        AbCipDataType.Real => DriverDataType.Float32,
        AbCipDataType.LReal => DriverDataType.Float64,
        AbCipDataType.String => DriverDataType.String,
        AbCipDataType.Dt => DriverDataType.Int32, // epoch-seconds DINT
        AbCipDataType.Structure => DriverDataType.String, // placeholder until UDT PR 6 introduces a structured kind
        _ => DriverDataType.Int32,
    };
}
