using System;
using System.IO.Ports;
using System.Text;
using System.Threading.Tasks;

namespace MesWork
{
    /// <summary>
    /// 单把扫码枪的串口通信封装
    /// 包含数据接收（缓冲+空闲提交）、防抖、断线自动重连
    /// </summary>
    public class BarcodeScanner : IDisposable
    {
        // ── 基本属性 ──
        /// <summary>关联工位号（OP10/OP20）</summary>
        public string OpName { get; }

        /// <summary>当前COM口</summary>
        public string ComPort { get; private set; }

        /// <summary>是否已连接</summary>
        public bool IsConnected
        {
            get { try { return _serial != null && _serial.IsOpen; } catch { return false; } }
        }

        /// <summary>最近一次扫到的条码</summary>
        public string LastBarcode { get; private set; } = "";

        /// <summary>最近一次扫码时间</summary>
        public DateTime? LastScanTime { get; private set; }

        // ── 事件 ──
        /// <summary>扫码完成事件（opName, barcode）</summary>
        public event Action<string, string> BarcodeReceived;

        /// <summary>日志事件（message），由外部接入系统日志</summary>
        public event Action<string> Log;

        // ── 私有字段 ──
        private SerialPort _serial;
        private readonly int _baudRate;
        private readonly object _bufferLock = new object();
        private readonly StringBuilder _buffer = new StringBuilder(256);
        private System.Windows.Forms.Timer _flushTimer;
        private const int FlushIntervalMs = 60;

        // 防抖
        private string _lastDebounceCode = "";
        private DateTime _lastDebounceTime = DateTime.MinValue;
        private const int DebounceMs = 500;

        // 断线重连
        private System.Windows.Forms.Timer _reconnectTimer;
        private volatile bool _reconnecting = false;
        private bool _disposed = false;

        public BarcodeScanner(string opName, int baudRate = 9600)
        {
            OpName = opName;
            _baudRate = baudRate;
        }

        /// <summary>
        /// 连接指定COM口
        /// </summary>
        public bool Connect(string comPort)
        {
            if (string.IsNullOrWhiteSpace(comPort) || comPort.Length < 3)
                return false;

            try
            {
                Disconnect();
                ComPort = comPort;

                _serial = new SerialPort
                {
                    PortName = comPort,
                    BaudRate = _baudRate,
                    Parity = Parity.None,
                    StopBits = StopBits.One,
                    ReadTimeout = 1000,
                    DtrEnable = true
                };
                _serial.DataReceived += Serial_DataReceived;
                _serial.Open();

                // 空闲提交定时器
                if (_flushTimer == null)
                {
                    _flushTimer = new System.Windows.Forms.Timer { Interval = FlushIntervalMs };
                    _flushTimer.Tick += FlushTimer_Tick;
                }

                // 断线重连定时器（每3秒检测）
                if (_reconnectTimer == null)
                {
                    _reconnectTimer = new System.Windows.Forms.Timer { Interval = 3000 };
                    _reconnectTimer.Tick += ReconnectTimer_Tick;
                }
                _reconnectTimer.Start();

                Log?.Invoke($"[{OpName}] 扫码枪已连接 ({comPort})");
                return true;
            }
            catch (Exception ex)
            {
                Log?.Invoke($"[{OpName}] 扫码枪连接失败 ({comPort})：{ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            try
            {
                _reconnectTimer?.Stop();
                _flushTimer?.Stop();

                if (_serial != null)
                {
                    _serial.DataReceived -= Serial_DataReceived;
                    if (_serial.IsOpen) _serial.Close();
                    _serial.Dispose();
                    _serial = null;
                }
            }
            catch { }
        }

        /// <summary>
        /// 测试当前COM口是否可连接
        /// </summary>
        public bool TestConnection(string comPort)
        {
            try
            {
                using (var sp = new SerialPort(comPort, _baudRate))
                {
                    sp.Open();
                    sp.Close();
                    return true;
                }
            }
            catch { return false; }
        }

        // ── 串口数据接收 ──
        private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                if (_serial == null || !_serial.IsOpen) return;

                int bytesToRead = _serial.BytesToRead;
                if (bytesToRead <= 0) return;

                byte[] buf = new byte[bytesToRead];
                _serial.Read(buf, 0, bytesToRead);
                string chunk = Encoding.UTF8.GetString(buf, 0, bytesToRead);

                lock (_bufferLock)
                {
                    _buffer.Append(chunk);
                    string acc = _buffer.ToString();

                    // 按换行符（\r \n）拆分完整条码
                    int startIndex = 0;
                    for (int i = 0; i < acc.Length; i++)
                    {
                        if (acc[i] == '\r' || acc[i] == '\n')
                        {
                            int len = i - startIndex;
                            if (len > 0)
                            {
                                string line = acc.Substring(startIndex, len).Trim();
                                if (!string.IsNullOrEmpty(line))
                                    SubmitBarcode(line);
                            }
                            startIndex = i + 1;
                        }
                    }

                    if (startIndex >= acc.Length)
                        _buffer.Clear();
                    else if (startIndex > 0)
                    {
                        _buffer.Clear();
                        _buffer.Append(acc.Substring(startIndex));
                    }

                    // 无换行 → 启动空闲定时器
                    if (_buffer.Length > 0)
                    {
                        try
                        {
                            _flushTimer?.Stop();
                            _flushTimer?.Start();
                        }
                        catch { }
                    }
                }
            }
            catch (Exception ex)
            {
                if (ex is System.IO.IOException || ex is InvalidOperationException)
                {
                    Log?.Invoke($"[{OpName}] 扫码枪通信异常，尝试重连：{ex.Message}");
                    TryReopen();
                }
            }
            finally
            {
                try { _serial?.DiscardInBuffer(); } catch { }
            }
        }

        // 空闲超时提交缓冲区
        private void FlushTimer_Tick(object sender, EventArgs e)
        {
            _flushTimer.Stop();
            string toSend;
            lock (_bufferLock)
            {
                toSend = _buffer.ToString().Trim();
                _buffer.Clear();
            }
            if (!string.IsNullOrEmpty(toSend))
                SubmitBarcode(toSend);
        }

        // 提交条码（含防抖）
        private void SubmitBarcode(string barcode)
        {
            barcode = barcode.Replace("\r", "").Replace("\n", "").Replace("\0", "").Trim();
            if (string.IsNullOrEmpty(barcode)) return;

            // 防抖
            var now = DateTime.Now;
            if (barcode == _lastDebounceCode && (now - _lastDebounceTime).TotalMilliseconds < DebounceMs)
                return;
            _lastDebounceCode = barcode;
            _lastDebounceTime = now;

            // 更新状态
            LastBarcode = barcode;
            LastScanTime = now;

            // 触发事件
            BarcodeReceived?.Invoke(OpName, barcode);
        }

        /// <summary>
        /// 手动发送条码（模拟扫码触发，走完整业务流程）
        /// </summary>
        public void ManualSend(string barcode)
        {
            if (!string.IsNullOrWhiteSpace(barcode))
                SubmitBarcode(barcode.Trim());
        }

        // ── 断线重连 ──
        private void ReconnectTimer_Tick(object sender, EventArgs e)
        {
            if (_reconnecting || string.IsNullOrWhiteSpace(ComPort)) return;
            if (IsConnected) return;

            _reconnecting = true;
            Task.Run(() =>
            {
                try { TryReopen(); }
                catch { }
                finally { _reconnecting = false; }
            });
        }

        private void TryReopen()
        {
            try
            {
                string[] ports = SerialPort.GetPortNames();
                bool exists = Array.Exists(ports, p => string.Equals(p, ComPort, StringComparison.OrdinalIgnoreCase));
                if (!exists) return;

                if (_serial != null)
                {
                    try { if (_serial.IsOpen) _serial.Close(); } catch { }
                    try { _serial.Dispose(); } catch { }
                }

                _serial = new SerialPort
                {
                    PortName = ComPort,
                    BaudRate = _baudRate,
                    Parity = Parity.None,
                    StopBits = StopBits.One,
                    ReadTimeout = 1000,
                    DtrEnable = true
                };
                _serial.DataReceived += Serial_DataReceived;
                _serial.Open();
                Log?.Invoke($"[{OpName}] 扫码枪自动重连成功 ({ComPort})");
            }
            catch (Exception ex)
            {
                Log?.Invoke($"[{OpName}] 扫码枪自动重连失败 ({ComPort})：{ex.Message}");
            }
        }

        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;
            Disconnect();
            _flushTimer?.Dispose();
            _reconnectTimer?.Dispose();
        }
    }
}
