using libplctag;

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

/// <summary>
///     libplctag-backed <see cref="IAbCipTemplateReader"/>. Opens the <c>@udt/{templateId}</c>
///     pseudo-tag libplctag exposes for Template Object reads, issues a <c>Read Template</c>
///     internally via a normal read call, + returns the raw byte buffer so
///     <see cref="CipTemplateObjectDecoder"/> can decode it.
/// </summary>
internal sealed class LibplctagTemplateReader : IAbCipTemplateReader
{
    private Tag? _tag;

    /// <inheritdoc />
    public async Task<byte[]> ReadAsync(
        AbCipTagCreateParams deviceParams,
        uint templateInstanceId,
        CancellationToken cancellationToken)
    {
        _tag?.Dispose();
        _tag = new Tag
        {
            Gateway = deviceParams.Gateway,
            Path = deviceParams.CipPath,
            PlcType = MapPlcType(deviceParams.LibplctagPlcAttribute),
            Protocol = Protocol.ab_eip,
            Name = $"@udt/{templateInstanceId}",
            Timeout = deviceParams.Timeout,
        };
        await _tag.InitializeAsync(cancellationToken).ConfigureAwait(false);
        await _tag.ReadAsync(cancellationToken).ConfigureAwait(false);
        return _tag.GetBuffer();
    }

    /// <summary>Disposes the template reader and releases the underlying libplctag tag.</summary>
    public void Dispose() => _tag?.Dispose();

    private static PlcType MapPlcType(string attribute) => attribute switch
    {
        "controllogix" => PlcType.ControlLogix,
        "compactlogix" => PlcType.ControlLogix,
        "micro800" => PlcType.Micro800,
        _ => PlcType.ControlLogix,
    };
}

internal sealed class LibplctagTemplateReaderFactory : IAbCipTemplateReaderFactory
{
    /// <inheritdoc />
    public IAbCipTemplateReader Create() => new LibplctagTemplateReader();
}
