using Common.Attributes;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using static Common.Attributes.ATSCommandAttribute;

namespace DeviceCommand.Base
{
    /// <summary>
    /// 提供基于 UDP 传输的 Modbus RTU 协议通信能力。
    /// 该类将标准 Modbus RTU 帧（含 CRC16 校验）封装在 UDP 数据包中进行传输，
    /// 适用于支持此非标准模式的工业设备或仿真平台。
    /// </summary>
    [ATSCommand]
    [DeviceCategory("全部驱动")] // 添加分类属性
    public class ModbusRtu_Udp : IDisposable
    {
        private UdpClient _udpClient;
        private IPEndPoint _remoteEndPoint;
        private bool _isConnected = false;
        private readonly object _lock = new();
        private bool _disposed = false;

        /// <summary>
        /// 获取目标设备的 IP 地址。
        /// </summary>
        public string RemoteIpAddress { get; private set; }

        /// <summary>
        /// 获取目标设备的 UDP 端口号。
        /// </summary>
        public int RemotePort { get; private set; }

        /// <summary>
        /// 获取本地 UDP 端口号。如果为 0，则表示使用系统自动分配的端口。
        /// </summary>
        public int LocalPort { get; private set; } = 0; // 0 表示自动分配

        /// <summary>
        /// 获取或设置每次通信的超时时间（毫秒），默认为 3000 毫秒。
        /// </summary>
        public int TimeoutMs { get; set; } = 3000;

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

        /// <summary>
        /// 初始化一个新的 Modbus RTU over UDP 实例。
        /// </summary>
        /// <param name="ipAddress">目标设备的 IPv4 地址。</param>
        /// <param name="remotePort">目标 UDP 端口。</param>
        /// <param name="localPort">本地 UDP 端口。如果为 0，则使用系统自动分配的端口，默认为 0。</param>
        /// <param name="timeoutMs">通信超时时间（毫秒）。</param>
        public ModbusRtu_Udp CreateDevice(string ipAddress, int remotePort, int localPort = 0)
        {
            RemoteIpAddress = ipAddress;
            RemotePort = remotePort;
            LocalPort = localPort; // 存储本地端口
            _remoteEndPoint = new IPEndPoint(IPAddress.Parse(ipAddress), remotePort);
            return this;
        }

        /// <summary>
        /// 修改 Modbus RTU over UDP 实例参数
        /// </summary>
        /// <param name="modbusUdp">ModbusRtu_Udp实例</param>
        /// <param name="ipAddress">IP地址</param>
        /// <param name="remotePort">远程端口号</param>
        /// <param name="localPort">本地端口号。如果为 0，则使用系统自动分配的端口。</param>
        /// <param name="timeoutMs">超时时间（毫秒）</param>
        public static void ChangeDeviceConfig(ModbusRtu_Udp modbusUdp, string ipAddress, int remotePort, int localPort = 0, int timeoutMs = 3000)
        {
            modbusUdp.RemoteIpAddress = ipAddress;
            modbusUdp.RemotePort = remotePort;
            modbusUdp.LocalPort = localPort; // 更新本地端口
            if (timeoutMs > 0)
            {
                modbusUdp.TimeoutMs = timeoutMs;
            }
            // 更新远程端点
            modbusUdp._remoteEndPoint = new IPEndPoint(IPAddress.Parse(ipAddress), remotePort);

            // 如果客户端已连接，需要重新配置
            if (modbusUdp._udpClient != null && modbusUdp._isConnected)
            {
                modbusUdp._udpClient.Client.ReceiveTimeout = modbusUdp.TimeoutMs;
            }
        }

        /// <summary>
        /// 异步初始化 UDP 客户端并标记为已连接状态。
        /// 注意：UDP 是无连接协议，此处“连接”仅为逻辑状态初始化。
        /// 如果 LocalPort 不为 0，则会绑定到指定的本地端口。
        /// </summary>
        /// <param name="ct">支持中途取消操作</param>
        /// <returns>连接结果</returns>
        public async Task<bool> ConnectAsync(CancellationToken ct = default)
        {
            await Task.Run(() =>
            {
                if (_disposed) throw new ObjectDisposedException(nameof(ModbusRtu_Udp));
                Disconnect();
                lock (_lock)
                {
                    // 修改：根据 LocalPort 创建 UdpClient
                    if (LocalPort != 0)
                    {
                        _udpClient = new UdpClient(LocalPort); // 绑定到指定本地端口
                    }
                    else
                    {
                        _udpClient = new UdpClient(); // 使用系统自动分配端口
                    }
                    _udpClient.Client.ReceiveTimeout = TimeoutMs;
                    _isConnected = true;
                }
            }, ct);

            return true; // UdpClient 初始化成功即返回 true
        }

        /// <summary>
        /// 初始化 UDP 客户端并标记为已连接状态。
        /// 注意：UDP 是无连接协议，此处“连接”仅为逻辑状态初始化。
        /// 如果 LocalPort 不为 0，则会绑定到指定的本地端口。
        /// </summary>
        public void Connect()
        {
            if (_disposed) throw new ObjectDisposedException(nameof(ModbusRtu_Udp));
            Disconnect();
            lock (_lock)
            {
                // 修改：根据 LocalPort 创建 UdpClient
                if (LocalPort != 0)
                {
                    _udpClient = new UdpClient(LocalPort); // 绑定到指定本地端口
                }
                else
                {

                    _udpClient = new UdpClient(); // 使用系统自动分配端口
                }
                _udpClient.Client.ReceiveTimeout = TimeoutMs;
                _isConnected = true;
            }
        }

        /// <summary>
        /// 关闭 UDP 客户端并清除连接状态。
        /// </summary>
        public void Disconnect()
        {
            lock (_lock)
            {
                _udpClient?.Close();
                _udpClient = null;
                _isConnected = false;
            }
        }

        /// <summary>
        /// 执行设备初始化操作（当前为占位实现，UDP 无状态故无需特殊初始化）。
        /// </summary>
        public void InitializeDevice()
        {
            // UDP 无状态，保留接口一致性
        }

        /// <summary>
        /// 触发紧急停止：立即断开通信连接。
        /// </summary>
        public void EmergencyStop()
        {
            Disconnect();
        }

        /// <summary>
        /// 异步发送 Modbus RTU 请求帧并通过 UDP 接收响应，支持重试机制。
        /// </summary>
        /// <param name="request">完整的 Modbus RTU 请求帧（含 CRC）。</param>
        /// <param name="ct">用于取消操作的取消令牌。</param>
        /// <returns>接收到的响应字节数组。</returns>
        /// <exception cref="TimeoutException">在指定重试次数内未收到有效响应。</exception>
        public async Task<byte[]> SendRequestAndReceiveAsync(byte[] request, CancellationToken ct)
        {
            if (!_isConnected) throw new InvalidOperationException("设备未连接。");

            for (int attempt = 0; attempt <= MaxRetries; attempt++)
            {
                try
                {
                    await _udpClient.SendAsync(request, request.Length, _remoteEndPoint);
                    var result = await _udpClient.ReceiveAsync().WaitAsync(TimeSpan.FromMilliseconds(TimeoutMs), ct);
                    return result.Buffer;
                }
                catch (Exception ex) when (ex is SocketException || ex is TimeoutException)
                {
                    if (attempt == MaxRetries)
                        throw new TimeoutException($"Modbus RTU over UDP 通信超时，已重试 {MaxRetries} 次。");
                    await Task.Delay(100, ct);
                }
            }
            throw new InvalidOperationException("通信失败。");
        }

        /// <summary>
        /// 计算 Modbus RTU 帧的 CRC16 校验值（小端格式，多项式 0xA001）。
        /// </summary>
        /// <param name="data">待计算 CRC 的字节数组。</param>
        /// <returns>16 位 CRC 校验值。</returns>
        private static ushort CalculateCRC16(byte[] data)
        {
            ushort crc = 0xFFFF;
            for (int i = 0; i < data.Length; i++)
            {
                crc ^= data[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);
            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) return false;
            if (response[0] != expectedSlave) return false;
            if (response[1] != expectedFunction && response[1] != (byte)(expectedFunction | 0x80)) return false;
            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);
            return receivedCrc == calculatedCrc;
        }

        /// <summary>
        /// 计算指定范围数据的 CRC16 校验值。
        /// </summary>
        /// <param name="data">源字节数组。</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>
        /// 异步读取从站的线圈状态（功能码 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 SendRequestAndReceiveAsync(request, ct);

            if (!ValidateResponse(response, slaveAddress, 0x01))
                throw new InvalidDataException("响应校验失败");

            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 SendRequestAndReceiveAsync(request, ct);
            if (!ValidateResponse(response, slaveAddress, 0x05))
                throw new InvalidDataException("写入线圈响应校验失败");
        }

        /// <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 SendRequestAndReceiveAsync(request, ct);
            if (!ValidateResponse(response, slaveAddress, 0x02))
                throw new InvalidDataException("读取离散输入响应校验失败");

            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 SendRequestAndReceiveAsync(request, ct);
            if (!ValidateResponse(response, slaveAddress, 0x03))
                throw new InvalidDataException("读取保持寄存器响应校验失败");

            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 SendRequestAndReceiveAsync(request, ct);
            if (!ValidateResponse(response, slaveAddress, 0x06))
                throw new InvalidDataException("写入寄存器响应校验失败");
        }

        /// <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 SendRequestAndReceiveAsync(request, ct);
            if (!ValidateResponse(response, slaveAddress, 0x04))
                throw new InvalidDataException("读取输入寄存器响应校验失败");

            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>
        /// 释放 UDP 客户端资源并标记为已处置。
        /// </summary>
        public void Dispose()
        {
            if (!_disposed)
            {
                Disconnect();
                _disposed = true;
            }
        }
    }
}