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

namespace DeviceCommand.Base
{
    public class Serial_Port : ISerialPort
    {
        public string PortName { get; set; } = "COM1";
        public int BaudRate { get; set; } = 9600;
        public int DataBits { get; set; } = 8;
        public StopBits StopBits { get; set; } = StopBits.One;
        public Parity Parity { get; set; } = Parity.None;
        public int ReadTimeout { get; set; } = 3000;
        public int WriteTimeout { get; set; } = 3000;
        public SerialPort SerialPort { get;  set; } = new SerialPort();
        protected readonly SemaphoreSlim commLock = new(1, 1);

        public void ConfigureDevice(string portName, int baudRate, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000)
        {
            PortName = portName;
            BaudRate = baudRate;
            DataBits = dataBits;
            StopBits = stopBits;
            Parity = parity;
            ReadTimeout = readTimeout;
            WriteTimeout = writeTimeout;
        }
        public virtual async Task<bool> ConnectAsync(CancellationToken ct = default)
        {
            await commLock.WaitAsync(ct);
            try
            {
                if (SerialPort.IsOpen) SerialPort.Close();

                SerialPort.PortName = PortName;
                SerialPort.BaudRate = BaudRate;
                SerialPort.DataBits = DataBits;
                SerialPort.StopBits = StopBits;
                SerialPort.Parity = Parity;
                SerialPort.ReadTimeout = ReadTimeout;
                SerialPort.WriteTimeout = WriteTimeout;

                SerialPort.Open();
                return true;
            }
            finally
            {
                commLock.Release();
            }
        }

        public virtual void Close()
        {
            if (SerialPort.IsOpen) SerialPort.Close();
        }

        public virtual async Task SendAsync(string data, CancellationToken ct = default)
        {
            await commLock.WaitAsync(ct);
            try
            {
                if (!SerialPort.IsOpen)
                {
                    throw new InvalidOperationException("串口未打开，无法进行操作。");
                }
                byte[] bytes = Encoding.UTF8.GetBytes(data);

                var timeoutTask = Task.Delay(WriteTimeout > 0 ? WriteTimeout : Timeout.Infinite, ct);
                var sendTask = Task.Run(() => SerialPort.Write(bytes, 0, bytes.Length), ct);

                var completedTask = await Task.WhenAny(sendTask, timeoutTask);
                if (completedTask == timeoutTask) throw new TimeoutException($"写入操作在 {WriteTimeout} ms内未完成");
                await sendTask;
            }
            finally
            {
                commLock.Release();
            }
        }
        public async Task<string> ReadAsync(CancellationToken ct = default)
        {
            return await myReadAsync(ct: ct).WaitAsync(TimeSpan.FromMilliseconds(ReadTimeout), ct);
        }

        public async Task<string> myReadAsync(string delimiter = "\n", CancellationToken ct = default)
        {
            await commLock.WaitAsync(ct);
            try
            {
                if (!SerialPort.IsOpen)
                {
                    throw new InvalidOperationException("串口未打开，无法进行操作。");
                }

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

                while (!ct.IsCancellationRequested)
                {
                    if (SerialPort.BytesToRead > 0)
                    {
                        int bytesRead = SerialPort.Read(buffer, 0, buffer.Length);
                        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();
                        }
                    }
                    else
                    {
                        await Task.Delay(10, ct); // 避免 CPU 空转
                    }
                }

                throw new OperationCanceledException("读取被取消");
            }
            finally
            {
                commLock.Release();
            }
        }
    }
}
