using Common.Attributes;
using System.Net.Sockets;
using System.Text;

namespace DeviceCommand.Flexible
{
    [ACPCommand]
    public static class FTCP
    {
        private static readonly SemaphoreSlim _commLock = new(1, 1);

        #region Send

        public static async Task SendAsync(string ipAddress,int port,int sendTimeout, byte[] buffer, CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                using var client = new TcpClient();
                client.SendTimeout = sendTimeout;

                await client.ConnectAsync(ipAddress, port, ct);

                using NetworkStream stream = client.GetStream();
                await stream.WriteAsync(buffer, 0, buffer.Length, ct).WaitAsync(TimeSpan.FromMilliseconds(sendTimeout));
            }
            finally
            {
                _commLock.Release();
            }
        }

        public static Task SendAsync(string ipAddress, int port,int sendTimeout, string text,CancellationToken ct = default)
        {
            return SendAsync( ipAddress, port, sendTimeout, Encoding.UTF8.GetBytes(text),ct);
        }

        #endregion

        #region Read

        public static async Task<byte[]> ReadAsync(string ipAddress,int port,int receiveTimeout,int length,CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                using var client = new TcpClient();
                client.ReceiveTimeout = receiveTimeout;

                await client.ConnectAsync(ipAddress, port, ct);

                using NetworkStream stream = client.GetStream();
                byte[] buffer = new byte[length];
                int offset = 0;

                using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
                if (receiveTimeout > 0)
                    cts.CancelAfter(receiveTimeout);

                while (offset < length)
                {
                    int read = await stream.ReadAsync(buffer, offset, length - offset, cts.Token);
                    if (read == 0) break;
                    offset += read;
                }

                return buffer[..offset];
            }
            finally
            {
                _commLock.Release();
            }
        }

        public static async Task<string> ReadLineAsync( string ipAddress,   int port,   int receiveTimeout,  string delimiter = "\n",CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                using var client = new TcpClient();
                client.ReceiveTimeout = receiveTimeout;

                await client.ConnectAsync(ipAddress, port, ct);

                using NetworkStream stream = client.GetStream();
                var sb = new StringBuilder();
                byte[] buffer = new byte[1024];

                using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
                if (receiveTimeout > 0)
                    cts.CancelAfter(receiveTimeout);

                while (!cts.Token.IsCancellationRequested)
                {
                    int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token);
                    if (bytesRead == 0) break;

                    sb.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));

                    int index = sb.ToString().IndexOf(delimiter, StringComparison.Ordinal);
                    if (index >= 0)
                        return sb.ToString(0, index).Trim();
                }

                throw new TimeoutException("读取超时或对端关闭");
            }
            finally
            {
                _commLock.Release();
            }
        }

        #endregion
    }
}
