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

namespace DeviceCommand.Base
{
    /// <summary>
    /// ModbusTcp协议
    /// </summary>
    [ATSCommand]
    [DeviceCategory("全部驱动")] // 添加分类属性
    public class ModbusTcp
    {
        public string IPAddress { get; set; } = "127.0.0.1";

        public int Port { get; set; } = 502;

        public int SendTimeout { get; set; } = 3000;

        public int ReceiveTimeout { get; set; } = 3000;

        public TcpClient TcpClient { get; set; } = new();

        public IModbusMaster Modbus { get; set; }

        /// <summary>
        /// 创建ModbusTCP设备对象
        /// </summary>
        /// <param name="ipAddress">IP地址</param>
        /// <param name="port">端口号</param>
        /// <param name="sendTimeout">发送超时时间</param>
        /// <param name="receiveTimeout">接收超时时间</param>
        public ModbusTcp CreateDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000)
        {
            IPAddress = ipAddress;
            Port = port;
            SendTimeout = sendTimeout;
            ReceiveTimeout = receiveTimeout;
            return this;
        }

        /// <summary>
        /// 修改ModbusTCP连接参数
        /// </summary>
        /// <param name="modbusTcp"></param>
        /// <param name="ipAddress">IP地址</param>
        /// <param name="port">端口号</param>
        /// <param name="sendTimeout">发送超时时间</param>
        /// <param name="receiveTimeout">接收超时时间</param>
        public static void ChangeDeviceConfig(ModbusTcp modbusTcp, string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000)
        {
            modbusTcp.IPAddress = ipAddress;
            modbusTcp.Port = port;
            if (sendTimeout > 0)
            {
                modbusTcp.SendTimeout = sendTimeout;
            }
            if (receiveTimeout > 0)
            {
                modbusTcp.ReceiveTimeout = receiveTimeout;
            }
        }

        /// <summary>
        /// 连接
        /// </summary>
        /// <param name="modbusTcp"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        public static async Task<bool> ConnectAsync(ModbusTcp modbusTcp, CancellationToken ct = default)
        {
            if (!modbusTcp.TcpClient.Connected)
            {
                modbusTcp.TcpClient.Close();
                modbusTcp.TcpClient.Dispose();
                modbusTcp.TcpClient = new TcpClient();
                await modbusTcp.TcpClient.ConnectAsync(modbusTcp.IPAddress, modbusTcp.Port, ct);
                modbusTcp.Modbus = new ModbusFactory().CreateMaster(modbusTcp.TcpClient);
            }
            else
            {
                // 获取当前连接的远程端点
                var remoteEndPoint = (IPEndPoint)modbusTcp.TcpClient.Client.RemoteEndPoint!;

                // 比较IP地址和端口
                var ip = remoteEndPoint.Address.MapToIPv4().ToString();
                bool isSameAddress = ip.Equals(modbusTcp.IPAddress);
                bool isSamePort = remoteEndPoint.Port == modbusTcp.Port;

                // 如果端点不匹配则断开重连
                if (!isSameAddress || !isSamePort)
                {
                    modbusTcp.TcpClient.Close();
                    modbusTcp.TcpClient.Dispose();
                    modbusTcp.TcpClient = new TcpClient();
                    await modbusTcp.TcpClient.ConnectAsync(modbusTcp.IPAddress, modbusTcp.Port, ct);
                    modbusTcp.Modbus = new ModbusFactory().CreateMaster(modbusTcp.TcpClient);
                }
            }
            return true;
        }

        /// <summary>
        /// 设备初始化
        /// </summary>
        /// <param name="modbusTcp">ModbusTcp实例</param>
        public static void ModbusTcpInitialize(ModbusTcp modbusTcp)
        {
            try
            {
                if (modbusTcp.TcpClient.Connected)
                {
                    Console.WriteLine("设备已初始化，无需重复初始化");
                    return;
                }

                modbusTcp.TcpClient = new TcpClient();
                modbusTcp.TcpClient.SendTimeout = modbusTcp.SendTimeout;
                modbusTcp.TcpClient.ReceiveTimeout = modbusTcp.ReceiveTimeout;
                Console.WriteLine($"ModbusTCP设备初始化完成");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"设备初始化失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 断开
        /// </summary>
        /// <param name="modbusTcp">ModbusTcp实例</param>
        public static void Close(ModbusTcp modbusTcp)
        {
            try
            {
                if (modbusTcp.TcpClient.Connected)
                {
                    modbusTcp.TcpClient.Close();
                    Console.WriteLine("ModbusTCP连接已断开");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"断开连接失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 读保存寄存器
        /// </summary>
        /// <param name="modbusTcp"></param>
        /// <param name="slaveAddress">设备地址</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="numberOfPoints">读取数量</param>
        /// <param name="ct"></param>
        /// <returns></returns>
        /// <exception cref="TimeoutException"></exception>
        public static async Task<ushort[]> ReadHoldingRegistersAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
        {

            var readTask = modbusTcp.Modbus.ReadHoldingRegistersAsync(slaveAddress, startAddress, numberOfPoints).WaitAsync(ct);
            var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct);

            var completedTask = await Task.WhenAny(readTask, timeoutTask);
            if (completedTask == timeoutTask)
            {
                throw new TimeoutException($"ModbusTCP通讯异常：读取操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
            }

            return await readTask;
        }

        /// <summary>
        /// 写入单个寄存器
        /// </summary>
        /// <param name="modbusTcp"></param>
        /// <param name="slaveAddress">从设备地址</param>
        /// <param name="registerAddress">寄存器地址</param>
        /// <param name="value">值</param>
        /// <param name="ct"></param>
        /// <returns></returns>
        /// <exception cref="TimeoutException"></exception>
        public static async Task WriteSingleRegisterAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort registerAddress, ushort value, CancellationToken ct = default)
        {
            var sendTask = modbusTcp.Modbus.WriteSingleRegisterAsync(slaveAddress, registerAddress, value).WaitAsync(ct);
            var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct);

            var completedTask = await Task.WhenAny(sendTask, timeoutTask);
            if (completedTask == timeoutTask)
            {
                throw new TimeoutException($"ModbusTCP通讯异常：写入操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
            }
        }

        /// <summary>
        /// 写入多个寄存器
        /// </summary>
        /// <param name="modbusTcp"></param>
        /// <param name="slaveAddress">从设备地址</param>
        /// <param name="startAddress">起始寄存器地址</param>
        /// <param name="values">值列表</param>
        /// <param name="ct"></param>
        /// <returns></returns>
        /// <exception cref="TimeoutException"></exception>
        public static async Task WriteMultipleRegistersAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort[] values, CancellationToken ct = default)
        {
            var sendTask = modbusTcp.Modbus.WriteMultipleRegistersAsync(slaveAddress, startAddress, values).WaitAsync(ct);
            var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct);

            var completedTask = await Task.WhenAny(sendTask, timeoutTask);
            if (completedTask == timeoutTask)
            {
                throw new TimeoutException($"ModbusTCP通讯异常：写入操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
            }
        }

        /// <summary>
        /// 读取线圈
        /// </summary>
        /// <param name="modbusTcp">ModbusTcp实例</param>
        /// <param name="slaveAddress">从机地址</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="numberOfPoints">读取数量</param>
        /// <param name="ct">支持中途取消发送指令</param>
        /// <returns>布尔数组</returns>
        public static async Task<bool[]> ReadCoilAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
        {
            try
            {
                var readTask = modbusTcp.Modbus.ReadCoilsAsync(slaveAddress, startAddress, numberOfPoints).WaitAsync(ct);
                var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct);

                var completedTask = await Task.WhenAny(readTask, timeoutTask);
                if (completedTask == timeoutTask)
                {
                    throw new TimeoutException($"ModbusTCP读取线圈操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
                }

                return await readTask;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"读取线圈失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 写入单个线圈
        /// </summary>
        /// <param name="modbusTcp"></param>
        /// <param name="slaveAddress">从设备地址</param>
        /// <param name="coilAddress">线圈地址</param>
        /// <param name="value">值</param>
        /// <param name="ct"></param>
        /// <returns></returns>
        /// <exception cref="TimeoutException"></exception>
        public static async Task WriteSingleCoilAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort coilAddress, bool value, CancellationToken ct = default)
        {
            var sendTask = modbusTcp.Modbus.WriteSingleCoilAsync(slaveAddress, coilAddress, value).WaitAsync(ct);
            var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct);

            var completedTask = await Task.WhenAny(sendTask, timeoutTask);
            if (completedTask == timeoutTask)
            {
                throw new TimeoutException($"ModbusTCP通讯异常：写入操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
            }
        }

        /// <summary>
        /// 写入多个线圈
        /// </summary>
        /// <param name="modbusTcp"></param>
        /// <param name="slaveAddress">从设备地址</param>
        /// <param name="startAddress">起始线圈地址</param>
        /// <param name="values">值列表</param>
        /// <param name="ct"></param>
        /// <returns></returns>
        /// <exception cref="TimeoutException"></exception>
        public static async Task WriteMultipleCoilsAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, bool[] values, CancellationToken ct = default)
        {
            var sendTask = modbusTcp.Modbus.WriteMultipleCoilsAsync(slaveAddress, startAddress, values).WaitAsync(ct);
            var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct);

            var completedTask = await Task.WhenAny(sendTask, timeoutTask);
            if (completedTask == timeoutTask)
            {
                throw new TimeoutException($"ModbusTCP通讯异常：写入操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
            }
        }

        /// <summary>
        /// 紧急停止
        /// </summary>
        /// <param name="modbusTcp">ModbusTcp实例</param>
        public static void ModbusTcpStopNow(ModbusTcp modbusTcp)
        {
            try
            {
                if (modbusTcp.TcpClient.Connected)
                {
                    modbusTcp.TcpClient.Close();
                    Console.WriteLine("紧急停止：ModbusTCP连接已断开");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"紧急停止失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 读取输入寄存器
        /// </summary>
        /// <param name="modbusTcp">ModbusTcp实例</param>
        /// <param name="slaveAddress">从机地址</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="numberOfPoints">读取数量</param>
        /// <param name="ct">支持中途取消发送指令</param>
        /// <returns>ushort数组</returns>
        public static async Task<ushort[]> ReadInputRegisterAsync(ModbusTcp modbusTcp, byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
        {
            try
            {
                var readTask = modbusTcp.Modbus.ReadInputRegistersAsync(slaveAddress, startAddress, numberOfPoints).WaitAsync(ct);
                var timeoutTask = Task.Delay(modbusTcp.ReceiveTimeout, ct);

                var completedTask = await Task.WhenAny(readTask, timeoutTask);
                if (completedTask == timeoutTask)
                {
                    throw new TimeoutException($"ModbusTCP读取输入寄存器操作在 {modbusTcp.ReceiveTimeout} ms内未完成");
                }

                return await readTask;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"读取输入寄存器失败: {ex.Message}");
                throw;
            }
        }
    }
}
