using Common.Attributes;
using System;
using System.IO.Ports;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

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

        private static SerialPort CreatePort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int sendTimeout,int receiveTimeout)
        {
            return new SerialPort(portName, baudRate, parity, dataBits, stopBits)
            {
                Encoding = Encoding.UTF8,
                WriteTimeout = sendTimeout > 0 ? sendTimeout : SerialPort.InfiniteTimeout,
                ReadTimeout = receiveTimeout > 0 ? receiveTimeout : SerialPort.InfiniteTimeout
            };
        }

        #region 最常用：只发送字符串

        public static async Task SendAsync(string portName,int baudRate,Parity parity,int dataBits,StopBits stopBits,int sendTimeout,int receiveTimeout,string command,CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                using var port = CreatePort(
                    portName, baudRate, parity, dataBits, stopBits, sendTimeout, receiveTimeout);

                port.Open();

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

                var bytes = port.Encoding.GetBytes(command);
                await port.BaseStream.WriteAsync(bytes, 0, bytes.Length, cts.Token);
                await port.BaseStream.FlushAsync(cts.Token);
            }
            finally
            {
                _commLock.Release();
            }
        }

        #endregion

        #region 最常用：发送字符串并读取字符串

        public static async Task<string> SendReadAsync(string portName,int baudRate, Parity parity, int dataBits, StopBits stopBits,int sendTimeout, int receiveTimeout,string command,string delimiter = "\n", CancellationToken ct = default)
        {
            await _commLock.WaitAsync(ct);
            try
            {
                using var port = CreatePort(
                    portName, baudRate, parity, dataBits, stopBits, sendTimeout, receiveTimeout);

                port.Open();

                // Send
                var sendBytes = port.Encoding.GetBytes(command);
                await port.BaseStream.WriteAsync(sendBytes, 0, sendBytes.Length, ct);
                await port.BaseStream.FlushAsync(ct);

                // Read
                delimiter ??= "\n";
                var sb = new StringBuilder();
                byte[] buffer = new byte[256];

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

                while (!cts.Token.IsCancellationRequested)
                {
                    int read = await port.BaseStream.ReadAsync(
                        buffer, 0, buffer.Length, cts.Token);

                    if (read == 0)
                        break;

                    sb.Append(port.Encoding.GetString(buffer, 0, read));

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

                throw new TimeoutException("串口读取超时");
            }
            finally
            {
                _commLock.Release();
            }
        }

        #endregion
    }
}
