using Common.Attributes;
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
using static Common.Attributes.ATSCommandAttribute;

namespace DeviceCommand.Base
{
    /// <summary>
    /// 提供标准串口 Modbus RTU 协议通信能力。
    /// 该类直接管理 SerialPort，并处理 Modbus RTU 帧的构建、发送、接收与 CRC16 校验。
    /// </summary>

    [ATSCommand]
    [DeviceCategory("全部驱动")] // 添加分类属性
    public class ModbusRtu_Serial : IDisposable
    {
        private SerialPort _serialPort;
        private readonly object _lock = new();
        private bool _disposed = false;
        protected internal SemaphoreSlim semaphoreSlimLock { get; set; } = new(1, 1);
        /// <summary>
        /// 获取或设置串口名称（如 "COM1"）。
        /// </summary>
        public string PortName { get; set; }

        /// <summary>
        /// 获取或设置波特率。
        /// </summary>
        public int BaudRate { get; set; }

        /// <summary>
        /// 获取或设置数据位数，默认为 8。
        /// </summary>
        public int DataBits { get; set; } = 8;

        /// <summary>
        /// 获取或设置停止位，默认为 One。
        /// </summary>
        public StopBits StopBits { get; set; } = StopBits.One;

        /// <summary>
        /// 获取或设置奇偶校验位，默认为 None。
        /// </summary>
        public Parity Parity { get; set; } = Parity.None;

        /// <summary>
        /// 获取或设置读取超时时间（毫秒），默认为 3000。
        /// </summary>
        public int ReadTimeout { get; set; } = 3000;

        /// <summary>
        /// 获取或设置写入超时时间（毫秒），默认为 3000。
        /// </summary>
        public int WriteTimeout { get; set; } = 3000;

        /// <summary>
        /// 获取或设置通信失败时的最大重试次数，默认为 3 次。
        /// </summary>
        public int MaxRetries { get; set; } = 3;
        public SerialPort _SerialPort { get; set; } = new SerialPort();

        /// <summary>
        /// 初始化一个新的 Modbus RTU 串口实例。
        /// </summary>
        /// <param name="portName">串口名称，如 "COM1"。</param>
        /// <param name="baudRate">波特率。</param>
        /// <param name="dataBits">数据位，默认 8。</param>
        /// <param name="stopBits">停止位，默认 One。</param>
        /// <param name="parity">奇偶校验，默认 None。</param>
        /// <param name="readTimeout">读取超时（毫秒）。</param>
        /// <param name="writeTimeout">写入超时（毫秒）。</param>
        public ModbusRtu_Serial CreateDevice(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;
            return this;
        }

        /// <summary>
        /// 修改串口实例参数
        /// </summary>
        /// <param name="modbusRtu">ModbusRtu串口实例</param>
        /// <param name="portName">串口名称（如"COM1"）</param>
        /// <param name="baudRate">波特率</param>
        /// <param name="dataBits">数据位</param>
        /// <param name="stopBits">停止位</param>
        /// <param name="parity">校验位（根据设备需求设置）</param>
        /// <param name="readTimeout">读取超时</param>
        /// <param name="writeTimeout">写入超时</param>
        public static void ChangeDeviceConfig(ModbusRtu_Serial modbusRtu, string portName, int baudRate,
            int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None,
            int readTimeout = 3000, int writeTimeout = 3000)
        {
            // 更新配置实例参数
            modbusRtu.PortName = portName;
            modbusRtu.BaudRate = baudRate;
            modbusRtu.DataBits = dataBits;
            modbusRtu.StopBits = stopBits;
            modbusRtu.Parity = parity;

            if (readTimeout > 0)
                modbusRtu.ReadTimeout = readTimeout;
            if (writeTimeout > 0)
                modbusRtu.WriteTimeout = writeTimeout;

            // 如果串口已打开，需要重新配置 SerialPort 对象
            if (modbusRtu._serialPort != null && modbusRtu._serialPort.IsOpen)
            {
                modbusRtu._serialPort.Close();
                modbusRtu._serialPort.PortName = modbusRtu.PortName;
                modbusRtu._serialPort.BaudRate = modbusRtu.BaudRate;
                modbusRtu._serialPort.Parity = modbusRtu.Parity;
                modbusRtu._serialPort.DataBits = modbusRtu.DataBits;
                modbusRtu._serialPort.StopBits = modbusRtu.StopBits;
                modbusRtu._serialPort.ReadTimeout = modbusRtu.ReadTimeout;
                modbusRtu._serialPort.WriteTimeout = modbusRtu.WriteTimeout;
                // 注意：DtrEnable, RtsEnable 等其他属性也可以在此更新，如果需要的话
            }
        }

        /// <summary>
        /// 异步打开串口连接。
        /// </summary>
        /// <param name="ct">支持中途取消操作</param>
        /// <returns>连接结果</returns>
        public static Task<bool> ConnectAsync(ModbusRtu_Serial modbusRtu_Serial, CancellationToken ct = default)
        {

            if (modbusRtu_Serial._SerialPort.PortName != modbusRtu_Serial.PortName
                 || modbusRtu_Serial._SerialPort.BaudRate != modbusRtu_Serial.BaudRate
                 || modbusRtu_Serial._SerialPort.Parity != modbusRtu_Serial.Parity
                 || modbusRtu_Serial._SerialPort.DataBits != modbusRtu_Serial.DataBits
                 || modbusRtu_Serial._SerialPort.StopBits != modbusRtu_Serial.StopBits
                 || modbusRtu_Serial._SerialPort.ReadTimeout != modbusRtu_Serial.ReadTimeout
                 || modbusRtu_Serial._SerialPort.WriteTimeout != modbusRtu_Serial.WriteTimeout)
            {

                // 关闭现有连接并重新配置
                modbusRtu_Serial._SerialPort.Close();

                //更新串口配置
                modbusRtu_Serial._SerialPort.PortName = modbusRtu_Serial.PortName;
                modbusRtu_Serial._SerialPort.BaudRate = modbusRtu_Serial.BaudRate;
                modbusRtu_Serial._SerialPort.Parity = modbusRtu_Serial.Parity;
                modbusRtu_Serial._SerialPort.DataBits = modbusRtu_Serial.DataBits;
                modbusRtu_Serial._SerialPort.StopBits = modbusRtu_Serial.StopBits;
                modbusRtu_Serial._SerialPort.ReadTimeout = modbusRtu_Serial.ReadTimeout;
                modbusRtu_Serial._SerialPort.WriteTimeout = modbusRtu_Serial.WriteTimeout;

                // 重新打开串口
                modbusRtu_Serial._SerialPort.Open();

            }
            else
            {
                // 检查串口是否已打开
                if (!modbusRtu_Serial._SerialPort.IsOpen)
                {
                    // 打开串口
                    modbusRtu_Serial._SerialPort.Open();
                }
            }
            return Task.FromResult(true);
        }

        /// <summary>
        /// 打开串口连接（同步方法）。
        /// </summary>
        public void Connect()
        {
            if (_disposed) throw new ObjectDisposedException(nameof(ModbusRtu_Serial));
            lock (_lock)
            {
                if (_serialPort == null)
                {
                    _serialPort = new SerialPort(PortName, BaudRate, Parity, DataBits, StopBits)
                    {
                        ReadTimeout = ReadTimeout,
                        WriteTimeout = WriteTimeout,
                        // DtrEnable = true, // 可选：根据设备要求设置
                        // RtsEnable = true  // 可选：根据设备要求设置
                    };
                }
                if (!_serialPort.IsOpen)
                {
                    _serialPort.Open();
                }
            }
        }

        /// <summary>
        /// 关闭串口连接。
        /// </summary>
        public void Disconnect()
        {
            lock (_lock)
            {
                if (_serialPort != null && _serialPort.IsOpen)
                {
                    _serialPort.Close();
                }
            }
        }

        /// <summary>
        /// 异步发送请求帧并接收响应帧，支持重试。
        /// </summary>
        /// <param name="requestFrame">待发送的完整 Modbus RTU 请求帧（含 CRC）。</param>
        /// <param name="ct">用于取消操作的取消令牌。</param>
        /// <returns>接收到的响应帧字节数组。</returns>
        private async Task<byte[]> SendAndReceiveFrameAsync(byte[] requestFrame, CancellationToken ct)
        {
            if (_disposed) throw new ObjectDisposedException(nameof(ModbusRtu_Serial));
            if (_serialPort == null || !_serialPort.IsOpen) throw new InvalidOperationException("串口未连接。");

            for (int attempt = 0; attempt <= MaxRetries; attempt++)
            {
                try
                {
                    lock (_lock)
                    {
                        _serialPort.DiscardInBuffer(); // 清空接收缓冲区
                        _serialPort.Write(requestFrame, 0, requestFrame.Length);
                    }

                    // 等待响应，使用超时
                    await Task.Delay(50, ct); // 短暂等待设备响应

                    byte[] responseBuffer = new byte[256]; // 预分配足够大的缓冲区
                    int receivedBytes = 0;
                    int totalBytesToRead = 0;

                    // 等待第一个字节，实现超时
                    var start = DateTime.Now;
                    while (_serialPort.BytesToRead == 0 && (DateTime.Now - start).TotalMilliseconds < ReadTimeout)
                    {
                        await Task.Delay(10, ct);
                    }

                    if (_serialPort.BytesToRead == 0)
                    {
                        if (attempt == MaxRetries)
                            throw new TimeoutException("读取响应超时。");
                        continue; // 重试
                    }

                    // 读取响应
                    while ((DateTime.Now - start).TotalMilliseconds < ReadTimeout)
                    {
                        int bytesAvailable = _serialPort.BytesToRead;
                        if (bytesAvailable > 0)
                        {
                            int read = _serialPort.Read(responseBuffer, receivedBytes, responseBuffer.Length - receivedBytes);
                            receivedBytes += read;
                            if (receivedBytes >= 5) // 最小响应帧长度 (地址 + 功能 + 数据 + CRC)
                            {
                                // 尝试解析帧长度（根据功能码和数据长度）
                                byte func = responseBuffer[1];
                                if ((func & 0x80) != 0) // 异常响应
                                {
                                    totalBytesToRead = 5; // 异常响应固定长度
                                }
                                else
                                {
                                    if (func == 0x01 || func == 0x02) // 读线圈/读离散输入
                                    {
                                        totalBytesToRead = 3 + responseBuffer[2] + 2; // 地址 + 功能 + 字节计数 + 数据 + CRC
                                    }
                                    else if (func == 0x03 || func == 0x04) // 读保持/输入寄存器
                                    {
                                        totalBytesToRead = 3 + (responseBuffer[2] * 2) + 2; // 地址 + 功能 + 字节计数 + (数据 * 2) + CRC
                                    }
                                    else if (func == 0x05 || func == 0x06) // 写单个线圈/寄存器
                                    {
                                        totalBytesToRead = 6; // 响应长度固定 (地址 + 功能 + 起始地址 + CRC)
                                    }
                                    else if (func == 0x10) // 写多个寄存器
                                    {
                                        totalBytesToRead = 6; // 响应长度固定 (地址 + 功能 + 起始地址 + 寄存器数量 + CRC)
                                    }
                                    else
                                    {
                                        totalBytesToRead = receivedBytes; // 无法预知长度，先读到当前为止
                                    }
                                }

                                if (receivedBytes >= totalBytesToRead)
                                {
                                    break; // 接收完成
                                }
                            }
                        }
                        await Task.Delay(10, ct);
                    }

                    if (receivedBytes == 0)
                    {
                        if (attempt == MaxRetries)
                            throw new TimeoutException("读取响应超时。");
                        continue; // 重试
                    }

                    byte[] response = new byte[receivedBytes];
                    Array.Copy(responseBuffer, response, receivedBytes);
                    return response;
                }
                catch (TimeoutException)
                {
                    if (attempt == MaxRetries)
                        throw;
                }
                catch (Exception)
                {
                    if (attempt == MaxRetries)
                        throw;
                    await Task.Delay(100, ct); // 重试前短暂延迟
                }
            }
            throw new InvalidOperationException("通信失败。");
        }

        /// <summary>
        /// 计算 Modbus RTU 帧的 CRC16 校验值（小端格式，多项式 0xA001）。
        /// </summary>
        /// <param name="data">待计算 CRC 的字节数组。</param>
        /// <param name="offset">起始偏移量。</param>
        /// <param name="length">数据长度。</param>
        /// <returns>16 位 CRC 校验值。</returns>
        private static ushort CalculateCRC16(byte[] data, int offset, int length)
        {
            ushort crc = 0xFFFF;
            for (int i = 0; i < length; i++)
            {
                crc ^= data[offset + i];
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                        crc >>= 1;
                }
            }
            return crc;
        }

        /// <summary>
        /// 构建完整的 Modbus RTU 帧（含 CRC16 校验）。
        /// </summary>
        /// <param name="slaveAddress">从站地址（1-247）。</param>
        /// <param name="functionCode">功能码（如 0x01、0x03 等）。</param>
        /// <param name="data">PDU 数据部分（不含地址和功能码）。</param>
        /// <returns>完整的 RTU 帧字节数组。</returns>
        private static byte[] BuildRtuFrame(byte slaveAddress, byte functionCode, byte[] data)
        {
            byte[] pdu = new byte[2 + data.Length];
            pdu[0] = slaveAddress;
            pdu[1] = functionCode;
            Array.Copy(data, 0, pdu, 2, data.Length);

            ushort crc = CalculateCRC16(pdu, 0, pdu.Length);
            byte[] frame = new byte[pdu.Length + 2];
            Array.Copy(pdu, frame, pdu.Length);
            frame[pdu.Length] = (byte)(crc & 0xFF);
            frame[pdu.Length + 1] = (byte)(crc >> 8);
            return frame;
        }

        /// <summary>
        /// 验证接收到的 Modbus RTU 响应帧是否有效。
        /// </summary>
        /// <param name="response">接收到的完整响应帧。</param>
        /// <param name="expectedSlave">期望的从站地址。</param>
        /// <param name="expectedFunction">期望的功能码。</param>
        /// <returns>若帧有效则返回 true；否则抛出异常。</returns>
        /// <exception cref="Exception">当收到异常响应或 CRC 校验失败时抛出。</exception>
        private static bool ValidateResponse(byte[] response, byte expectedSlave, byte expectedFunction)
        {
            if (response.Length < 4) throw new InvalidDataException("响应帧长度不足。");

            if (response[0] != expectedSlave) throw new InvalidDataException("从站地址不匹配。");
            if (response[1] != expectedFunction && response[1] != (byte)(expectedFunction | 0x80))
                throw new InvalidDataException("功能码不匹配。");

            if ((response[1] & 0x80) != 0)
                throw new Exception($"Modbus 异常响应：功能码 {response[1] & 0x7F}，错误码 {response[2]}");

            ushort receivedCrc = (ushort)(response[response.Length - 2] | (response[response.Length - 1] << 8));
            ushort calculatedCrc = CalculateCRC16(response, 0, response.Length - 2);
            if (receivedCrc != calculatedCrc)
                throw new InvalidDataException("响应帧 CRC 校验失败。");

            return true;
        }

        // ————————————————————————
        // 公共 Modbus 功能方法
        // ————————————————————————

        /// <summary>
        /// 异步读取从站的线圈状态（功能码 01）。
        /// </summary>
        /// <param name="slaveAddress">从站地址。</param>
        /// <param name="startAddress">起始线圈地址（0-based）。</param>
        /// <param name="numberOfPoints">要读取的线圈数量。</param>
        /// <param name="ct">取消令牌。</param>
        /// <returns>布尔数组，表示每个线圈的 ON/OFF 状态。</returns>
        public async Task<bool[]> ReadCoilsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
        {
            byte[] data = {
                (byte)(startAddress >> 8), (byte)startAddress,
                (byte)(numberOfPoints >> 8), (byte)numberOfPoints
            };
            byte[] request = BuildRtuFrame(slaveAddress, 0x01, data);
            byte[] response = await SendAndReceiveFrameAsync(request, ct);
            ValidateResponse(response, slaveAddress, 0x01);

            int byteCount = response[2];
            bool[] result = new bool[numberOfPoints];
            for (int i = 0; i < numberOfPoints; i++)
            {
                int byteIndex = i / 8;
                int bitIndex = i % 8;
                result[i] = (response[3 + byteIndex] & (1 << bitIndex)) != 0;
            }
            return result;
        }

        /// <summary>
        /// 异步写入单个线圈状态（功能码 05）。
        /// </summary>
        /// <param name="slaveAddress">从站地址。</param>
        /// <param name="coilAddress">线圈地址（0-based）。</param>
        /// <param name="value">目标值（true = ON, false = OFF）。</param>
        /// <param name="ct">取消令牌。</param>
        /// <returns>任务完成表示写入成功。</returns>
        public async Task WriteSingleCoilAsync(byte slaveAddress, ushort coilAddress, bool value, CancellationToken ct = default)
        {
            ushort coilValue = value ? (ushort)0xFF00 : (ushort)0x0000;
            byte[] data = {
                (byte)(coilAddress >> 8), (byte)coilAddress,
                (byte)(coilValue >> 8), (byte)coilValue
            };
            byte[] request = BuildRtuFrame(slaveAddress, 0x05, data);
            byte[] response = await SendAndReceiveFrameAsync(request, ct);
            ValidateResponse(response, slaveAddress, 0x05);
        }

        /// <summary>
        /// 异步读取从站的离散输入状态（功能码 02）。
        /// </summary>
        /// <param name="slaveAddress">从站地址。</param>
        /// <param name="startAddress">起始输入地址（0-based）。</param>
        /// <param name="numberOfPoints">要读取的输入点数量。</param>
        /// <param name="ct">取消令牌。</param>
        /// <returns>布尔数组，表示每个输入的状态。</returns>
        public async Task<bool[]> ReadDiscreteInputsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
        {
            byte[] data = {
                (byte)(startAddress >> 8), (byte)startAddress,
                (byte)(numberOfPoints >> 8), (byte)numberOfPoints
            };
            byte[] request = BuildRtuFrame(slaveAddress, 0x02, data);
            byte[] response = await SendAndReceiveFrameAsync(request, ct);
            ValidateResponse(response, slaveAddress, 0x02);

            int byteCount = response[2];
            bool[] result = new bool[numberOfPoints];
            for (int i = 0; i < numberOfPoints; i++)
            {
                int byteIndex = i / 8;
                int bitIndex = i % 8;
                result[i] = (response[3 + byteIndex] & (1 << bitIndex)) != 0;
            }
            return result;
        }

        /// <summary>
        /// 异步读取从站的保持寄存器值（功能码 03）。
        /// </summary>
        /// <param name="slaveAddress">从站地址。</param>
        /// <param name="startAddress">起始寄存器地址（0-based）。</param>
        /// <param name="numberOfRegisters">要读取的寄存器数量。</param>
        /// <param name="ct">取消令牌。</param>
        /// <returns>16 位无符号整数数组，表示寄存器值。</returns>
        public async Task<ushort[]> ReadHoldingRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfRegisters, CancellationToken ct = default)
        {
            byte[] data = {
                (byte)(startAddress >> 8), (byte)startAddress,
                (byte)(numberOfRegisters >> 8), (byte)numberOfRegisters
            };
            byte[] request = BuildRtuFrame(slaveAddress, 0x03, data);
            byte[] response = await SendAndReceiveFrameAsync(request, ct);
            ValidateResponse(response, slaveAddress, 0x03);

            ushort[] result = new ushort[numberOfRegisters];
            for (int i = 0; i < numberOfRegisters; i++)
            {
                result[i] = (ushort)((response[3 + i * 2] << 8) | response[4 + i * 2]);
            }
            return result;
        }

        /// <summary>
        /// 异步写入单个保持寄存器（功能码 06）。
        /// </summary>
        /// <param name="slaveAddress">从站地址。</param>
        /// <param name="registerAddress">寄存器地址（0-based）。</param>
        /// <param name="value">要写入的值。</param>
        /// <param name="ct">取消令牌。</param>
        /// <returns>任务完成表示写入成功。</returns>
        public async Task WriteSingleRegisterAsync(byte slaveAddress, ushort registerAddress, ushort value, CancellationToken ct = default)
        {
            byte[] data = {
                (byte)(registerAddress >> 8), (byte)registerAddress,
                (byte)(value >> 8), (byte)value
            };
            byte[] request = BuildRtuFrame(slaveAddress, 0x06, data);
            byte[] response = await SendAndReceiveFrameAsync(request, ct);
            ValidateResponse(response, slaveAddress, 0x06);
        }

        /// <summary>
        /// 异步写入多个保持寄存器（功能码 10 (0x10)）。
        /// </summary>
        /// <param name="slaveAddress">从站地址。</param>
        /// <param name="startAddress">起始寄存器地址（0-based）。</param>
        /// <param name="values">要写入的值数组。</param>
        /// <param name="ct">取消令牌。</param>
        /// <returns>任务完成表示写入成功。</returns>
        public async Task WriteMultipleRegistersAsync(byte slaveAddress, ushort startAddress, ushort[] values, CancellationToken ct = default)
        {
            if (values == null || values.Length == 0)
                throw new ArgumentException("写入值数组不能为空。", nameof(values));

            int byteCount = values.Length * 2;
            byte[] data = new byte[4 + byteCount]; // 起始地址(2) + 寄存器数量(2) + 字节计数(1) + 数据(N)
            data[0] = (byte)(startAddress >> 8);
            data[1] = (byte)(startAddress & 0xFF);
            data[2] = (byte)(values.Length >> 8);
            data[3] = (byte)(values.Length & 0xFF);
            data[4] = (byte)byteCount; // 字节计数

            for (int i = 0; i < values.Length; i++)
            {
                data[5 + i * 2] = (byte)(values[i] >> 8);     // High byte
                data[5 + i * 2 + 1] = (byte)(values[i] & 0xFF); // Low byte
            }

            byte[] request = BuildRtuFrame(slaveAddress, 0x10, data);
            byte[] response = await SendAndReceiveFrameAsync(request, ct);
            ValidateResponse(response, slaveAddress, 0x10);
        }

        /// <summary>
        /// 异步读取从站的输入寄存器值（功能码 04）。
        /// </summary>
        /// <param name="slaveAddress">从站地址。</param>
        /// <param name="startAddress">起始寄存器地址（0-based）。</param>
        /// <param name="numberOfRegisters">要读取的寄存器数量。</param>
        /// <param name="ct">取消令牌。</param>
        /// <returns>16 位无符号整数数组，表示输入寄存器值。</returns>
        public async Task<ushort[]> ReadInputRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfRegisters, CancellationToken ct = default)
        {
            byte[] data = {
                (byte)(startAddress >> 8), (byte)startAddress,
                (byte)(numberOfRegisters >> 8), (byte)numberOfRegisters
            };
            byte[] request = BuildRtuFrame(slaveAddress, 0x04, data);
            byte[] response = await SendAndReceiveFrameAsync(request, ct);
            ValidateResponse(response, slaveAddress, 0x04);

            ushort[] result = new ushort[numberOfRegisters];
            for (int i = 0; i < numberOfRegisters; i++)
            {
                result[i] = (ushort)((response[3 + i * 2] << 8) | response[4 + i * 2]);
            }
            return result;
        }

        /// <summary>
        /// 释放串口资源并标记为已处置。
        /// </summary>
        public void Dispose()
        {
            if (!_disposed)
            {
                Disconnect();
                if (_serialPort != null)
                {
                    _serialPort.Dispose();
                    _serialPort = null;
                }
                _disposed = true;
            }
        }
    }
}