using System.Collections.Concurrent;

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

/// <summary>
///     Cache of UDT shape descriptors keyed by <c>(deviceHostAddress, templateInstanceId)</c>.
///     Populated on demand during discovery + whole-UDT reads; flushed via
///     <see cref="AbCipDriver.FlushOptionalCachesAsync"/> and on device
///     <c>ReinitializeAsync</c>.
/// </summary>
/// <remarks>
///     Template shape read (CIP Template Object class 0x6C, <c>GetAttributeList</c> +
///     <c>Read Template</c>) lands with PR 6. This class ships the cache surface so PR 6 can
///     drop the decoder in without reshaping any caller code.
/// </remarks>
public sealed class AbCipTemplateCache
{
    private readonly ConcurrentDictionary<(string device, uint instanceId), AbCipUdtShape> _shapes = new();

    /// <summary>
    ///     Retrieve a cached UDT shape, or <c>null</c> if not yet read.
    /// </summary>
    public AbCipUdtShape? TryGet(string deviceHostAddress, uint templateInstanceId) =>
        _shapes.TryGetValue((deviceHostAddress, templateInstanceId), out var shape) ? shape : null;

    /// <summary>Store a freshly-decoded UDT shape.</summary>
    public void Put(string deviceHostAddress, uint templateInstanceId, AbCipUdtShape shape) =>
        _shapes[(deviceHostAddress, templateInstanceId)] = shape;

    /// <summary>Drop every cached shape — called on <see cref="AbCipDriver.FlushOptionalCachesAsync"/>.</summary>
    public void Clear() => _shapes.Clear();

    /// <summary>Count of cached shapes — exposed for diagnostics + tests.</summary>
    public int Count => _shapes.Count;
}

/// <summary>
///     Decoded shape of one Logix UDT — member list + each member's offset + type. Populated
///     by PR 6's Template Object reader. At PR 5 time this is the cache's value type only;
///     no reader writes to it yet.
/// </summary>
/// <param name="TypeName">UDT name as reported by the Template Object.</param>
/// <param name="TotalSize">Bytes the UDT occupies in a whole-UDT read buffer.</param>
/// <param name="Members">Ordered list of members, each with its byte offset + type.</param>
public sealed record AbCipUdtShape(
    string TypeName,
    int TotalSize,
    IReadOnlyList<AbCipUdtMember> Members);

/// <summary>One member of a Logix UDT.</summary>
public sealed record AbCipUdtMember(
    string Name,
    int Offset,
    AbCipDataType DataType,
    int ArrayLength);
