using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AssistClient.Utils
{
    public class SSerialPort
    {
        private SerialPort _serialPort;
        private readonly ConcurrentQueue<byte[]> _messageQueue;
        private readonly EventWaitHandle _messageWaitHandle;
        private int _receiveCount, _sendCount;
        private readonly object _lock;
        private int queueSize;
        public int ReceiveCount
        {
            get => _receiveCount;
        }
        public int SendCount
        {
            get => _sendCount;
        }
        public SSerialPort(string com, int baud,int size=-1)
        {
            // initialized
            _lock = new object();
            _receiveCount = 0;
            _sendCount = 0;
            _messageQueue = new ConcurrentQueue<byte[]>();
            _messageWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
            queueSize = size;
            // Open Com
            openCom(com.ToUpper(), baud);

            // Receive byte
            pushMessage();
        }

        private void openCom(string com, int baud)
        {
            // Open Com
            _serialPort = new SerialPort(com, baud);
            if (_serialPort.IsOpen) _serialPort.Close();

            // Set the read / write timeouts
            _serialPort.ReadTimeout = 500;
            _serialPort.WriteTimeout = 500;

            // Set read / write buffer Size，the default of value is 1MB
            _serialPort.ReadBufferSize = 1024 * 1024;
            _serialPort.WriteBufferSize = 1024 * 1024;

            _serialPort.Open();

            // Discard Buffer
            _serialPort.DiscardInBuffer();
            _serialPort.DiscardOutBuffer();
        }


        #region Static
        /// <summary>
        /// 获取当前计算机的串行端口名的数组
        /// </summary>
        /// <returns></returns>
        public static string[] getPortNames()
        {
            return SerialPort.GetPortNames();
        }
        #endregion

        #region Receive
        private void pushMessage()
        {
            _serialPort.DataReceived += (sender, e) =>
            {
                lock (_lock)
                {
                    if (_serialPort.IsOpen == false) return;  
                    int length = _serialPort.BytesToRead; 
                    byte[] buffer = new byte[length];
                    _serialPort.Read(buffer, 0, length);
                    _receiveCount += length;
                    _messageQueue.Enqueue(buffer);

                    byte[] outObj;
                    while (queueSize>0 && _messageQueue.Count > queueSize)
                    {
                        _messageQueue.TryDequeue(out outObj);
                    }
                    _messageWaitHandle.Set();
                }
            };
        }

        /// <summary>
        /// 获取串口接受到的内容
        /// </summary>
        /// <param name="millisecondsToTimeout">取消息的超时时间</param>
        /// <returns>返回byte数组</returns>
        public byte[] getMessage(int millisecondsToTimeout = -1)
        {
            if (_messageQueue.TryDequeue(out var message))
            {
                return message;
            }

            if (_messageWaitHandle.WaitOne(millisecondsToTimeout))
            {
                if (_messageQueue.TryDequeue(out message))
                {
                    return message;
                }
            }
            return default;
        }
        #endregion


        #region Send
        /// <summary>
        /// 发送消息（byte数组）
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        public void send(byte[] buffer, int offset, int count)
        {
            lock (_lock)
            {
                _serialPort.Write(buffer, offset, count);
                //_sendCount += (count - offset);
                _sendCount += count;
            }
        }

        /// <summary>
        /// 发送消息（字符串）
        /// </summary>
        /// <param name="encoding">字符串编码方式，具体方式见<see cref="Encoding"/></param>
        /// <param name="message"></param>
        public void send(Encoding encoding, string message)
        {
            lock (_lock)
            {
                var buffer = encoding.GetBytes(message);
                _serialPort.Write(buffer, 0, buffer.Length);
                _sendCount += buffer.Length;
            }
        }
        #endregion

        /// <summary>
        /// 清空接受/发送总数统计
        /// </summary>
        public void clearCount()
        {
            lock (_lock)
            {
                _sendCount = 0;
                _receiveCount = 0;
            }
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        public void close()
        {
            _serialPort.Close();
        }
    }

}
