using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Threading;
using System.Diagnostics;
using System.Net.Sockets;
using System.IO;

namespace SerialDebug
{
    class CSerialDebug
    {
        private Queue<SerialDebugReceiveData> receiveQueue = new Queue<SerialDebugReceiveData>();
        private List<CSendParam> sendList = new List<CSendParam>();

        private SerialPort _serialPort = new SerialPort();
        private bool IsReceiveStart = false;
        private Thread receiveThread;
        private Thread parseThread;
        private bool IsSendStart = false;
        private Thread sendThread;
        private TimeSpan delayTime = new TimeSpan(10 * 100); // 100us
        private int LoopCount = 0;
        private int _ReceiveTimeOut = 3;
        private bool _SendThreadBusy = false;
        public delegate void ReceivedEventHandler(object sender, SerialDebugReceiveData e);
        public event ReceivedEventHandler ReceivedEvent;

        public delegate void SendCompletedEventHandler(object sender, SendCompletedEventArgs e);
        public event SendCompletedEventHandler SendCompletedEvent;
        public event EventHandler SendOverEvent;

        private AutoResetEvent waitReceiveEvent = new AutoResetEvent(false);
        private ManualResetEvent waitParseEvent = new ManualResetEvent(false);
        private ManualResetEvent uartReceivedEvent = new ManualResetEvent(false);

        public CSerialDebug(SerialPort sport)
        {
            _serialPort = sport;
        }

        public SerialPort serialPort
        {
            get { return _serialPort; }
            set { _serialPort = value; }
        }

        public int ReceiveTimeOut
        {
            get { return _ReceiveTimeOut; }
            set { _ReceiveTimeOut = value; }
        }

        public bool SendThreadBusy
        {
            get { return _SendThreadBusy; }
        }

        public void Start()
        {

            try
            {
                IsReceiveStart = true;
                lock (receiveQueue)
                {
                    receiveQueue.Clear();
                }

                lock (sendList)
                {
                    sendList.Clear();
                }

                uartReceivedEvent.Reset();
                waitReceiveEvent.Reset();
                waitParseEvent.Reset();

                if (serialPort.IsOpen)
                {
                    serialPort.Close();
                }
                serialPort.Open();
                serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);

                releaseThread(receiveThread);
                releaseThread(sendThread);

                receiveThread = new Thread(new ThreadStart(ReceiveThreadHandler));
                receiveThread.IsBackground = true;
                receiveThread.Name = "receiveThread";
                receiveThread.Start();

                parseThread = new Thread(new ThreadStart(ParseThreadHandler));
                parseThread.IsBackground = true;
                parseThread.Name = "parseThread";
                parseThread.Start();

                Send(sendList, 1);
            }
            catch (System.Exception ex)
            {
                throw ex;
            }

        }

        public void Stop()
        {
            try
            {
                IsReceiveStart = false;
                IsSendStart = false;
                if (serialPort != null)
                {

                    lock (serialPort)
                    {
                        serialPort.DataReceived -= new SerialDataReceivedEventHandler(serialPort_DataReceived);
                        if (serialPort.IsOpen)
                        {
                            serialPort.Close();
                        }

                    }
                }
                releaseThread(receiveThread);

            }
            catch (System.Exception ex)
            {
                throw ex;
            }

        }

        public void StopReceive()
        {
            IsReceiveStart = false;
        }

        public void StopSend()
        {
            IsSendStart = false;
            waitParseEvent.Set();
            waitParseEvent.Reset();

            while (SendThreadBusy)
            {
                Thread.Sleep(10);
            }

            //releaseThread(sendThread);
        }
        public void Send(List<CSendParam> list)
        {
            LoopCount = 1;
            Send(list, LoopCount);
        }
        public void Send(List<CSendParam> list, int loop)
        {

            if (sendThread != null)
            {
                if (sendThread.IsAlive)
                {
                    releaseThread(sendThread);
                }
            }


            LoopCount = loop;
            lock (sendList)
            {
                sendList.Clear();
            }
            sendList = list;
            IsSendStart = true;

            sendThread = new Thread(new ThreadStart(SendThreadHandler));
            sendThread.IsBackground = true;
            sendThread.Name = "sendThread";
            sendThread.Start();
        }

        private bool SerialPortWrite(byte[] bytes, int offset, int count)
        {
            if (_serialPort != null && _serialPort.IsOpen)
            {
                lock (_serialPort)
                {
                    _serialPort.Write(bytes, offset, count);
                    return true;
                }
            }
            return false;
        }

        void releaseThread(Thread th)
        {
            try
            {
                if (th != null)
                {
                    lock (th)
                    {
                        if (th.IsAlive)
                        {
                            th.Abort();
                        }
                    }
                }
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
        }

        void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (uartReceivedEvent != null)
            {
                uartReceivedEvent.Set();
            }

        }

        /// <summary>
        /// 接收线程
        /// </summary>
        private void ReceiveThreadHandler()
        {

            //byte[] receiveBytes = new byte[4 * 1024];
            while (IsReceiveStart)
            {
                try
                {
                    if (_serialPort.IsOpen)
                    {
                        try
                        {
                            bool IsNeedContinueReceive = false;
                            int dataLen;
                            if (uartReceivedEvent.WaitOne() || IsNeedContinueReceive)
                            {
                                uartReceivedEvent.Reset();

                                //DateTime beginTime = DateTime.Now;
                                int buffSize = 0;
                                do
                                {
                                    buffSize = serialPort.BytesToRead;
                                    if (buffSize >= 4 * 1024)
                                    {
                                        IsNeedContinueReceive = true;
                                        break;
                                    }
                                    Thread.Sleep(ReceiveTimeOut);

                                } while (buffSize != serialPort.BytesToRead);

                                if (buffSize > 0)
                                {
                                    byte[] receiveBytes = new byte[buffSize];
                                    dataLen = serialPort.Read(receiveBytes, 0, receiveBytes.Length);
                                    if (dataLen > 0)
                                    {
                                        byte[] bytes = new byte[dataLen];
                                        Array.Copy(receiveBytes, bytes, dataLen);
                                        lock (receiveQueue)
                                        {
                                            receiveQueue.Enqueue(new SerialDebugReceiveData(bytes));
                                        }
                                        waitReceiveEvent.Set();
                                    }
                                }
                            }

                        }
                        catch (System.Exception ex)
                        {
                            if (ex is ThreadInterruptedException)
                                throw;
                            Debug.WriteLine(ex.ToString());
                        }

                    }
                    else
                    {
                        Thread.Sleep(10);
                    }

                }
                catch (System.Exception ex)
                {
                    if (ex is ThreadInterruptedException)
                        break;
                    Debug.WriteLine(ex.ToString());
                }

            }
        }

        /// <summary>
        /// 接收消息处理线程
        /// </summary>
        private void ParseThreadHandler()
        {
            while (IsReceiveStart)
            {
                try
                {
                    SerialDebugReceiveData data = null;
                    lock (receiveQueue)
                    {
                        if (receiveQueue.Count > 0)
                        {
                            data = receiveQueue.Dequeue();
                        }
                    }

                    if (data != null)
                    {

                        if (ReceivedEvent != null)
                        {
                            ReceivedEvent(this, data);
                        }
                        waitParseEvent.Set();
                    }
                    else
                    {
                        waitReceiveEvent.WaitOne(10);
                    }

                }
                catch (Exception ex)
                {
                    Console.WriteLine("数据处理线程：" + ex.Message);
                }
            }
        }

        /// <summary>
        /// 发送线程
        /// </summary>
        private void SendThreadHandler()
        {
            bool IsFirstSend = true;

            if (LoopCount == 0)
            {
                LoopCount = int.MaxValue;
            }
            try
            {
                while (LoopCount > 0 && IsSendStart)
                {
                    LoopCount--;

                    waitParseEvent.Reset();

                    int index = 0;
                    while (index < sendList.Count && IsSendStart)
                    {
                        _SendThreadBusy = true;

                        CSendParam sendParam = null;
                        lock (sendList)
                        {
                            sendParam = sendList[index];
                        }
                        index++;

                        if (sendParam != null)
                        {
                            bool DelayEnable = true;
                            if (sendParam.Mode == SendParamMode.SendAfterLastSend)
                            {
                                if (IsFirstSend)
                                {
                                    DelayEnable = false;
                                }
                            }
                            else if (sendParam.Mode == SendParamMode.SendAfterReceived)
                            {
                                waitParseEvent.WaitOne();
                            }
                            IsFirstSend = false;

                            if (DelayEnable && sendParam.DelayTime > 0)
                            {
                                DateTime startTime = DateTime.Now;
                                TimeSpan ts = DateTime.Now - startTime;
                                while (ts.TotalMilliseconds < sendParam.DelayTime)
                                {
                                    Thread.Sleep(delayTime);
                                    ts = DateTime.Now - startTime;
                                    if (!IsSendStart)
                                    {
                                        break;
                                    }
                                }
                                ;
                            }

                            if (IsSendStart && SerialPortWrite(sendParam.DataBytes, 0, sendParam.DataBytes.Length))
                            {
                                if (SendCompletedEvent != null)
                                {
                                    SendCompletedEventHandler handler = SendCompletedEvent;
                                    handler(this, new SendCompletedEventArgs(sendParam));
                                }
                            }
                            else
                            {
                                IsSendStart = false;
                            }

                            waitParseEvent.Reset();
                        }
                        _SendThreadBusy = false;
                    }

                }

            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
            finally
            {
                if (SendOverEvent != null)
                {
                    SendOverEvent(this, null);
                }
            }


        }
    }

    public class SerialDebugReceiveData : EventArgs
    {
        private readonly DateTime _ReceiveTime;
        private readonly byte[] _ReceiveData;
        private readonly int _DataLen;

        public SerialDebugReceiveData(byte[] data)
        {
            _ReceiveData = data;
            _ReceiveTime = DateTime.Now;
            if (data != null)
            {
                _DataLen = data.Length;
            }
            else
            {
                _DataLen = 0;
            }
        }

        public byte[] ReceiveData
        {
            get { return _ReceiveData; }
        }

        public DateTime ReceiveTime
        {
            get { return _ReceiveTime; }
        }

        public int DataLen
        {
            get { return _DataLen; }
        }

        public string TimeString
        {
            get
            {
                return string.Format("[{0}.{1:D3}]", _ReceiveTime.ToString("yyyy-MM-dd HH:mm:ss.fff"), _ReceiveTime.Millisecond);
            }
        }

        public string HexString
        {
            get
            {
                return string.Format("{0} ", BitConverter.ToString(_ReceiveData).Replace('-', ' '));
            }
        }

        public string ASCIIString
        {
            get { return System.Text.ASCIIEncoding.Default.GetString(_ReceiveData); }
        }

        public string DecString
        {
            get
            {
                StringBuilder sb = new StringBuilder();

                foreach (byte b in _ReceiveData)
                {
                    sb.AppendFormat("{0} ", Convert.ToInt32(b));
                }

                return sb.ToString();
            }

        }
    }

    public class SendCompletedEventArgs : EventArgs
    {
        private readonly DateTime _SendTime;
        private CSendParam _SendParam;

        public SendCompletedEventArgs(CSendParam sendParam)
        {
            _SendTime = DateTime.Now;
            _SendParam = sendParam;

            //if (sendParam!=null)
            //{
            //    _SendParam = new CSendParam(sendParam.Format,
            //    sendParam.Mode,
            //    sendParam.DelayTime,
            //    sendParam.DataBytes, 0, sendParam.DataLen);
            //}
            //else
            //{
            //    _SendParam = null;
            //}
        }

        public DateTime SendTime
        {
            get { return _SendTime; }
        }

        public CSendParam SendParam
        {
            get { return _SendParam; }
        }

        public string TimeString
        {
            get
            {
                return string.Format("[{0}.{1:D3}]", _SendTime.ToString("yyyy-MM-dd HH:mm:ss"), _SendTime.Millisecond);
            }
        }
    }

}
