using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using COMM;
using ENIG;
using System.Security.Cryptography;
using AR;
using System.IO.Ports;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;

namespace Project.Device
{
    public enum eDocStep : byte
    {
        NotSet = 0,

        /// <summary>
        /// 투입준비됨
        /// </summary>
        ReadyForEnter = 10,

        /// <summary>
        /// 투입완료
        /// </summary>
        EnterComplete = 11,

        /// <summary>
        /// 투입(진행중)
        /// </summary>
        EnterIng = 12,

        /// <summary>
        /// 진출완료
        /// </summary>
        ExitComplete = 21,

        /// <summary>
        /// 진출중
        /// </summary>
        ExitIng = 22,
    }

    public class Xbee : SerialPort, arDev.ISerialComm
    {
        public string buffer = string.Empty;
        public System.Text.StringBuilder newbuffer = new StringBuilder();
        public string ErrorMessage { get; set; } = string.Empty;
        public DateTime LastStatusSendTime { get; set; } = DateTime.Now;
        private EEProtocol proto;

        public class MessageArgs : EventArgs
        {
            public bool IsError { get; set; }
            public string Message { get; set; }
            public MessageArgs(bool iserr, string m)
            {
                this.IsError = iserr;
                this.Message = m;
            }
        }

        public event EventHandler<MessageArgs> MessageReceived;
        public event EventHandler<EEProtocol.DataEventArgs> ProtocReceived;

        public Xbee()
        {
            this.WriteTimeout = 500;
            this.ReadTimeout = 500;
            this.DataReceived += Xbee_DataReceived;
            proto = new EEProtocol();
            proto.OnDataReceived += Proto_OnDataReceived;
            proto.OnMessage += Proto_OnMessage;
        }
        ~Xbee()
        {
            this.DataReceived -= Xbee_DataReceived;
            proto.OnDataReceived -= Proto_OnDataReceived;
            proto.OnMessage -= Proto_OnMessage;
        }

        public int senderrcnt = 0;
        /// <summary>
        /// 지그비장치에 데이터를 전송합니다
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool Send(byte[] data)
        {
            try
            {
                this.Write(data, 0, data.Length);
                senderrcnt = 0;
                return true;
            }
            catch (Exception ex)
            {
                senderrcnt += 1;
                ErrorMessage = ex.Message;
                return false;
            }
        }
        public new bool Close()
        {
            try
            {
                base.Close();
                return true;
            }
            catch
            {
                return false;
            }

        }
        public new bool Open()
        {
            try
            {
                base.Open();
                senderrcnt = 0;
                return IsOpen;
            }
            catch (Exception ex)
            {
                ErrorMessage = ex.Message;
                PUB.logxbee.AddE(ErrorMessage);
                return false;
            }
        }

        private void Proto_OnDataReceived(object sender, EEProtocol.DataEventArgs e)
        {
            var hexstrRaw = e.ReceivedPacket.RawData.HexString();
            var hexstr = e.ReceivedPacket.Data.HexString();
            var cmd = e.ReceivedPacket.Command.ToString("X2");
            var id = e.ReceivedPacket.ID.ToString("X2");
            PUB.logxbee.Add("RX", $"{hexstrRaw}\nID:{id},CMD:{cmd},DATA:{hexstr}");
            ProtocReceived?.Invoke(this, e);
        }

        private void Proto_OnMessage(object sender, EEProtocol.MessageEventArgs e)
        {
            MessageReceived?.Invoke(this, new MessageArgs(e.IsError, e.Message));
        }

        private void Xbee_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            var dev = sender as System.IO.Ports.SerialPort;
            var buffer = new byte[dev.BytesToRead];
            dev.Read(buffer, 0, buffer.Length);
            proto.ProcessReceivedData(buffer);
            VAR.TIME[eVarTime.LastRecv_XBE] = DateTime.Now;
        }

        /// <summary>
        /// 이동완료 신호 전송
        /// </summary>
        /// <param name="tag">목적지태그값</param>
        public void SendMoveComplete(string tag)
        {
            var id = PUB.setting.XBE_ID;
            byte cmd = (byte)ENIGProtocol.AGVCommandEH.Arrived;
            var data = System.Text.Encoding.Default.GetBytes(tag);
            var packet = proto.CreatePacket(id, cmd, data);
            if (Send(packet))
                PUB.logxbee.AddI($"Send SendMoveComplete [O] : {packet.Length} {packet.HexString()}");
            else
                PUB.logxbee.AddE($"Send SendMoveComplete [X] : {packet.Length} {packet.HexString()} {PUB.XBE.ErrorMessage}");
        }

        StateMachine.ERunStep lastactioncmd = StateMachine.ERunStep.READY;
        DateTime lastactioncmdtime = DateTime.Now;

        /// <summary>
        /// 특정행동이 완료되었을때 전달함
        /// </summary>
        /// <param name="tag"></param>
        public void SendActionComplete(StateMachine.ERunStep actionCmd)
        {
            if (lastactioncmd == actionCmd)
            {
                var ts = DateTime.Now - lastactioncmdtime;
                if(ts.TotalSeconds < 2)
                {
                    return;
                }
            }

            var id = PUB.setting.XBE_ID;
            byte cmd = (byte)ENIGProtocol.AGVCommandEH.ActionComplete;
            var packet = proto.CreatePacket(id, cmd, null);
            if (Send(packet))
                PUB.logxbee.AddI($"SendActionComplete [O] : {packet.Length} {packet.HexString()}");
            else
                PUB.logxbee.AddE($"SendActionComplete [X] : {packet.Length} {packet.HexString()} {PUB.XBE.ErrorMessage}");
            
            //최종액션저장
            lastactioncmd = actionCmd;
            lastactioncmdtime = DateTime.Now;
        }



        /// <summary>
        /// 신규 RFID태그값이 읽혔다면 이명령을 통해서 전송한다
        /// </summary>
        public void SendRFIDTag(string tag)
        {
            var id = PUB.setting.XBE_ID;
            byte cmd = (byte)ENIGProtocol.AGVCommandEH.ReadRFID;
            var data = System.Text.Encoding.Default.GetBytes(tag);
            var packet = proto.CreatePacket(id, cmd, data);
            if (Send(packet))
                PUB.logxbee.AddI($"Send SendRFIDTag [O] : {packet.Length} {packet.HexString()}");
            else
                PUB.logxbee.AddE($"Send SendRFIDTag [X] : {packet.Length} {packet.HexString()} {PUB.XBE.ErrorMessage}");
        }

        /// <summary>
        /// 오류코드를 호스트에 전송합니다
        /// </summary>
        /// <param name="errcode"></param>
        public void SendError(ENIGProtocol.AGVErrorCode errcode)
        {
            // Update global error state so it persists in Status messages
            PUB.Result.RunStepErrorCode = errcode;
            //PUB.Result.ResultMessage = errormessage;

            var id = PUB.setting.XBE_ID;
            byte cmd = (byte)ENIGProtocol.AGVCommandEH.Error;
            // if (errormessage.Length > 30) errormessage = errormessage.Substring(0, 29);

            var data = new List<byte>();
            data.Add((byte)errcode);
            //var datamsg = System.Text.Encoding.Default.GetBytes(errormessage);
            // data.AddRange(datamsg);

            var packet = proto.CreatePacket(id, cmd, data.ToArray());
            if (Send(packet))
                PUB.logxbee.AddI($"Send SendError [O] : {packet.Length} {packet.HexString()}");
            else
                PUB.logxbee.AddE($"Send SendError [X] : {packet.Length} {packet.HexString()} {PUB.XBE.ErrorMessage}");
        }

        public eDocStep StepMC { get; set; } = eDocStep.NotSet;

        ManualResetEvent sendlock = new ManualResetEvent(true);


        /// <summary>
        /// AGV상태를 Xbee 로 전송한다
        /// </summary>
        public void SendStatus()
        {
            if (this.IsOpen == false) return;
            if (sendlock.WaitOne() == false) return;
            sendlock.Reset();

            /*
            Mode[1] : 0=manual, 1=auto
            RunSt[1] : 0=stop, 1=run, 2=error
            RunStep[1] : (byte)PUB.sm.RunStep
            RunStepSeq[1] : (byte)StepMC
            MotorDir[1] : 0=F(Forward), 1=B(Backward)
            MagnetDir[1] : 0=S(Straight), 1=L(Left), 2=R(Right)
            ChargeSt[1] : 0=off, 1=on
            CartSt[1] : 0=off, 1=on, 2=unknown   
            LiftSt[1] : 0=down , 1=up, 2=unknown
            ErrorCode[1] : (byte)PUB.Result.ResultErrorCode
            LastTag[4] : "0000"
            */
            try
            {
                List<byte> data = new List<byte>();
                byte value = 0;
                // Autoron Mode

                value = (byte)(VAR.BOOL[eVarBool.FLAG_AUTORUN] ? 1 : 0);
                data.Add(value);

                // RunSt
                if (PUB.AGV.error.Value > 0)
                    value = 2; // error
                else if (PUB.AGV.system1.agv_run)
                    value = 1; // run
                else
                    value = 0; // stop
                data.Add(value);


                //나르미오류코드전송필요   260202 
                //agv\structure\errorflag.cs 파일의  public enum eflag 에 내용있음
                data.AddRange(BitConverter.GetBytes(PUB.AGV.error.Value));

                //runstep
                value = (byte)PUB.sm.RunStep;
                data.Add(value);

                //runstepseq
                value = (byte)StepMC;
                data.Add(value);

                // Motor Direction
                if (PUB.AGV.data.Direction == 'F')
                    value = 0;
                else if (PUB.AGV.data.Direction == 'B')
                    value = 1;
                else
                    value = 0xff; //unknown
                data.Add(value);

                // Magnet Direction
                if (PUB.AGV.data.Sts == 'L')
                    value = 1; // left
                else if (PUB.AGV.data.Sts == 'R')
                    value = 2; // right
                else if (PUB.AGV.data.Sts == 'S')
                    value = 0; // straight
                else
                    value = 0xFF; //unknown
                data.Add(value);

                // ChargeSt
                value = (byte)((VAR.BOOL[eVarBool.FLAG_CHARGEONA] || VAR.BOOL[eVarBool.FLAG_CHARGEONM]) ? 1 : 0);
                data.Add(value);

                // CartSt
                if (PUB.AGV.signal2.cart_detect1 && PUB.AGV.signal2.cart_detect2)
                    value = 1; // 센서두개가 모두 감지되는 경우
                else if (PUB.AGV.signal2.cart_detect1 == false && PUB.AGV.signal2.cart_detect2 == false)
                    value = 0; // 센서두개가 모두 감지되지 않는 경우
                else
                    value = 2; // 센서하나만 감지되는 경우
                data.Add(value);

                // LiftSt
                if (PUB.AGV.signal1.lift_up)
                    value = 1; // 위로 올라가는 경우
                else if (PUB.AGV.signal1.lift_down)
                    value = 0; // 아래로 내려가는 경우
                else
                    value = 2; // unknown (기본값)
                data.Add(value);

                // ErrorCode [New RunStepErrorCode]
                value = (byte)PUB.Result.RunStepErrorCode;

                //라이다로인해 멈춘경우 오류코드 추가 260204
                if (value == 0 && PUB.AGV.system1.stop_by_front_detect)
                    value = (byte)ENIGProtocol.AGVErrorCode.LIDAR_STOP;
                data.Add(value);

                // LastTag
                string lastTag = PUB.AGV.data.TagNo.ToString("0000") ?? "0000";
                byte[] tagBytes = Encoding.ASCII.GetBytes(lastTag.PadRight(4, '0'));
                data.AddRange(tagBytes);

                // 데이터 전송
                var cmd = (byte)ENIGProtocol.AGVCommandEH.Status;
                var packet = proto.CreatePacket(PUB.setting.XBE_ID, cmd, data.ToArray());
                if (Send(packet))
                    PUB.logxbee.AddI($"Send status [O] : {packet.Length} {packet.HexString()}");
                else
                    PUB.logxbee.AddE($"Send status [X] : {packet.Length} {packet.HexString()} {PUB.XBE.ErrorMessage}");
                LastStatusSendTime = DateTime.Now;
            }
            catch (Exception ex)
            {
                ErrorMessage = ex.Message;
                PUB.logxbee.AddE(ErrorMessage);
            }
            finally
            {
                sendlock.Set();
            }

        }


    }
}
