using FlyAd2021.Inc;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FlyAd2021
{
    public class FlyAd2021Comm_SerialPort : IFlyAd2021Comm
    {
        /// <summary>
        /// 串口号
        /// </summary>
        public string Addr { get; set; }

        /// <summary>
        /// 是否异常
        /// </summary>
        public bool IsError => !string.IsNullOrEmpty(ErrMsg);

        /// <summary>
        /// 异常信息
        /// </summary>
        public string ErrMsg { get; private set; }

        /// <summary>
        /// Open 成功
        /// </summary>
        public bool IsConnected { get; private set; }

        /// <summary>
        /// 运行中
        /// </summary>
        public bool IsRunning { get; private set; }

        /// <summary>
        /// 接收task调用
        /// </summary>
        public event Action<byte[]> RecMsg;

        public event PropertyChangedEventHandler PropertyChanged;


        SerialPort sp;
        CancellationTokenSource cancellation;
        CancellationTokenSource cancellation_waitforSend;

        public FlyAd2021Comm_SerialPort() 
        {
            sp = new SerialPort();
            sp.BaudRate = 115200 * 2; ;
            sp.Parity = Parity.None;
            sp.StopBits = StopBits.One;
            
        }

        public void SendMsg(byte[] msg)
        {
            if (!IsConnected)
                return;
            try
            {
                sp.Write(msg, 0, msg.Length);
                ErrMsg = null;
            }
            catch (Exception e) {
                ErrMsg = e.Message;
                IsConnected = false;
            }
        }

        void OnTask()
        {
            while (!cancellation.IsCancellationRequested)
            {
                ConnectTask();

                if (IsConnected)
                {
                    //进入接收task
                    ReceiveTask();
                }

                //休息一会儿，在重连
                try
                {
                    Task.Delay(2000, cancellation.Token).Wait();
                }
                catch (Exception e)
                {
                    //被打断了
                    break;
                }
            }

            IsConnected = false;
        }
        /// <summary>
        /// 连接任务
        /// </summary>
        void ConnectTask()
        {
            sp.PortName = Addr;
            
            while (!cancellation.IsCancellationRequested)
            {
                try
                {
                    sp.Open();
                    IsConnected = true;
                    ErrMsg = null;
                    return;
                }
                catch (Exception e)
                {
                    //连接出错
                    //等待重试
                    ErrMsg = e.Message;
                }

                try
                {
                    Task.Delay(2000, cancellation.Token).Wait();
                }
                catch (Exception e)
                {
                    //被打断
                    return;
                }
            }
        }

        /// <summary>
        /// 接收任务
        /// </summary>
        void ReceiveTask()
        {
            sp.DataReceived += Sp_DataReceived;

            while (!cancellation.IsCancellationRequested)
            {
                if (!IsConnected)
                    break;

                try
                {
                    Task.Delay(1000, cancellation.Token).Wait();
                }
                catch (Exception e)
                {
                    //被打断了
                    break;
                }
            }

            sp.DataReceived -= Sp_DataReceived;
        }


        public void Start()
        {
            if (IsRunning)
                return;
            IsRunning = true;

            sp.PortName = Addr;
            cancellation = new CancellationTokenSource();
            Task.Factory.StartNew(OnTask, cancellation.Token);
        }

        private void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int bytesToRead = sp.BytesToRead;
            if (bytesToRead <= 0)
                return;
            byte[] recBuf = new byte[bytesToRead];
            sp.Read(recBuf, 0, bytesToRead);
            RecMsg?.Invoke(recBuf);
        }

        public void Stop()
        {
            if (!IsRunning)
                return;
            IsRunning = false;

            cancellation.Cancel();
            sp.Close();
        }


    }
}
