using System.Diagnostics;
using System.IO.Ports;

namespace UHFHelper
{
    public class UHF_RS485_Helper
    {
        private SerialPort? _serialPort;
        private SerialParam? _serialParam;
        public Action OpenOk { get; set; }

        public Action DisConnect { get; set; }

        /// <summary>
        /// 单例模式
        /// </summary>
        private static UHF_RS485_Helper? Instance { get; set; }
        public static UHF_RS485_Helper GetInstance()
        {
            if (Instance == null)
            {
                Instance = new UHF_RS485_Helper();
            }
            return Instance;
        }
        /// <summary>
        /// 打开串口
        /// </summary>
        /// <param name="param"></param>
        public void Open(SerialParam param)
        {
            if (SerialPort.GetPortNames().Contains(param.PortName))
            {
                _serialParam = param;
                _serialPort = new SerialPort(_serialParam.PortName, _serialParam.BaudRate, _serialParam.PortParity, _serialParam.DataBits);
                try
                {
                    _serialPort.Open();
                    if (_serialPort.IsOpen) OpenOk?.Invoke();

                }
                catch (Exception)
                {
                }

            }
        }
        /// <summary>
        /// 获取串口状态
        /// </summary>
        /// <returns></returns>
        public bool GetSerialPortState()
        {
            if (_serialPort != null)
            {
                return _serialPort.IsOpen;
            }
            else
            {
                DisConnect?.Invoke();
                return false;
            }
        }
        /// <summary>
        /// 关闭串口
        /// </summary>
        public void Close()
        {
            if (_serialPort == null) return;
            _serialPort.Close();
            _serialPort.Dispose();
            _serialPort = null;
        }



        /// <summary>
        /// 读卡方法，返回读卡器数据
        /// </summary>
        /// <param name="adr">设备读取地址</param>
        /// <returns></returns>
        public DKoutput? ReadCard(int adr)
        {
            if (_serialPort == null) return null;
            DKoutput dKoutput = new();
            var readByte = ReadByte(adr);
            _serialPort.Write(readByte, 0, readByte.Length);
            var result = SendMessage(readByte, 150, 18);
            if (result == null)
            {
                return null;
            }
            else
            {
                //获取校验码
                var crc = result.Skip(16).Take(2).ToArray();
                //获取卡号
                var cardNo = result.Skip(6).Take(10).ToArray();
                //获取读卡器数据
                var readData = result.Skip(0).Take(16).ToArray();
                //获取读卡器地址
                var address = result.Skip(1).Take(1).ToArray();
                //判断数据是否合法
                var temcrc = CRC16.ToCRC16(readData);
                if (crc.ByteArrayToHexString() == temcrc.ByteArrayToHexString())
                {
                    dKoutput.Address = address.ByteArrayToHexString();
                    dKoutput.ResData = cardNo.ByteArrayToHexString();
                    return dKoutput;
                }
                else
                {
                    return null;
                }
            }
        }
        /// <summary>
        /// 生成读取命令
        /// </summary>
        /// <returns></returns>
        private byte[] ReadByte(int adr)
        {
            //根据读取地址生成读取命令
            byte[] data1 = new byte[3] { 04, (byte)adr, 01 };
            //生成crc校验码
            byte[] data2 = CRC16.ToCRC16(data1);
            byte[] data3 = new byte[data1.Length + data2.Length];
            data1.CopyTo(data3, 0);
            data2.CopyTo(data3, data1.Length);
            return data3;
        }
        /// <summary>
        /// 获取本机可用串口
        /// </summary>
        /// <returns></returns>
        public string[] GetLocalSerialPortNames()
        {
            return SerialPort.GetPortNames();
        }
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="msg">数据</param>
        /// <param name="timeout">超时时间</param>
        /// <param name="count">串口回复字节数</param>
        /// <returns></returns>
        public byte[]? SendMessage(byte[] msg, long timeout, int count)
        {
            if (_serialPort == null) return null;
            var stopwatch = Stopwatch.StartNew();
            try
            {
                _serialPort.DiscardInBuffer();
                _serialPort.Write(msg, 0, msg.Length);
                var tmpCount = count;
                byte[] buffer = new byte[tmpCount];
                int offset = 0;
                while (tmpCount > 0)
                {
                    if (stopwatch.ElapsedMilliseconds >= timeout)
                    {
                        return null;
                    }
                    int res = _serialPort.BytesToRead;
                    if (res > 0)
                    {
                        var readCount = _serialPort.Read(buffer, offset, tmpCount);
                        if (buffer.Skip(0).Take(1).ToArray().ByteArrayToHexString() == "05")
                        {
                            return null;
                        }
                        offset += readCount;
                        tmpCount -= readCount;
                    }
                }
                return buffer;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        /// <summary>
        /// 发送数据
        /// </summary>
        public void SendMessage(int adr)
        {
            var readByte = ReadByte(adr);
            if (_serialPort == null) return;
            _serialPort.Write(readByte, 0, readByte.Length);
        }
        /// <summary>
        /// 读取数据
        /// </summary>
        /// <returns></returns>
        public byte[]? GetMessage(int count, long timeout)
        {
            if (_serialPort == null) return null;
            var stopwatch = Stopwatch.StartNew();
            try
            {
                _serialPort.DiscardInBuffer();
                var tmpCount = count;
                byte[] buffer = new byte[tmpCount];
                int offset = 0;
                while (tmpCount > 0)
                {

                    var readCount = _serialPort.Read(buffer, offset, tmpCount);
                    if (readCount == 6)
                    {
                        tmpCount = 0;
                        continue;
                    }
                    offset += readCount;
                    tmpCount -= readCount;
                    if (stopwatch.ElapsedMilliseconds >= timeout)
                    {
                        return null;
                    }
                }
                return buffer;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            throw new NotImplementedException();
        }

        ///// <summary>
        ///// 发送数据
        ///// </summary>
        ///// <param name="msg">数据</param>
        ///// <param name="timeout">超时时间</param>
        ///// <param name="count">串口回复字节数</param>
        ///// <returns></returns>
        //public async Task<byte[]?> SendMessageAsync(byte[] msg, TimeSpan timeout, int count)
        //{
        //    var sendTask = Task.Run(() => SendMessage(msg, timeout, count));
        //    //try
        //    //{
        //    //    await Task.WhenAny(sendTask, Task.Delay(timeout));
        //    //}
        //    //catch (TaskCanceledException)
        //    //{
        //    //    return null;
        //    //}
        //    return await sendTask;
        //}

        /// <summary>
        /// 发出蜂鸣声音3次,代表扣款成功!
        /// </summary>
        public void OpenBeep(int addr)
        {
            //byte[] sendData = new byte[] { 0x07, (byte)addr, 0x33, 0x08, 0x02, 0x03, 0x33, 0xB1 };
            byte[] data1 = new byte[6] { 0x07, (byte)addr, 0x33, 0x08, 0x02, 0x03 };
            //生成crc校验码
            byte[] data2 = CRC16.ToCRC16(data1);
            byte[] data3 = new byte[data1.Length + data2.Length];
            data1.CopyTo(data3, 0);
            data2.CopyTo(data3, data1.Length);
            if (_serialPort != null)
            {
                _serialPort.DiscardInBuffer();
                _serialPort.Write(data3, 0, data3.Length);
            }
        }
    }

    public class SerialParam
    {
        public string PortName { get; set; } = "COM3";
        public int BaudRate { get; set; } = 57600;
        public int DataBits { get; set; } = 8;
        public Parity PortParity { get; set; } = Parity.None;
        public StopBits PortStopBits { get; set; } = StopBits.One;
    }
}
