using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Project
{
    public partial class StateMachine
    {
        public DateTime UpdateTime;
        private Boolean bLoop = true;
        private DateTime StepStartTime;
        private byte _messageOption;
        private Boolean _isRun;
        private System.Threading.Thread worker;
        private Boolean firstRun = false;
        public Boolean isRun { get { return _isRun; } }
        public double Loop_ms { get; set; }
        // private IMCResult MCResult = null;

        public StateMachine()
        {
            UpdateTime = DateTime.Now;
            _messageOption = 0xFF;   //모든메세지가 오도록 한다.
            worker = new System.Threading.Thread(new System.Threading.ThreadStart(Loop));
            worker.IsBackground = false;
            StepStartTime = DateTime.Parse("1982-11-23");
            Loop_ms = 0;
        }

        //public void attachMCResult(IMCResult item)
        //{
        //    MCResult = item;
        //    if (GetMsgOpt(eMsgOpt.NORMAL))
        //        RaiseMessage("ATTACH", string.Format("MCResult"));
        //}

        public void Stop()
        {
            bLoop = false;
            worker.Abort();
        }
        public void Start()
        {
            worker.Start();
        }

        void Loop()
        {
            _isRun = true;
            RaiseMessage("SM", "Start");
            while (bLoop)
            {

                //Console.WriteLine("LOOP-START  => 11111111111   " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));


                //이전업데이트타임과의 시간차를 기록한다
                Loop_ms = (DateTime.Now - UpdateTime).TotalMilliseconds;

                UpdateTime = DateTime.Now;

                //항상 작동하는 경우
                if (SPS != null)
                {
                    //try
                    //{
                        SPS(this,new EventArgs());
                    //}
                    //catch (Exception ex)
                    //{
                    //    Console.WriteLine("ERROR:SM:SPS:" + ex.Message);
                    //}
                }



               // Console.WriteLine("LOOP-START  => 2222222222   " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

                //작동스텝이 변경되었다면 그것을 알림 처리한다.
                if (getNewStep != Step)
                {
                    if (GetMsgOpt(eMsgOpt.STEPCHANGE))
                        RaiseMessage("SM-STEP", string.Format("Step Changed {0} >> {1}", Step, getNewStep));

                    StepApply();
                    firstRun = true;
                    StepStartTime = DateTime.Now;
                }
                else firstRun = false;

               // Console.WriteLine("LOOP-START  => 3333333333   " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

                //동작중에 발생하는 이벤트
                if (Running != null)
                {

                    //try
                    //{
                        Running(this,new RunningEventArgs(Step, firstRun, StepRunTime));
                    //}
                    //catch (Exception ex)
                    //{
                    //    //오류보고는 3초에 한번씩 동작한다
                    //    if(Pub.GetVarRunTime(eVarTime.SMRUNERROR).TotalSeconds > 3)
                    //    {
                    //        Pub.SetVarTime(eVarTime.SMRUNERROR, DateTime.Now);
                    //        Pub.log.AddE("상태머신 RUN 오류:" + ex.Message);
                    //    }   
                    //}
                }

                System.Threading.Thread.Sleep(1);

               // Console.WriteLine("LOOP-END  =>  4444444444444  " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

            }
            _isRun = false;
            if (GetMsgOpt(eMsgOpt.NORMAL)) RaiseMessage("SM", "Stop");
        }

       

        eSMStep prebackupstepo = eSMStep.NOTSET;
        eSMStep prebackupstepn = eSMStep.NOTSET;
        public void PushStep()
        {
            prebackupstepo = OldStep;
            prebackupstepn = _step;
        }
        public void PopStep()
        {
            if (prebackupstepn != eSMStep.NOTSET)
            {
                _step = prebackupstepn;
                OldStep = prebackupstepo;
                prebackupstepn = eSMStep.NOTSET;
            }
        }
        /// <summary>
        /// 상태머신의 작동상태
        /// </summary>
        public eSMStep Step { get { return _step; } }

        /// <summary>
        /// 상태머신값을 숫자로 반환 합니다. (20이하는 시스템상태이고 그 이상은 사용자 상태)
        /// </summary>
        public byte StepValue
        {
            get
            {
                return (byte)this._step;
            }
        }

        public void setNewStep(eSMStep newstep_, Boolean force = false)
        {
            if (Step != newstep_)   //현재스텝과 새로운 스텝이 다른경우
            {
                if (NewStep != newstep_)
                {
                    if (force == false && NewStep == eSMStep.EMERGENCY && newstep_ != eSMStep.RESET)
                    {
                        //스텝을 변경해야하는데. 예약된 스텝이 비상정지라면 처리하지 않는다
                        RaiseMessage("SM-STEP", string.Format("비상정지가 예약되어 있으므로 이 스텝({0})은 취소 됩니다", newstep_));
                    }
                    else
                    {
                        //바뀌도록 예약을 한다.
                        NewStep = newstep_;
                        if (GetMsgOpt(eMsgOpt.STEPCHANGE))
                            RaiseMessage("SM-STEP", string.Format("Step Reserve {0} -> {1}", Step, NewStep));
                    }
                }
                else
                {
                    //예약은 되어있는데 아직 바뀐것은 아니다.
                    if (GetMsgOpt(eMsgOpt.STEPCHANGE))
                        RaiseMessage("SM-STEP", string.Format("Step Already Reserve {0} -> {1}", Step, NewStep));
                }
            }
        }
        public eSMStep getNewStep
        {
            get
            {
                return NewStep;
            }
        }
        private eSMStep NewStep = eSMStep.NOTSET;
        public eSMStep OldStep = eSMStep.NOTSET; //171214
        private eSMStep _step;

        /// <summary>
        /// newstep 의 값을 step 에 적용합니다.
        /// </summary>
        private void StepApply()
        {
            var ostep = _step;
            OldStep = _step; _step = NewStep;
            if (StepChanged != null) StepChanged(this, new StepChangeEventArgs(ostep, _step));
        }    //171214

        /// <summary>
        /// 메세지 출력옵션을 변경 합니다.
        /// </summary>
        /// <param name="opt"></param>
        /// <param name="value"></param>
        public void SetMsgOpt(eMsgOpt opt, Boolean value)
        {
            byte pos = (byte)opt;
            if (value)
                _messageOption = (byte)(_messageOption | (1 << pos));
            else
                _messageOption = (byte)(_messageOption & ~(1 << pos));
        }
        public void SetMegOptOn() { _messageOption = 0xFF; }
        public void SetMsgOptOff() { _messageOption = 0; }
        public Boolean GetMsgOpt(eMsgOpt opt)
        {
            byte pos = (byte)opt;
            return (_messageOption & (1 << pos)) > 0;
        }
        public TimeSpan StepRunTime
        {
            get
            {
                if (StepStartTime.Year == 1982) return new TimeSpan(0);
                else return DateTime.Now - StepStartTime;
            }
        }
    }
}
