namespace TLVProtocal;

internal class TlvPackLayer(bool ConvertToBigEnd) : BaseFilter
{
    private readonly ConcurrentDictionary<Connection, List<byte>> _buffer = [];

    public override bool Receive(Data data)
    {
        if (data.RawData is null || data.RawData.Length == 0)
            return false;

        List<byte> bytes;
        lock (_buffer)
            if (!this._buffer.TryGetValue(data.Connection, out bytes) || bytes is null)
            {
                bytes = [];
                this._buffer[data.Connection] = bytes;
            }

        lock (bytes)
            bytes.AddRange(data.RawData);

        for (ushort length; bytes.Count > 2;)
        {
            byte[] message = [.. bytes];

            if (ConvertToBigEnd)
            {
                byte[] lengthByte = new byte[2];
                Array.Copy(message, lengthByte, 2);
                Array.Reverse(lengthByte);
                length = BitConverter.ToUInt16(lengthByte, 0);
            }
            else
                length = BitConverter.ToUInt16(message, 0);

            if (message.Length < length + 2)
                return true;

            byte[] dataBytes = new byte[length];
            bytes.CopyTo(2, dataBytes, 0, length);
            bytes.RemoveRange(0, length + 2);

            Data dataRecived = new(dataBytes, data.Connection, data.DateTime);
            base.Receive(dataRecived);
        }

        return true;
    }

    public override bool Send(Data data)
    {
        if (data.RawData is null || data.RawData.Length < 2)
            return false;

        ushort length = (ushort)data.RawData.Length;

        byte[] lengthByte = BitConverter.GetBytes(length);

        if (ConvertToBigEnd)
            Array.Reverse(lengthByte);

        List<byte> bytes = [];
        bytes.AddRange(lengthByte);
        bytes.AddRange(data.RawData);

        Data sendData = new([.. bytes], data.Connection, data.DateTime);
        return base.Send(sendData);
    }

    public override void Disconnected(Connection connection)
    {
        this._buffer.TryRemove(connection, out _);

        base.Disconnected(connection);
    }

    public override void Connected(Connection connection)
    {
        lock (_buffer)
            if (!this._buffer.TryGetValue(connection, out List<byte> bytes) || bytes is null)
            {
                bytes = [];
                this._buffer[connection] = bytes;
            }
        base.Connected(connection);
    }
}
