using Common.Attributes;
using NModbus;
using NModbus.Device;
using NModbus.Serial;


//using DeviceCommand.Interface;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using static Common.Attributes.ATSCommandAttribute;


namespace DeviceCommand.Base
{
    [ATSCommand]
    [DeviceCategory("全部驱动")] // 添加分类属性
    public class ModbusRtu_Tcp
    {
        private TcpClient _tcpClient;
        private NetworkStream _networkStream;
        private readonly object _lock = new();
        private bool _disposed = false;

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

        /// <summary>
        /// 获取目标设备的 TCP 端口号，默认为 502。
        /// </summary>
        public int RemotePort { get; private set; } = 502;

        /// <summary>
        /// 接收和发送的超时时间（毫秒）。
        /// </summary>
        public int ReceiveTimeout { get; set; } = 3000;

        /// <summary>
        /// 发送超时时间
        /// </summary>
        public int SendTimeout { get; set; } = 3000;


        public TcpClient TcpClient
        {
            get => _tcpClient;
            set => _tcpClient = value;
        }

        /// <summary>
        /// 获取内部的 NetworkStream 对象。
        /// </summary>
        public NetworkStream NetworkStream => _networkStream;

        /// <summary>
        /// 初始化一个新的 Modbus RTU over TCP 实例。
        /// </summary>
        /// <param name="ipAddress">目标设备的 IPv4 地址。</param>
        /// <param name="port">目标 TCP 端口，默认为 502。</param>
        /// <param name="sendTimeout">发送超时时间。</param>
        /// <param name="receiveTimeout">接收超时时间。</param>
        public ModbusRtu_Tcp CreateDevice(string ipAddress, int port = 502, int sendTimeout = 3000, int receiveTimeout = 3000)
        {
            RemoteIpAddress = ipAddress;
            RemotePort = port;
            
            return this;
        }

        /// <summary>
        /// 连接到 Modbus RTU over TCP 设备。
        /// </summary>
        /// <summary>
        /// 异步连接到 Modbus RTU over TCP 设备。
        /// </summary>
        /// <param name="modbusTcp">Modbus RTU over TCP 设备对象实例。</param>
        /// <param name="ct">用于取消操作的取消令牌。</param>
        /// <returns>连接结果，成功为 true，失败为 false。</returns>
        public static async Task<bool> ConnectAsync(ModbusRtu_Tcp modbusTcp, CancellationToken ct = default)
        {
            if (modbusTcp._disposed) throw new ObjectDisposedException(nameof(ModbusRtu_Tcp));

            lock (modbusTcp._lock) // 确保线程安全
            {
                if (modbusTcp._tcpClient == null)
                {
                    modbusTcp._tcpClient = new TcpClient();
                }
            }

            // 检查连接状态和端点匹配
            if (!modbusTcp._tcpClient.Connected)
            {
                // 未连接，直接创建新连接
                lock (modbusTcp._lock)
                {
                    modbusTcp._tcpClient.Close(); // 关闭可能存在的旧连接
                    modbusTcp._tcpClient.Dispose();
                    modbusTcp._tcpClient = new TcpClient();
                    modbusTcp._tcpClient.ReceiveTimeout = modbusTcp.ReceiveTimeout;
                    modbusTcp._tcpClient.SendTimeout = modbusTcp.SendTimeout;
                }
                await modbusTcp._tcpClient.ConnectAsync(modbusTcp.RemoteIpAddress, modbusTcp.RemotePort, ct);
            }
            else
            {
                // 已连接，检查端点是否匹配
                var remoteEndPoint = (IPEndPoint)modbusTcp._tcpClient.Client.RemoteEndPoint!;
                var ip = remoteEndPoint.Address.MapToIPv4().ToString();
                bool isSameAddress = ip.Equals(modbusTcp.RemoteIpAddress);
                bool isSamePort = remoteEndPoint.Port == modbusTcp.RemotePort;

                if (!isSameAddress || !isSamePort)
                {
                    // 端点不匹配，断开旧连接并创建新连接
                    lock (modbusTcp._lock)
                    {
                        modbusTcp._tcpClient.Close();
                        modbusTcp._tcpClient.Dispose();
                        modbusTcp._tcpClient = new TcpClient();
                        modbusTcp._tcpClient.ReceiveTimeout = modbusTcp.ReceiveTimeout;
                        modbusTcp._tcpClient.SendTimeout = modbusTcp.SendTimeout;
                    }
                    await modbusTcp._tcpClient.ConnectAsync(modbusTcp.RemoteIpAddress, modbusTcp.RemotePort, ct);
                }
                // 如果端点匹配，则无需操作
            }

            // 连接成功后，初始化 NetworkStream
            lock (modbusTcp._lock)
            {
                if (modbusTcp._tcpClient.Connected)
                {
                    modbusTcp._networkStream = modbusTcp._tcpClient.GetStream();
                    return true;
                }
            }

            return false; // 理论上不应到达此处，除非连接状态判断有误
        }

        /// <summary>
        /// 修改 Modbus RTU over TCP 设备的连接配置。
        /// /// </summary>
        /// <param name="modbusTcp">Modbus RTU over TCP 设备对象实例。</param>
        /// <param name="ipAddress">目标设备的 IPv4 地址。</param>
        /// <param name="port">目标 TCP 端口，默认为 502。</param>
        /// <param name="timeoutMs">通信超时时间（毫秒）。</param>
        
        public static void ChangeDeviceConfig(ModbusRtu_Tcp modbusTcp, string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000)
        {
            modbusTcp.RemoteIpAddress = ipAddress;
            modbusTcp.RemotePort = port;
            modbusTcp.ReceiveTimeout = receiveTimeout;
            modbusTcp.SendTimeout = sendTimeout;
        }

        /// <summary>
        /// 断开与 Modbus RTU over TCP 设备的连接。
        /// </summary>
        public void Disconnect()
        {
            lock (_lock)
            {
                _networkStream?.Close();
                _networkStream = null;
                _tcpClient?.Close();
                _tcpClient = null;
            }
        }

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

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

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

            for (int attempt = 0; attempt <= 3; attempt++)
            {
                try
                {
                    await _networkStream.WriteAsync(requestFrame, 0, requestFrame.Length, ct);

                    // 读取响应长度（最小响应帧长度为 5: 从站地址 + 功能码 + 字节计数 + CRC(2)）
                    // 先读取至少 5 个字节
                    byte[] initialBuffer = new byte[5];
                    int totalRead = 0;
                    while (totalRead < 5)
                    {
                        int n = await _networkStream.ReadAsync(initialBuffer, totalRead, 5 - totalRead, ct);
                        if (n == 0) throw new IOException("连接中断。");
                        totalRead += n;
                    }

                    // 解析功能码和字节计数
                    byte func = initialBuffer[1];
                    int pduDataLength = 0;
                    int expectedTotalLength = 0;

                    if ((func & 0x80) != 0) // 异常响应
                    {
                        expectedTotalLength = 5; // 异常响应固定长度
                    }
                    else
                    {
                        if (func == 0x01 || func == 0x02) // 读线圈/读离散输入
                        {
                            pduDataLength = initialBuffer[2];
                        }
                        else if (func == 0x03 || func == 0x04) // 读保持/输入寄存器
                        {
                            pduDataLength = initialBuffer[2];
                        }
                        // 其他功能码可以继续扩展
                        expectedTotalLength = 2 + 1 + pduDataLength + 2; // 从站地址 + 功能码 + 字节计数(或数据) + CRC(2)
                    }

                    byte[] response = new byte[expectedTotalLength];
                    Array.Copy(initialBuffer, response, 5);

                    if (expectedTotalLength > 5)
                    {
                        totalRead = 5;
                        while (totalRead < expectedTotalLength)
                        {
                            int n = await _networkStream.ReadAsync(response, totalRead, expectedTotalLength - totalRead, ct);
                            if (n == 0) throw new IOException("连接中断。");
                            totalRead += n;
                        }
                    }

                    return response;
                }
                catch (Exception ex) when (ex is SocketException || ex is TimeoutException || ex is IOException)
                {
                    if (attempt == 3)
                        throw new TimeoutException($"Modbus RTU over TCP 通信超时，已重试 3 次。");
                    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 功能方法 (编号 68-70)
        // ————————————————————————

        /// <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 SendRequestAndReceiveFrameAsync(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 SendRequestAndReceiveFrameAsync(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 SendRequestAndReceiveFrameAsync(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 SendRequestAndReceiveFrameAsync(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 SendRequestAndReceiveFrameAsync(request, ct);
            ValidateResponse(response, slaveAddress, 0x06);
        }

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


    }
}
