using NPOI.SS.Formula.Functions;
using Sunny.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Check.Main.Common
{
    /// 
    /// 汇川 Easy 系列 PLC 客户端 (Modbus TCP)
    /// 支持 D/M/X/Y 地址读写,整数、浮点数、字符串
    /// 
    public class EasyPlcClient : IDisposable
    {
        private readonly TcpClient _tcpClient = new();
        private NetworkStream _stream;
        private ushort _transactionId = 0;
        private readonly SemaphoreSlim _lock = new(1, 1);
        private readonly string _ip;
        private readonly int _port;
        private readonly byte _slaveId;
        public EasyPlcClient(string ip, int port = 502, byte slaveId = 1)
        {
            _ip = ip;
            _port = port;
            _slaveId = slaveId;
        }
        public bool IsConnected => _tcpClient != null && _tcpClient.Connected;//10.11添加
        public async Task ConnectAsync()
        {
            await _tcpClient.ConnectAsync(_ip, _port);
            _stream = _tcpClient.GetStream();
        }
        #region ---- 基础 Modbus ----
        private ushort GetTransactionId() =>
            unchecked((ushort)Interlocked.Increment(ref Unsafe.As(ref _transactionId)));
        private async Task SendAndReceiveAsync(byte[] pdu, ushort startAddr, ushort length = 1)
        {
            ushort tid = GetTransactionId();
            ushort lengthField = (ushort)(pdu.Length + 1);
            byte[] mbap = {
                (byte)(tid >> 8), (byte)tid,
                0x00, 0x00, // Protocol = 0
                (byte)(lengthField >> 8), (byte)lengthField,
                _slaveId
            };
            byte[] adu = new byte[mbap.Length + pdu.Length];
            Buffer.BlockCopy(mbap, 0, adu, 0, mbap.Length);
            Buffer.BlockCopy(pdu, 0, adu, mbap.Length, pdu.Length);
            await _lock.WaitAsync();
            try
            {
                await _stream.WriteAsync(adu, 0, adu.Length);
                byte[] header = new byte[7];
                await _stream.ReadAsync(header, 0, 7);
                int respLength = (header[4] << 8) + header[5];
                byte[] resp = new byte[respLength - 1];
                await _stream.ReadAsync(resp, 0, resp.Length);
                return resp;
            }
            finally { _lock.Release(); }
        }
        #endregion
        #region ---- 地址解析 ----
        private void ParseAddress(string address, out string area, out ushort offset)
        {
            var letters = Regex.Match(address, @"^[A-Za-z]+").Value.ToUpper();
            var digits = Regex.Match(address, @"\d+").Value;
            if (string.IsNullOrEmpty(letters) || string.IsNullOrEmpty(digits))
                throw new ArgumentException($"地址 {address} 无效");
            area = letters;
            offset = (ushort)(ushort.Parse(digits) - 1);
            // 👉 注意:这里的偏移需根据你的 PLC Modbus 地址表调整
            // 例如 D 区可能是 40001 起,M 区可能是 00001 起
            // 目前假设 D -> Holding Register, M/Y -> Coil, X -> Discrete Input
        }
        #endregion
        #region ---- 整数读写 ----
        public async Task WriteAsync(string address, int value)
        {
            ParseAddress(address, out string area, out ushort offset);
            switch (area)
            {
                case "D": // 写寄存器
                    byte[] pdu = {
                        0x06,
                        (byte)(offset >> 8), (byte)offset,
                        (byte)(value >> 8), (byte)value
                    };
                    await SendAndReceiveAsync(pdu, offset);
                    break;
                case "M":
                case "Y": // 写单线圈
                    byte[] coil = {
                        0x05,
                        (byte)(offset >> 8), (byte)offset,
                        (byte)(value != 0 ? 0xFF : 0x00), 0x00
                    };
                    await SendAndReceiveAsync(coil, offset);
                    break;
                default:
                    throw new NotSupportedException($"写 {area} 区未支持");
            }
        }
        public async Task ReadAsync(string address)
        {
            ParseAddress(address, out string area, out ushort offset);
            switch (area)
            {
                case "D": // Holding Register
                    byte[] pdu = { 0x03, (byte)(offset >> 8), (byte)offset, 0x00, 0x01 };
                    var resp = await SendAndReceiveAsync(pdu, offset);
                    return (resp[1] << 8) + resp[2];
                case "M":
                case "Y": // Coils
                    byte[] pdu1 = { 0x01, (byte)(offset >> 8), (byte)offset, 0x00, 0x01 };
                    var resp1 = await SendAndReceiveAsync(pdu1, offset);
                    return (resp1[2] & 0x01) != 0 ? 1 : 0;
                case "X": // Inputs
                    byte[] pdu2 = { 0x02, (byte)(offset >> 8), (byte)offset, 0x00, 0x01 };
                    var resp2 = await SendAndReceiveAsync(pdu2, offset);
                    return (resp2[2] & 0x01) != 0 ? 1 : 0;
                default:
                    throw new NotSupportedException($"读 {area} 区未支持");
            }
        }
        #endregion
        #region ---- 浮点读写 (2寄存器) ----
        public async Task WriteFloatAsync(string address, float value)
        {
            ParseAddress(address, out string area, out ushort offset);
            if (area != "D") throw new NotSupportedException("浮点仅支持 D 区");
            byte[] bytes = BitConverter.GetBytes(value);
            if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
            byte[] pdu = {
                0x10,
                (byte)(offset >> 8), (byte)offset,
                0x00, 0x02,
                0x04,
                bytes[0], bytes[1], bytes[2], bytes[3]
            };
            await SendAndReceiveAsync(pdu, offset, 2);
        }
        public async Task ReadFloatAsync(string address)
        {
            ParseAddress(address, out string area, out ushort offset);
            if (area != "D") throw new NotSupportedException("浮点仅支持 D 区");
            byte[] pdu = { 0x03, (byte)(offset >> 8), (byte)offset, 0x00, 0x02 };
            var resp = await SendAndReceiveAsync(pdu, offset, 2);
            byte[] data = { resp[1], resp[2], resp[3], resp[4] };
            if (BitConverter.IsLittleEndian) Array.Reverse(data);
            return BitConverter.ToSingle(data, 0);
        }
        #endregion
        #region ---- 字符串读写 ----
        public async Task WriteStringAsync(string address, string value, int maxLength)
        {
            ParseAddress(address, out string area, out ushort offset);
            if (area != "D") throw new NotSupportedException("字符串仅支持 D 区");
            byte[] strBytes = Encoding.ASCII.GetBytes(value);
            if (strBytes.Length > maxLength) Array.Resize(ref strBytes, maxLength);
            // 每寄存器2字节
            int regCount = (strBytes.Length + 1) / 2;
            byte[] data = new byte[regCount * 2];
            Array.Copy(strBytes, data, strBytes.Length);
            byte[] pdu = new byte[7 + data.Length];
            pdu[0] = 0x10;
            pdu[1] = (byte)(offset >> 8); pdu[2] = (byte)offset;
            pdu[3] = (byte)(regCount >> 8); pdu[4] = (byte)regCount;
            pdu[5] = (byte)(data.Length);
            Buffer.BlockCopy(data, 0, pdu, 6, data.Length);
            await SendAndReceiveAsync(pdu, offset, (ushort)regCount);
        }
        public async Task ReadStringAsync(string address, int length)
        {
            ParseAddress(address, out string area, out ushort offset);
            if (area != "D") throw new NotSupportedException("字符串仅支持 D 区");
            int regCount = (length + 1) / 2;
            byte[] pdu = { 0x03, (byte)(offset >> 8), (byte)offset, (byte)(regCount >> 8), (byte)regCount };
            var resp = await SendAndReceiveAsync(pdu, offset, (ushort)regCount);
            byte[] data = new byte[resp[0]];
            Array.Copy(resp, 1, data, 0, data.Length);
            return Encoding.ASCII.GetString(data).TrimEnd('\0');
        }
        #endregion
        public void Dispose()
        {
            _stream?.Dispose();
            _tcpClient?.Close();
            _lock?.Dispose();
        }
    }
}