using DeviceCommand.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BenchMovementModule.HardwareDrive
{
    public abstract class GantryControlBase
    {
        protected readonly IModbusDevice _device;
        protected readonly byte _slaveAddress;

        // 默认 PLC Modbus 映射地址
        protected const ushort ADDR_HM_X_ENABLE = 0xC100; // HM0: X轴使能
        protected const ushort ADDR_HM_Y_ENABLE = 0xC101; // HM1: Y轴使能
        protected const ushort ADDR_HM_Z_ENABLE = 0xC102; // HM2: Z轴使能

        protected const ushort ADDR_M_X_START = 30;       // M30: X轴指定位置启动
        protected const ushort ADDR_M_Y_START = 31;       // M31: Y轴指定位置启动
        protected const ushort ADDR_M_Z_START = 35;       // M35: Z轴指定位置启动

        protected const ushort ADDR_M_ALL_STOP = 40;     // M40: 全停
        protected const ushort ADDR_M_X_STOP = 41;       // M41: X轴停止
        protected const ushort ADDR_M_Y_STOP = 42;       // M42: Y轴停止
        protected const ushort ADDR_M_Z_STOP = 43;       // M43: Z轴停止

        protected const ushort ADDR_M_HOME = 100;        // M100: 回原点触发

        protected const ushort ADDR_HD_X_SPEED = 0xA166;  // HD230: X轴速度 (32位)
        protected const ushort ADDR_HD_Y_SPEED = 0xA17A;  // HD250: Y轴速度 (32位)
        protected const ushort ADDR_HD_Z_SPEED = 0xA170;  // HD240: Z轴速度 (32位)

        protected const ushort ADDR_HD_X_TARGET = 0xA10C; // HD140: X轴指定位置 (32位)
        protected const ushort ADDR_HD_Y_TARGET = 0xA120; // HD160: Y轴指定位置 (32位)
        protected const ushort ADDR_HD_Z_TARGET = 0xA116; // HD150: Z轴指定位置 (32位)

        public bool IsConnected => _device?.IsConnected ?? false;

        protected GantryControlBase(IModbusDevice device, byte slaveAddress = 1)
        {
            _device = device ?? throw new ArgumentNullException(nameof(device));
            _slaveAddress = slaveAddress;
        }

        public async Task<bool> ConnectAsync(CancellationToken ct = default)
        {
            return await _device.ConnectAsync(ct);
        }

        public void Close()
        {
            _device.Close();
        }

        /// <summary>
        /// 轴使能控制
        /// </summary>
        public async Task SetAxisEnableAsync(int axis, bool enable, CancellationToken ct = default)
        {
            ushort address = axis switch
            {
                1 => ADDR_HM_X_ENABLE, // X
                2 => ADDR_HM_Y_ENABLE, // Y
                3 => ADDR_HM_Z_ENABLE, // Z
                _ => throw new ArgumentException("轴通道错误，仅支持 1(X), 2(Y), 3(Z)")
            };

            await _device.WriteSingleCoilAsync(_slaveAddress, address, enable, ct);
        }

        /// <summary>
        /// 左右移动（X轴）
        /// </summary>
        /// <param name="position">目标绝对位置（脉冲数/单位值）</param>
        /// <param name="speed">运行速度</param>
        public async Task MoveLeftRightAsync(int position, int speed, CancellationToken ct = default)
        {
            // 1. 写入速度与目标位置（32位数据，写入连续的2个保持寄存器）
            await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_X_SPEED, Int32ToUshorts(speed), ct);
            await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_X_TARGET, Int32ToUshorts(position), ct);

            // 2. 边缘触发启动信号 M30
            await TriggerCoilAsync(ADDR_M_X_START, ct);
        }

        /// <summary>
        /// 上下移动（Z轴）
        /// </summary>
        /// <param name="position">目标绝对位置</param>
        /// <param name="speed">运行速度</param>
        public async Task MoveUpDownAsync(int position, int speed, CancellationToken ct = default)
        {
            // 1. 写入速度与目标位置（32位数据）
            await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Z_SPEED, Int32ToUshorts(speed), ct);
            await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Z_TARGET, Int32ToUshorts(position), ct);

            // 2. 边缘触发启动信号 M35
            await TriggerCoilAsync(ADDR_M_Z_START, ct);
        }

        /// <summary>
        /// 前后移动（Y轴 - 预留拓展）
        /// </summary>
        public async Task MoveFrontBackAsync(int position, int speed, CancellationToken ct = default)
        {
            await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Y_SPEED, Int32ToUshorts(speed), ct);
            await _device.WriteMultipleRegistersAsync(_slaveAddress, ADDR_HD_Y_TARGET, Int32ToUshorts(position), ct);
            await TriggerCoilAsync(ADDR_M_Y_START, ct);
        }

        /// <summary>
        /// 全局回零
        /// </summary>
        public async Task HomeAsync(CancellationToken ct = default)
        {
            await TriggerCoilAsync(ADDR_M_HOME, ct);
        }

        /// <summary>
        /// 轴停止
        /// </summary>
        /// <param name="axis">1:X轴, 2:Y轴, 3:Z轴, 0:全停</param>
        public async Task StopAsync(int axis = 0, CancellationToken ct = default)
        {
            ushort address = axis switch
            {
                0 => ADDR_M_ALL_STOP,
                1 => ADDR_M_X_STOP,
                2 => ADDR_M_Y_STOP,
                3 => ADDR_M_Z_STOP,
                _ => ADDR_M_ALL_STOP
            };

            await TriggerCoilAsync(address, ct);
        }

        #region 内部工具方法

        /// <summary>
        /// 触发一个点动信号（置 1 后，延时 100ms 自动置 0）
        /// </summary>
        protected async Task TriggerCoilAsync(ushort address, CancellationToken ct = default)
        {
            await _device.WriteSingleCoilAsync(_slaveAddress, address, true, ct);
            await Task.Delay(100, ct); // 给予 PLC 扫描周期足够的响应时间
            await _device.WriteSingleCoilAsync(_slaveAddress, address, false, ct);
        }

        /// <summary>
        /// 将 32 位整型数据转换为 Modbus 的 2 个 16 位无符号整数（高低字转换）
        /// </summary>
        protected ushort[] Int32ToUshorts(int value)
        {
            // 本处采用低字在前（CDAB），如果PLC高字在前（ABCD），可将返回数组顺序颠倒
            ushort lowWord = (ushort)(value & 0xFFFF);
            ushort highWord = (ushort)((value >> 16) & 0xFFFF);
            return new ushort[] { lowWord, highWord };
        }

        #endregion
    }
}
