using System.Threading.Tasks;
using UniversalNetFrame451;

namespace TLVProtocal;

internal class TlvCommunicatorServer : BaseFilter, ITlvCommunicatorServer
{
    private ITcpServer _tcpServer;
    private ITlvProvider _provider;

    private Connection _connection;

    private MessageQueue _messageQueue;
    private MessageReceiveQueue _receiveQueue;

    private TlvAckLayer _ackLayer;
    private TlvPackLayer _packLayer;
    private TlvContentLayer _contentLayer;

    private RawDataLog _rawDataLog;

    bool ITlvCommunicatorServer.Initialize(ITlvProvider provider, bool ConvertToBigEnd, int timeoutTime)
    {
        if (this._tcpServer is not null)
            return false;

        this._tcpServer = IOFactory.GetTcpServer();
        this._provider = provider;

        this._ackLayer = new(ConvertToBigEnd, timeoutTime);
        this._packLayer = new(ConvertToBigEnd);
        this._contentLayer = new(ConvertToBigEnd);

        this._messageQueue = new();
        this._messageQueue.Initialize();

        this._receiveQueue = new();
        this._receiveQueue.Initialize();

        this._rawDataLog = new();

        string filePath = Path.Combine(Environment.CurrentDirectory, "Log", "TlvCommunicatorServer.log");
        this._rawDataLog.Initialize(filePath);

        _ = this >> this._contentLayer >> this._receiveQueue >> this._ackLayer >> this._packLayer >> this._messageQueue >> (BaseFilter)this._tcpServer;
        //this._rawDataLog >>
        return true;
    }

    bool ITlvCommunicatorServer.Open(string ip, ushort port)
    {
        if (this._tcpServer is null)
            return false;

        if (this._provider is null)
            return false;

        if (!this._tcpServer.Initialize($"TlvServer {ip}:{port}"))
            return false;

        return this._tcpServer.Open(ip, port);
    }

    bool ITlvCommunicatorServer.Send(TlvData tlvData)
    {
        if (this._tcpServer is null)
            return false;

        if (this._provider is null)
            return false;

        if (this._connection is null)
            return false;

        if (tlvData.Connection is null)
            tlvData = new(tlvData.Tag, tlvData.RawData, this._connection, tlvData.DateTime);

        return base.Send(tlvData);
    }

    public bool Send<T>(byte tag, T data) where T : struct
    {
        if (!StructConverter.TryGetBytes<T>(data, out byte[] alarmBytes) || alarmBytes is null)
            return false;

        TlvData tlv = new(tag, alarmBytes);
        return this.Send(tlv);
    }

    void IDisposable.Dispose()
    {
        this._tcpServer?.Close();
        this._tcpServer = null;

        ((IDisposable)this._messageQueue)?.Dispose();
        ((IDisposable)this._receiveQueue)?.Dispose();

        this._messageQueue = null;
        this._receiveQueue = null;

        this._ackLayer = null;
        this._packLayer = null;
        this._contentLayer = null;
        this._rawDataLog = null;
    }

    #region IFilter
    public override bool Receive(Data data)
    {
        if (data is not TlvData tlv)
            return false;
        if (tlv.RequestID.HasValue)
        {
            TlvData reply = this._provider?.RequestReply(tlv);
            Task.Factory.StartNew(() =>
            {
                base.Send(reply);
            });
        }
        else
            this._provider?.Received(tlv);

        return true;
    }

    public override void Connected(Connection connection)
    {
        if (connection is not TcpConnection tcpConnection)
            return;
        this._connection = connection;

        this._provider?.Connected(tcpConnection);
    }

    public override void Disconnected(Connection connection)
    {
        if (connection is not TcpConnection tcpConnection)
            return;
        this._connection = null;

        this._provider?.Disconnected(tcpConnection);
    }


    #endregion
}
