using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Collections;
using COMM;
using AR;
using System.Xml;

namespace arDev
{
    public partial class Narumi : arDev.NarumiSerialComm
    {
        Hashtable SystemCheck, ErrorCheck;
        private Queue Errlog;                                               // 에러발생시 코드 임시 저장용(쓰레드 동기화용)

        public int nBatteryNo { get; set; } = 0;

        public Narumi()
        {
            SystemCheck = new Hashtable();
            SystemCheck.Add(15, "100");
            SystemCheck.Add(14, "9");
            SystemCheck.Add(13, "8");
            SystemCheck.Add(12, "53");
            SystemCheck.Add(11, "3");
            //SystemCheck.Add(10, "66");
            //SystemCheck.Add(9, "56");
            SystemCheck.Add(8, "6");
            SystemCheck.Add(7, "5");
            SystemCheck.Add(6, "60");
            SystemCheck.Add(5, "1");
            SystemCheck.Add(4, "2");
            //SystemCheck.Add(3, "65");
            //SystemCheck.Add(2, "55");
            //SystemCheck.Add(1, "11");
            //SystemCheck.Add(0, "22");

            ErrorCheck = new Hashtable();
            ErrorCheck.Add(15, "17");
            ErrorCheck.Add(14, "15");
            ErrorCheck.Add(13, "18");
            ErrorCheck.Add(12, "19");
            //ErrorCheck.Add(11, "300");
            ErrorCheck.Add(10, "299");
            ErrorCheck.Add(9, "298");
            ErrorCheck.Add(8, "297");
            ErrorCheck.Add(7, "296");
            //ErrorCheck.Add(6,"");
            ErrorCheck.Add(5, "12");
            ErrorCheck.Add(4, "13");
            ErrorCheck.Add(3, "14");
            ErrorCheck.Add(2, "16");
            ErrorCheck.Add(1, "11");
            ErrorCheck.Add(0, "10");

            Errlog = new Queue();
            MinRecvLength = 4;

        }



        #region "External Events"


        /// <summary>
        /// 분석완료된 데이터 이벤트
        /// </summary>
        public event EventHandler<DataEventArgs> DataReceive;
        public event EventHandler<TurnEventArgs> TurnComplete;


        #endregion


        protected override bool CustomParser(byte[] buf, out byte[] remainBuffer)
        {
            List<byte> remain = new List<byte>();

            //데이터는 총 34byt 이며 , DD~77로 구성됨
            Boolean bComplete = false;
            for (int i = 0; i < buf.Length; i++)
            {
                var incomByte = buf[i];
                if (bComplete == true)
                {
                    remain.Add(incomByte);
                }
                else
                {
                    if (incomByte == 0x02)
                    {
                        findSTX = true;
                        tempBuffer.Clear();
                        tempBuffer.Add(incomByte);
                    }
                    else if (incomByte == 0x03)
                    {
                        //데이터가 맞게 수신됨
                        //tempBuffer.Add(incomByte);
                        tempBuffer.Add(incomByte);
                        findSTX = false;
                        bComplete = true;
                    }
                    else
                    {
                        tempBuffer.Add(incomByte);
                        if (tempBuffer.Count == 150) //data overload
                        {
                            findSTX = false;
                            tempBuffer.Clear();
                            bComplete = false;
                            RaiseMessage(MessageType.Error, "Buffer Over");
                        }
                    }
                }
            }

            remainBuffer = remain.ToArray();
            return bComplete;
        }

        public override bool ProcessRecvData(byte[] data)
        {
            //LastReceiveBuffer
            var frame = new Dataframe(data, MinRecvLength);
            if (frame.Valid == false)
            {
                RaiseMessage(MessageType.Error, frame.Message);
                return false;
            }
            else if (frame.DataString.StartsWith("$") == false && CheckSum(data) == false)
            {
                RaiseMessage(MessageType.Error, "Checksum Error MSG=" + frame.DataString);
                return false;
            }

            var retval = true;

            ///////////////////////////////////////////////////////////////////////////////
            if (frame.Cmd.Equals("ACK") || frame.Cmd.Equals("NAK"))
            {                               // 응답확인값 수신 : Header(ACX), Length(9), CheckSum 확인 
                RevACK(frame);
            }
            ///////////////////////////////////////////////////////////////////////////////
            else if (frame.Cmd.Equals("STS"))
            {                              // AGV 상태 정보 : Header(STS), Length(30), CheckSum 확인
                RevSTS(frame);
            }
            /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            else if ((frame.Cmd.Equals("TAG") || frame.Cmd.Equals("CAL") || frame.Cmd.Equals("CCA")))
            {                              // TAG ID 정보 : Header(STX), Length(13), CheckSum 확인
                RevTAG(frame);
            }
            else if (frame.DataString.StartsWith("$"))
            {
                // [Turn Completion Check]
                if (frame.DataString.Contains("LEFT-TURN OK"))
                {
                    if (TurnInformation != null)
                    {
                        TurnInformation.State = eNarumiTurn.Left;
                        TurnInformation.End = DateTime.Now;
                    }
                    TurnComplete?.Invoke(this, new TurnEventArgs(eTurnEvent.Left));
                }
                else if (frame.DataString.Contains("RIGHT-TURN OK"))
                {
                    if (TurnInformation != null)
                    {
                        TurnInformation.State = eNarumiTurn.Right;
                        TurnInformation.End = DateTime.Now;
                    }
                    TurnComplete?.Invoke(this, new TurnEventArgs(eTurnEvent.Right));
                }
                // $로 시작되는 AGV 상태 표시
                //var text_Sts_Etc = Encoding.Default.GetString(bRcvData, 3, bRcvData.Length - 2).TrimStart(' ');   //20210311 김정만 - SmartX FrameWork 사용 안함으로 주석처리
                //var sMessageOther = Encoding.Default.GetString(bRcvData, 3, bRcvData.Length - 2).TrimStart(' ');
                //AGV RIGHT-TURN OK
                
                RaiseMessage(MessageType.Normal, "$메세지수신:" + frame.DataString);
            }
            else
            {
                try
                {
                    DataReceive?.Invoke(this, new DataEventArgs(DataType.UNKNOWN));
                }
                catch (Exception ex)
                {
                    RaiseMessage(MessageType.Error, ex.Message);
                    retval = false;
                }
            }
            return retval;


        }

        #region [수신] ACK(응답확인값) 분석

        public string ACKData { get; set; } = string.Empty;
        public DateTime ACKtime { get; set; } = DateTime.Now;
        private void RevACK(Dataframe frame)
        {
            //ACKSADA7
            var rcvdNow = frame.DataString;
            var bRcvData = frame.Buffer;

            ACKData = rcvdNow.Substring(3);
            ACKtime = DateTime.Now;

            if (rcvdNow.StartsWith("ACK"))
            {
                //RaiseMessage("get ack");
                DataReceive?.Invoke(this, new DataEventArgs(DataType.ACK));
                //if (datamanager.commandQueue.Count != 0)
                //{
                //    byte[] bCmdData = encoding.GetBytes((String)datamanager.commandQueue.Peek());
                //    if (bCmdData[1] == bRcvData[4] && bCmdData[2] == bRcvData[5] && bCmdData[3] == bRcvData[6])  //보낸값이 맞는지 확인후 Dequeue
                //    {
                //        nSendCount = 0;
                //        datamanager.commandQueue.Dequeue();
                //        sSendCommandString = "";
                //    }
                //}
            }
            else if (rcvdNow.StartsWith("NAK"))
            {
                //RaiseMessage("get nak");
                DataReceive?.Invoke(this, new DataEventArgs(DataType.NAK));
                //nSendCount = 0;  //NAK - 재전송이 날아왔을때 nSendCommandCounter 초기화
            }
        }

        #endregion

        public SystemFlag0 system0 = new SystemFlag0();
        public SystemFlag1 system1 = new SystemFlag1();
        public ErrorFlag error = new ErrorFlag();
        public AgvData data = new AgvData();
        public Signal1 signal1 = new Signal1();
        public Signal2 signal2 = new Signal2();

        #region [수신] STS(AGV상태정보) 분석
        public string LastSTS { get; set; } = string.Empty;
        private void RevSTS(Dataframe frame)
        {
            LastSTS = frame.DataString;
            string rcvdNow = frame.DataString.Replace("\0", "");
            byte[] bRcvData = frame.Buffer;
            var encoding = System.Text.Encoding.Default;
            try
            {

                // AGV 베터리 잔량 표시 flag (4~6) /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                var idx = 3;
                var battery = int.Parse(rcvdNow.Substring(idx, 3)) / 10f;// Convert.ToDouble(encoding.GetString(rcvdNow, 3, 3)) / 10;
                idx += 3;

                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // AGV 장치연결상태 flag (7~10) - System Flag 0
                var nDataTemp = Convert.ToInt16(rcvdNow.Substring(idx, 4), 16);
                system0.SetValue(nDataTemp);
                idx += 4;

                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // AGV Sts(현재 상태) flag (11~14) - System Flag 1
                nDataTemp = Convert.ToInt16(rcvdNow.Substring(idx, 4), 16);
                system1.SetValue(nDataTemp);
                idx += 4;

                /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // AGV Error flag (15~18) - Error Flag
                nDataTemp = Convert.ToInt16(rcvdNow.Substring(idx, 4), 16);
                error.SetValue(nDataTemp);
                idx += 4;

                data.Speed = rcvdNow.Substring(idx, 1)[0]; idx += 1;    //L,M.H
                data.Sts = rcvdNow.Substring(idx, 1)[0]; idx += 1;      //S(직진),L(좌분기),R(우분기)
                data.Direction = rcvdNow.Substring(idx, 1)[0]; idx += 1;    //F,B,L,R
                data.guidesensor = int.Parse(rcvdNow.Substring(idx, 1)); idx += 1;  //가이드 좌측부터 1~9

                nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16);
                signal1.SetValue(nDataTemp); idx += 2;

                //agv가 멈춰있고 들어온경우, 턴 작업이었다면 턴 셋팅을 한다
                // [REMOVED] Logic replaced by message-based check ($AGV...TURN OK)
                //if (false && system1.agv_run == false && system1.agv_stop == true &&
                //    TurnInformation != null)
                //{
                //    if (TurnInformation.Start.Year > 2000)
                //    {
                //        if (TurnInformation.StateNew == eNarumiTurn.LeftIng || TurnInformation.StateNew == eNarumiTurn.RightIng)
                //        {
                //            TurnInformation.End = DateTime.Now;
                //            if (TurnInformation.State == eNarumiTurn.LeftIng) TurnInformation.State = eNarumiTurn.Left;
                //            if (TurnInformation.State == eNarumiTurn.RightIng) TurnInformation.State = eNarumiTurn.Right;
                //        }
                //    }
                //    else
                //    {
                //        //시작시간이 설정되지 않았다면 처리하지 않는다.
                //    }
                //}

                if (idx <= rcvdNow.Length - 5)
                {
                    nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16);
                    signal2.SetValue(nDataTemp); idx += 2;
                }

                if (idx <= rcvdNow.Length - 3)
                {
                    var str_dist= rcvdNow.Substring(idx, 3);  //260226
                    if (int.TryParse(str_dist, out int vDist))
                        data.Distance = vDist;
                    else
                        data.Distance = -1;
                    idx += 3;
                }


                DataReceive?.Invoke(this, new DataEventArgs(DataType.STS));

            }
            catch (Exception ex)
            {
                RaiseMessage(MessageType.Error, $"Parse Error(STS) = {ex.Message}");
            }
        }

        #endregion

        #region [수신] TAG(태그정보) 분석

        System.Text.Encoding encoding = System.Text.Encoding.Default;
        string old_TagString = string.Empty;
        string old_CALString = string.Empty;
        string old_CCAString = string.Empty;
        private void RevTAG(Dataframe frame)
        {
            string rcvdNow = frame.DataString;
            byte[] bRcvData = frame.Buffer;
            if (rcvdNow.StartsWith("TAG"))
            {
                //221123    chi 숫자로변경
                var tagnostr = rcvdNow.Substring(3);
                if (ushort.TryParse(tagnostr, out ushort tagnoint))
                {
                    if (TurnInformation != null && TurnInformation.IsTurning)
                    {
                        //턴진행중에 들어온 태그는 무시하자 260226
                        RaiseMessage(MessageType.TagIgnore, $"Tag Ignore by Turn (Tag:{tagnostr})");
                    }
                    else
                    {
                        //var Changed = !old_TagString.Equals(tagnostr);
                        data.TagString = tagnostr;
                        data.TagNo = tagnoint;
                        old_TagString = tagnostr;
                        DataReceive?.Invoke(this, new DataEventArgs(DataType.TAG));
                    }
                }

                WriteData(MakeCheckSum("ACKTAG"));

            }
            else if (rcvdNow.StartsWith("CAL"))
            {

                var tagnostr = encoding.GetString(bRcvData, 4, 4);
                if (int.TryParse(tagnostr, out int tagnoint))
                {
                    var Changed = !old_CALString.Equals(tagnostr);
                    data.CallString = tagnostr;
                    data.CallNo = tagnoint;
                    old_CALString = tagnostr;
                    if (Changed) DataReceive?.Invoke(this, new DataEventArgs(DataType.CALL));
                }
                WriteData(MakeCheckSum("ACKCAL"));
            }
            else if (rcvdNow.StartsWith("CCA")) //능동형 Call에 의하여 들어 오는 구문
            {

                var tagnostr = encoding.GetString(bRcvData, 4, 4);
                if (int.TryParse(tagnostr, out int tagnoint))
                {
                    var Changed = !old_CCAString.Equals(tagnostr);
                    data.CCAString = tagnostr;
                    data.CCANo = tagnoint;
                    old_CCAString = tagnostr;
                    if (Changed) DataReceive?.Invoke(this, new DataEventArgs(DataType.CCA));
                }
                WriteData(MakeCheckSum("ACKCCA"));
            }
        }
        #endregion

        private string MakeCheckSum(string sCommand)
        {
            //sCommand = sCommand.ToUpper();

            List<byte> buffer = new List<byte>();
            buffer.Add(0x02);

            //byte[] bCommand = System.Text.ASCIIEncoding.ASCII.GetBytes(sCommand);
            buffer.AddRange(System.Text.ASCIIEncoding.ASCII.GetBytes(sCommand));

            //make sum
            int nSum = 0;
            for (int nCnt = 1; nCnt < buffer.Count; nCnt++)
                nSum += buffer[nCnt];

            string sSum = nSum.ToString("X2").ToUpper();// Convert.ToString(nSum, 16).ToUpper();
            if (sSum.Length < 2) sSum = "0" + sSum;
            sSum = sSum.Substring(sSum.Length - 2);

            //if (sSum.Length == 3)
            //    sSum = sSum.Remove(0, 1);
            //else if (sSum.Length == 4)
            //    sSum = sSum.Remove(0, 2);

            buffer.AddRange(System.Text.ASCIIEncoding.Default.GetBytes(sSum));
            buffer.Add(0x03);

            //  byte[] bStxEtx = { 0x02, 0x03 };



            //var orgstr = (System.Text.ASCIIEncoding.ASCII.GetString(bStxEtx, 0, 1) + sCommand + sSum + System.Text.ASCIIEncoding.ASCII.GetString(bStxEtx, 1, 1));
            var newstr = System.Text.Encoding.Default.GetString(buffer.ToArray());
            return newstr;

        }

        ///// <summary>
        ///// commandQueue에 명령을 추가합니다(체크섬은 자동 추가됨)
        ///// </summary>
        ///// <param name="cmd">체크섬을 제외한 평문</param>
        ///// <param name="addlog">sendlog 에 추가할 경우 true를 입력하세요</param>
        //public void AddCommand(string cmd, bool addlog = true, int Repeat = 1)
        //{
        //    var fullcmd = MakeCheckSum(cmd);
        //    for (int i = 0; i < Repeat; i++)
        //        commandQueue.Enqueue(fullcmd);
        //    if (addlog)
        //    {
        //        Message?.Invoke(this, new MessageEventArgs()
        //    }
        //}

        public enum eStopOpt
        {
            Stop = 0,
            MarkStop,
            MarkRotateLeft,
            MarkRotateRight,
            MarkChangeDirection,
        }
        public enum eRunOpt
        {
            NotSet,
            Forward,
            Backward,
        }

        public enum ManulOpt
        {
            FS,
            BS,
            RT,
            LT,
            FL,
            FR,
            BL,
            BR,
        }
        public enum Sensor
        {
            PBSOff = 0,
            PBSOn,
            AllOn,
        }
        public enum Speed
        {
            High,
            Mid,
            Low,
        }


        private bool CheckSum(byte[] bData)
        {
            if (bData.Length < 2)       // 데이터 길이가 2이하일 경우 비정상 처리
                return false;

            int nSum = 0;
            // byte[] bData = encoding.GetBytes(sData);

            for (int nCnt = 1; nCnt < bData.Length - 3; nCnt++)
            {
                nSum += bData[nCnt];
            }


            string sSum = Convert.ToString(nSum, 16).ToUpper();

            string sCSTemp = string.Empty;

            if (sSum.Length == 3)
                sCSTemp = sSum.Remove(0, 1);
            else if (sSum.Length == 4)
                sCSTemp = sSum.Remove(0, 2);

            if (bData[bData.Length - 2] == '*' && bData[bData.Length - 3] == '*')
                return true;

            if (sCSTemp[0] == (char)(bData[bData.Length - 3]) && sCSTemp[1] == (char)(bData[bData.Length - 2]))
                return true;
            else
                return false;
        }

    }


}
