using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Threading;
using FlyADBase;

namespace Flyad7_WPF
{
    public class AutoSync : INotifyPropertyChanged, Misc.ISaveToXml
    {
        IFlyADClient mFlyAD;

        const int OutIdxOfVSensor = 1;
        /// <summary>
        /// 在边界等待的时间,s
        /// </summary>
        const int ExtraTime = 2;
        /// <summary>
        /// 开始等待时间
        /// </summary>
        const int StartWaitTime = 60;


        /// <summary>
        /// 温修消耗时间,s
        /// </summary>
        public int ConsumeOfSampleBackw { get; set; }
        /// <summary>
        /// 样品消耗时间,s
        /// </summary>
        public int ConsumeOfSampleForw { get; set; }
        /// <summary>
        /// 扫描消耗时间,s
        /// </summary>
        public int ConsumeOfScan { get; set; }

        /// <summary>
        /// 等待grid完成耗时,s
        /// </summary>
        public int ConsumeOfGrid { get; set; }

        /// <summary>
        /// 温修主轴脉冲消耗
        /// </summary>
        public int Pos2ConsumeOfSampleBackw { get; protected set; }

        /// <summary>
        /// 样品主轴脉冲消耗
        /// </summary>
        public int Pos2ConsumeOfSampleForw { get; protected set; }


        /// <summary>
        /// 扫描单次 主轴脉冲消耗
        /// </summary>
        public int Pos2ConsumeOfScan { get; set; }



        /// <summary>
        /// 等待下次扫描 主轴脉冲消耗
        /// </summary>
        public int Pos2ConsumeOfWaitNextScan { get; set; }


        public int SampleInterval { get; set; }

        /// <summary>
        /// 扫描宽度，单位脉冲
        /// </summary>
        public int ScanWidth { get; set; }
        /// <summary>
        /// 扫描开始位置，单位脉冲
        /// </summary>
        public int ScanBegin { get; set; }

        public Misc.Range SamplePos_Backw = new Misc.Range();
        public Misc.Range SamplePos_Forw = new Misc.Range();

        public UInt32 Velocity { get; set; }
        public UInt32 VSample { get; set; }

        double VDistanceWithHeaderInUsed;

        public double DistanceWithHeader { get; set; } = 0.486;


        public int sampleCount { get; set; }

        public int last_pos2 { get; set; }

        /// <summary>
        /// pos1 脉冲比例， 如 Velocity = 8000, 所以 Speed1 = Velocity*pos1_scale
        /// </summary>
        public double Pos1Scale { get; set; } = 0.25;



        /// <summary>
        /// 主轴脉冲  mm/脉冲
        /// </summary>
        public double Mmpp2 { get; set; } = 0.326;

        /// <summary>
        /// 当前线速度
        /// </summary>
        public double CurrFilmVelocity { get; set; }
        
        /// <summary>
        /// 当前速度
        /// </summary>
        public double CurrVelocity { get; set; }

        /// <summary>
        /// 电机脉冲  mm/脉冲
        /// </summary>
        public double Mmpp1 { get; set; } = 0.16;

        /// <summary>
        /// m/min
        /// </summary>
        public double FilmVelocity { get; set; }


        public AutoSync(FlyAD7 flyad)
        {
            ScanBegin = 484;
            ScanWidth = 4000;

            SamplePos_Backw.Begin = 67 - 50;
            SamplePos_Backw.End = 67 + 50;

            SamplePos_Forw.Begin = 5200 - 50;
            SamplePos_Forw.End = 5200 + 50;

            Velocity = 2000;// 8000;
            VSample = 1000;// 3000;

            FilmVelocity = 18;//m/min
            SampleInterval = 10;

            Pos1Scale = 0.25;


            ConsumeOfSampleBackw = 6;
            ConsumeOfSampleForw = 6;
            ConsumeOfScan = 8;
            ConsumeOfGrid = 3;

            Load();

            Init(flyad);
        }

        void Init(IFlyADClient flyad)
        {
            mFlyAD = flyad;
            Misc.BindingOperations.SetBinding(mFlyAD, new string[] { "Ratio02", "Ratio01" },
                delegate()
                {
                    Pos1Scale = (double)mFlyAD.Ratio02 / mFlyAD.Ratio01;
                });

            Misc.BindingOperations.SetBinding(mFlyAD, "Speed2",
                delegate()
                {
                    CurrFilmVelocity = (mFlyAD.Speed2 * Mmpp2 * 60) / 1000.0;
                });
            Misc.BindingOperations.SetBinding(mFlyAD, "Speed",
                delegate()
                {
                    CurrVelocity = (mFlyAD.Speed * Mmpp1 * 60) / 1000.0;
                });

            TestInit();
        }

        void GM_SyncScan_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "DistanceWithHeader") //滞后,应该增大
            {
                double d = DistanceWithHeader - VDistanceWithHeaderInUsed;
                d *= 1000.0;
                d = -d;
                int offset = (int)(d / Mmpp2);
                mFlyAD.SetPos2Offset(offset);//提前
                VDistanceWithHeaderInUsed = DistanceWithHeader;
            }
        }

        void mFlyAD_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Marker")
            {
                for (int i = 0; i < syncOrderList.Count(); i++)
                {
                    if (syncOrderList[i].Marker == mFlyAD.Marker)
                    {
                        //当前正在运作此同步指令
                        //在它之前的，肯定运行完了，删除
                        syncOrderList.RemoveRange(0, i);
                        OnPoll_SyncOrder();
                        break;
                    }
                }
            }
            else if (e.PropertyName == "IsSync")
            {
                if (!mFlyAD.IsSync)
                {
                    Stop();
                }
            }
        }

        public void Start()
        {

            //清空同步指令
            syncOrderList.Clear();
            mFlyAD.SyncPos2Clear();
            mFlyAD.PropertyChanged += mFlyAD_PropertyChanged;



            //InitSyncParam();
            mFlyAD.Pos1LCShift = -ScanBegin;
            //ScanWidth = syncparam.ScanWidth;

            double wait_mm = StartWaitTime * FilmVelocity / 60 * 1000.0;

            int pos2 = (int)((-DistanceWithHeader * 1000 - wait_mm) / Mmpp2);

            int pos2at01 = (int)((-DistanceWithHeader * 1000) / Mmpp2);

            last_pos2 = 0;
            sampleCount = 0;

            //当前last_pos2 >= pos2


            LastOrder = new SyncOrder() { Order = SyncOrder.OrderType.Init };
            OnPoll_SyncOrder();


            mFlyAD.SyncBegin(pos2);
            mFlyAD.SyncPos2Clear();
            mFlyAD.SetPos2At01(pos2at01, true);

            //设置纵向边界信号为0
            ClearVSensor();

            DispatcherTimer timer = new DispatcherTimer();
            timer.Tick += (s, e) =>
            {
                ((DispatcherTimer)s).Stop();
                SetVSensor();
            };
            timer.Interval = TimeSpan.FromSeconds(0.5);
            timer.Start();
        }
        public void Stop()
        {
            mFlyAD.SyncEnd();
            //mFlyAD.SyncClear();
            mFlyAD.PropertyChanged -= mFlyAD_PropertyChanged;

        }

        public void InitSyncParam()
        {
            double speed1 = Velocity * Pos1Scale;// pos1 pps
            double speed2 = FilmVelocity * 1000 / 60 / Mmpp2; //pos2 pps

            //反向取样时间
            double consumeTOfTemp = ConsumeOfSampleForw;
            Pos2ConsumeOfSampleBackw = (int)(speed2 * consumeTOfTemp);

            //正向取样时间
            double consumeTOfSample = consumeTOfTemp;
            Pos2ConsumeOfSampleForw = (int)(speed2 * consumeTOfSample);

            //单次扫描时间
            double consumeTOfScan;
            consumeTOfScan = ConsumeOfScan;
            Pos2ConsumeOfScan = (int)(speed2 * consumeTOfScan);

            //在边界等1秒
            Pos2ConsumeOfWaitNextScan = (int)(speed2 * (ConsumeOfGrid + 1));

            //mFlyAD.SyncEnd();
            mFlyAD.SyncClear();
        }

        #region 测试模式

        /// <summary>
        /// 当前线速度
        /// </summary>
        public int TestInterval { get; set; } = 60;


        /// <summary>
        /// 当前线速度
        /// </summary>
        public int TestCnt { get; set; }

        public enum TEST_STATE 
        {
            IDLE,
            INIT,
            START,
            POLL,
            END,
            ERROR
        }

        public TEST_STATE TestState { get; set; } = TEST_STATE.IDLE;


        DispatcherTimer timer = new DispatcherTimer();
        void TestInit() 
        {
            TestState = TEST_STATE.INIT;
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += new EventHandler(timer_Tick);
            mFlyAD.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == "IsConnected") 
                {
                    if (mFlyAD.IsConnected == false) 
                    {
                        TestState = TEST_STATE.ERROR;
                        timer.Stop();
                    }
                }
            };
        }
        public void StartTest() 
        {
            if (!timer.IsEnabled)
            {
                TestState = TEST_STATE.INIT;
                timer.Start();
            }
        }
        public void StopTest() 
        {
            TestState = TEST_STATE.IDLE;
            timer.Stop();
        }
        void timer_Tick(object sender, EventArgs e)
        {
            switch (TestState) 
            {
                case TEST_STATE.INIT:
                    InitSyncParam();
                    TestState = TEST_STATE.START;
                    break;
                case TEST_STATE.START:
                    Start();
                    TestState = TEST_STATE.POLL;
                    TestCnt = 0;
                    break;
                case TEST_STATE.POLL:
                    {
                        TestCnt++;
                        if (TestCnt >= TestInterval) 
                        {
                            TestState = TEST_STATE.END;
                        }
                    }break;
                case TEST_STATE.END:
                    Stop();
                    TestState = TEST_STATE.INIT;
                    break;
            }
        }
        #endregion

        /// <summary>
        /// 令虚拟边界传感器 输出1 （OUT1，输出1 ,OUT1 接 机头 与 机尾 AD卡的同步信号）
        /// </summary>
        public void SetVSensor()
        {
            mFlyAD.SetOutput(
                (UInt16)Misc.MyBase.BIT(OutIdxOfVSensor - 1),
                (UInt16)Misc.MyBase.BIT(OutIdxOfVSensor - 1));
        }
        /// <summary>
        /// 令虚拟边界传感器 输出0 （OUT1，输出0 ,OUT1 接 机头 与 机尾 AD卡的同步信号）
        /// </summary>
        public void ClearVSensor()
        {
            mFlyAD.SetOutput(
                (UInt16)Misc.MyBase.BIT(OutIdxOfVSensor - 1),
                (UInt16)~Misc.MyBase.BIT(OutIdxOfVSensor - 1));
        }
        #region 同步指令管理
        /// <summary>
        /// 它就是flyad7 当中的同步指令列表
        /// 每收到grid后面带的 marker，就要判断是 哪句order，做哪件事情
        /// </summary>
        public List<SyncOrder> syncOrderList = new List<SyncOrder>();
        private SyncOrder lastorder;

        [PropertyChanged.DoNotCheckEquality]
        public SyncOrder LastOrder { get; set; }

        public struct SyncOrder
        {
            /// <summary>
            /// 都是从 ScanRangeLC.Begin 开始运行的
            /// -----样品取样 &amp; 归0周期:
            /// 1.归0,
            /// 2.反向取样
            /// 3.正向扫描
            /// 4.正向取样
            /// 5.反向扫描
            /// ------正常周期：
            /// 1.正向扫描
            /// 2.反向扫描
            /// </summary>
            public enum OrderType
            {
                Init,//刚开始，下一条就是First
                /// <summary>
                /// 老板说，要同步扫描前，必须有一条 SyncRunTo 或 SyncRunToLC
                /// </summary>
                First,


                /// <summary>
                /// 【归零】，SyncOrigin()
                /// </summary>
                Origin,
                /// <summary>
                /// 反向到 【反向样品】结束位，SyncRunTo(SamplePos_Backw.End)
                /// </summary>
                SampleBackw_ready,
                /// <summary>
                /// 【反向样品】取样，SyncRunTo(SamplePos_Backw.Begin)
                /// </summary>
                SampleBackw,
                /// <summary>
                /// 【反向样品】完成后，正向回到边界，SyncRunToLC(ScanRangeLC.Begin)
                /// </summary>
                SampleBackw_complete,
                
                /// <summary>
                /// 【正向扫描】, SyncRunAtLC(pos2_begin,pos2_end, ScanRangeLC.End)
                /// </summary>
                ScanForw,
                /// <summary>
                /// 【正向扫描】等待grid完成，SyncWait(ms)
                /// </summary>
                ScanForw_waitGridComplete,
                /// <summary>
                /// 正向到 【正向样品】开始位，SyncRunTo(SamplePos_Forw.Begin)
                /// </summary>
                SampleForw_ready,
                /// <summary>
                /// 【正向样品】取样，SyncRunTo(SamplePos_Forw.End)
                /// </summary>
                SampleForw,
                /// <summary>
                /// 【正向样品】完成后，反向回到边界，SyncRunToLC(ScanRangeLC.End)
                /// </summary>
                SampleForw_complete,
                /// <summary>
                /// 【反向扫描】, SyncRunAtLC(pos2_begin,pos2_end, ScanRangeLC.Begin)
                /// </summary>
                ScanBackw,
                /// <summary>
                /// 【反向扫描】等待grid完成，SyncWait(ms)
                /// </summary>
                ScanBackw_waitGridComplete,
            }
            public OrderType Order;
            /// <summary>
            /// 主轴脉冲
            /// </summary>
            public Int32 Marker;
            public override string ToString()
            {
                return Order.ToString() + "|" + Marker.ToString();
            }
        }
        bool IsSample()
        {
            if ((sampleCount >= SampleInterval) && (SampleInterval > 0))
                return true;
            return false;
        }
        /// <summary>
        /// 每次调用创造1条同步指令！！！！
        /// </summary>
        public void CreateSyncOrder()
        {
            SyncOrder order = new SyncOrder();
            switch (LastOrder.Order)
            {
                case SyncOrder.OrderType.Init:
                    {
                        order.Order = SyncOrder.OrderType.First;
                    } break;
                case SyncOrder.OrderType.ScanBackw:
                    {
                        order.Order = SyncOrder.OrderType.ScanBackw_waitGridComplete;
                    } break;
                case SyncOrder.OrderType.First:
                case SyncOrder.OrderType.ScanBackw_waitGridComplete:
                    {
                        if (sampleCount < SampleInterval)
                            sampleCount++;

                        if (IsSample())
                            order.Order = SyncOrder.OrderType.Origin;
                        else
                            order.Order = SyncOrder.OrderType.ScanForw;
                    }break;
                case SyncOrder.OrderType.ScanForw:
                    {
                        order.Order = SyncOrder.OrderType.ScanForw_waitGridComplete;
                    }break;
                case SyncOrder.OrderType.ScanForw_waitGridComplete:
                    {
                        if (IsSample())
                        {
                            sampleCount = 0;
                            order.Order = SyncOrder.OrderType.SampleForw_ready;
                        }
                        else
                            order.Order = SyncOrder.OrderType.ScanBackw;
                    }break;
                case SyncOrder.OrderType.Origin:
                    order.Order = SyncOrder.OrderType.SampleBackw_ready;
                    break;
                case SyncOrder.OrderType.SampleBackw_ready:
                    order.Order = SyncOrder.OrderType.SampleBackw;
                    break;
                case SyncOrder.OrderType.SampleBackw:
                    order.Order = SyncOrder.OrderType.SampleBackw_complete;
                    break;
                case SyncOrder.OrderType.SampleBackw_complete:
                    order.Order = SyncOrder.OrderType.ScanForw;
                    break;

                case SyncOrder.OrderType.SampleForw_ready:
                    order.Order = SyncOrder.OrderType.SampleForw;
                    break;
                case SyncOrder.OrderType.SampleForw:
                    order.Order = SyncOrder.OrderType.SampleForw_complete;
                    break;
                case SyncOrder.OrderType.SampleForw_complete:
                    order.Order = SyncOrder.OrderType.ScanBackw;
                    break;
            }
            DoOrder(ref order);
            syncOrderList.Add(order);
            LastOrder = order;
        }
        void DoOrder(ref SyncOrder order)
        {
            switch (order.Order)
            {
                case SyncOrder.OrderType.First:
                    {
                        order.Marker = last_pos2;
                        mFlyAD.SyncRunAtLC(
                            last_pos2,
                            last_pos2 + Pos2ConsumeOfScan,
                            0,
                            true,
                            order.Marker);
                        last_pos2 += Pos2ConsumeOfScan;

                    } break;
                case SyncOrder.OrderType.Origin:
                    {
                        order.Marker = last_pos2;

                        mFlyAD.SyncOrigin(order.Marker);
                    } break;
                case SyncOrder.OrderType.SampleBackw_ready:
                    {
                        order.Marker = last_pos2 + 1;

                        mFlyAD.SyncRunTo(SamplePos_Backw.End,
                            Velocity,
                            false,
                            order.Marker);

                    } break;
                case SyncOrder.OrderType.SampleBackw:
                    {
                        order.Marker = (last_pos2 + 2);

                        mFlyAD.SyncRunTo(SamplePos_Backw.Begin,
                            VSample,
                            true,
                            order.Marker);

                    } break;
                case SyncOrder.OrderType.SampleBackw_complete:
                    {
                        order.Marker = (last_pos2 + 3);

                        mFlyAD.SyncRunToLC(0,
                            Velocity,
                            false,
                            order.Marker);

                        last_pos2 += Pos2ConsumeOfSampleBackw;
                    } break;
                case SyncOrder.OrderType.ScanBackw:
                    {
                        order.Marker = last_pos2;
                        mFlyAD.SyncRunAtLC(
                            last_pos2,
                            last_pos2 + Pos2ConsumeOfScan,
                            0,
                            true,
                            order.Marker);
                        last_pos2 += Pos2ConsumeOfScan;
                    } break;
                case SyncOrder.OrderType.ScanBackw_waitGridComplete:
                    {
                        order.Marker = last_pos2;

                        mFlyAD.SyncRunAtLC(
                            last_pos2,
                            last_pos2 + Pos2ConsumeOfWaitNextScan,
                            0,
                            false,
                            order.Marker);
                        last_pos2 += Pos2ConsumeOfWaitNextScan;
                    }break;
                case SyncOrder.OrderType.SampleForw_ready:
                    {
                        order.Marker = last_pos2;

                        mFlyAD.SyncRunTo(SamplePos_Forw.Begin,
                            Velocity,
                            false,
                            order.Marker);

                    } break;
                case SyncOrder.OrderType.SampleForw:
                    {
                        order.Marker = (last_pos2 + 1);

                        mFlyAD.SyncRunTo(SamplePos_Forw.End,
                            VSample,
                            true,
                            order.Marker);

                    } break;
                case SyncOrder.OrderType.SampleForw_complete:
                    {
                        order.Marker = (last_pos2 + 2);

                        mFlyAD.SyncRunToLC(ScanWidth - 1,
                            Velocity,
                            false,
                            order.Marker);

                        last_pos2 += Pos2ConsumeOfSampleForw;
                    } break;
                case SyncOrder.OrderType.ScanForw:
                    {
                        order.Marker = last_pos2;
                        mFlyAD.SyncRunAtLC(
                            last_pos2,
                            last_pos2 + Pos2ConsumeOfScan,
                            ScanWidth - 1,
                            true,
                            order.Marker);
                        last_pos2 += Pos2ConsumeOfScan;

                    } break;
                case SyncOrder.OrderType.ScanForw_waitGridComplete:
                    {
                        order.Marker = last_pos2;

                        mFlyAD.SyncRunAtLC(
                            last_pos2,
                            last_pos2 + Pos2ConsumeOfWaitNextScan,
                            ScanWidth - 1,
                            false,
                            order.Marker);
                        last_pos2 += Pos2ConsumeOfWaitNextScan;
                    }break;
            }
        }
        /// <summary>
        /// 保证列表有10条以上,少于10条，每次添加10条
        /// </summary>
        void OnPoll_SyncOrder()
        {
            if (syncOrderList.Count() < 6)
            {
                for (int i = 0; i < 6; i++)
                    CreateSyncOrder();
            }
        }
        #endregion

        public event PropertyChangedEventHandler PropertyChanged;

        public void Save()
        {
            Misc.SaveToXmlHepler.Save("autosync.xml", this);
        }
        public void Load()
        {
            Misc.SaveToXmlHepler.Load("autosync.xml", this);
        }
        public string[] GetSavePropertyNames()
        {
            return new string[]{
                "ScanBegin",
                "ScanWidth",
                "Mmpp1",
                "Mmpp2",
                "SamplePos_Backw",
                "SamplePos_Backw",
                "SamplePos_Forw",
                "SamplePos_Forw",
                "Velocity",
                "VSample",

                "FilmVelocity",
                "SampleInterval",
                "TestInterval",

                "ConsumeOfSampleBackw",
                "ConsumeOfSampleForw",
                "ConsumeOfScan",
                "ConsumeOfGrid"
           };
        }
    }
}
