using FObjBase;
using Misc;
using NLog.Fluent;
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Channels;

namespace FlyADBase
{
    public partial class FlyAd2021B2 : IFlyADClientAdv
    {
        #region IFlyADClientAdv property
        /// <summary>
        /// grid数据平滑
        /// </summary>
        public int GridSmooth { get; set; }

        /// <summary>
        /// 机架总长
        /// </summary>
        public int PosLen { get; set; } = 8900;

        /// <summary>
        /// 使用独立的脉冲最大最小值
        /// </summary>
        public bool HasPosMaxMin { get; set; }

        /// <summary>
        /// 最小脉冲，默认是0
        /// </summary>
        public int PosMin { get; set; } = 0;

        /// <summary>
        /// 最大脉冲，默认于PosLen 一样
        /// </summary>
        public int PosMax { get; set; } = 8900;

        public int GridLen => PosLen / PosOfGrid;
        /// <summary>
        /// Speed1 = Velocity * Speed1Scale
        /// </summary>
        public double Speed1Scale => (double)Ratio02 / Ratio01;

        /// <summary>
        /// 动作完成
        /// </summary>
        public bool IsFinish { get; set; }

        /// <summary>
        /// ad滞后修正  单位ms
        /// </summary>
        public int ADLag { get; set; }

        /// <summary>
        /// ad2滞后修正  单位ms
        /// </summary>
        public int AD2Lag { get; set; }

        //一共有1.AD数据池（由timegrid提供） 1min
        //2.pos数据池（pos推送提供） 1min
        //4.当接收的grid事件数据。它有 （direction, grid_start，grid_len, systick )
        //systick 就是结束的时间点。 当AD数据池出现了这个时间点

        //pos数据池向前找。 pos 在 grid_start*posOfGrid 范围的数据。 
        //找到开始的systick 后，整合3个数据池的数据。
        //5.最后代替 grid 推送出去。
        public TimeGridAdvHelper mTimeGridAdvHelper { get; } = new TimeGridAdvHelper();

        /// <summary>
        /// 动作指令完成，准备推送 timegridadv 事件
        /// </summary>
        public bool IsTimeToPushTimeGridAdv { get; private set; } = false;

        /// <summary>
        /// 以timegrid 为单位，推送数据
        /// </summary>
        public event TimeGridAdv2EventHandler TimeGridAdv2Event;
        #endregion


        DateTime lastUpdateADTime;
        int lastGrid = Misc.MyBase.NULL_VALUE;
        DateTime lastGridTime;
        Stopwatch stopwatch_gridTime = new Stopwatch();

        void advConstructor()
        {
            mTimeGridAdvHelper.Init();

            this.PropertyChanged += FlyAD7_PropertyChanged1;

            //轮询事件触发
            PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD, OnPoll);
        }
        private void FlyAD7_PropertyChanged1(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(DriveStatus))
            {
                switch (DriveStatus)
                {
                    case DRIVE_MAN_STATUS.STOP_MANUAL:
                    case DRIVE_MAN_STATUS.STOP:
                    case DRIVE_MAN_STATUS.LIMIT:
                        IsFinish = true;
                        break;
                    case DRIVE_MAN_STATUS.RUNNING:
                        IsFinish = false;
                        break;
                }
            }
        }

        void advAfterContected()
        {
            mTimeGridAdvHelper.Clear();
            lastUpdateADTime = DateTime.MinValue;
            lastGrid = Misc.MyBase.NULL_VALUE;
            lastGridTime = DateTime.MinValue;
        }

        #region IFlyADClientAdv function
        /// <summary>
        /// Runto(0)， 不同于 Backward
        /// </summary>
        public void RuntoMin()
        {
            if (HasPosMaxMin)
                Runto(PosMin);
            else
                Runto(0);
        }

        /// <summary>
        /// Runto(PosLen)， 不同于 Forward
        /// </summary>
        public void RuntoMax()
        {
            if (HasPosMaxMin)
                Runto(PosMax);
            else
                Runto(PosLen);
        }

        /// <summary>
        /// 设置输出
        /// </summary>
        /// <param name="index"></param>
        /// <param name="is1"></param>
        public void SetOutputBit(int index, bool is1)
        {
            if (index > 15)//4)
                return;
            if (index < 0)
                return;
            if (is1)
            {
                SetOutput(
                    (UInt16)Misc.MyBase.BIT(index),
                    (UInt16)Misc.MyBase.BIT(index));
            }
            else
            {
                SetOutput(
                    (UInt16)Misc.MyBase.BIT(index),
                    (UInt16)~Misc.MyBase.BIT(index));
            }
        }

        #endregion

        
        void advPushData(DateTime dt, int ad, int ad2)
        {
            var ret = mTimeGridAdvHelper.AddData(dt, ad, ad2, Position, Position2, IStatus);
            if (ret.isChanged)
            {
                if (ret.isIstatusChanged) {
                    IStatusChangedEvent?.Invoke(this,
                        new IStatusChangedEventArgs(Now, IStatus, ret.ichanged, Position, Position2));
                }
                if (ret.isPosChanged)
                {
                    PositionChangedEvent?.Invoke(this, new PositionChangedEventArgs() { Time = Now, Position = Position });
                }
                if (ret.isPos2Changed) 
                {
                    Position2ChangedEvent?.Invoke(this, new PositionChangedEventArgs() { Time = Now, Position = Position2 });
                }
            }
        }
        
        void advPushStatus()
        {
            if (mTimeGridAdvHelper.AddDriveStatus(Now, DriveOrder, DriveStatus))
            {

                //状态变了
                if (DriveStatus != DRIVE_MAN_STATUS.RUNNING)
                {
                    //通知 mTimeGridAdvHelper 下次触发 timegridadv
                    IsTimeToPushTimeGridAdv = true;
                }

                DriveStatusChangedEvent?.Invoke(this, new DriveStatusChangedEventArgs()
                {
                    Time = Now,
                    DriveOrder = DriveOrder,
                    DriveStatus = DriveStatus
                });
            }
        }

        /// <summary>
        /// <para>ScanMotion 使用。</para> 
        /// <para>给定按扫描的时间点。当探头停下来后，获取数据。</para> 
        /// <para>数据带滞后修正,带运动方向。</para> 
        /// </summary>
        /// <param name="beginTime"></param>
        /// <returns></returns>
        public TimeGridAdv2EventArgs GetTimeGridAdv2Event(DateTime beginTime)
        {
            //获取ad列表
            var dataList = mTimeGridAdvHelper.GetData(beginTime);
            if (dataList.Count() == 0)
                return null;
            DateTime endTime = mTimeGridAdvHelper.NewestTime;
            Misc.DIRECTION direction;
            
            //判断运动方向
            if (dataList.Last().pos > dataList.First().pos)
            {
                //正向
                direction = DIRECTION.FORWARD;
            }
            else
            {
                direction = DIRECTION.BACKWARD;
            }

            //数据滞后
            if(!TimeGridAdvHelperExt.AdLagCorr(dataList, ref endTime, ADLag, AD2Lag)) 
            {
                throw new Exception("数据量太少,无法滞后修正");
            }



            TimeGridAdv2EventArgs eventArgs = new TimeGridAdv2EventArgs();
            eventArgs.Direction = direction;
            eventArgs.EndTime = endTime;
            eventArgs.DataList = dataList;
            return eventArgs;
        }

        void OnPoll()
        {
            if (!IsConnected)
                return;
            if (!IsReady)
                return;

            OnPoll_TimeGrid();
            OnPoll_TimeGridAdv();
            OnPoll_miniGrid();
        }

        //没有滞后处理，没有机架修正
        void OnPoll_TimeGrid()
        {
            if (lastUpdateADTime == DateTime.MinValue)
            {
                lastUpdateADTime = Now;
                return;
            }


            if (Now - lastUpdateADTime < TimeSpan.FromSeconds(0.1))
                return;

            //获取0.1s到现在的数据
            var dataList = mTimeGridAdvHelper.GetData(lastUpdateADTime);

            //算AD均值
            int[] ad1s = dataList.Select(d => d.ad).ToArray();
            int[] ad2s = dataList.Select(d => d.ad2).ToArray();

            int ad = (int)ad1s.Average();
            AD = ad;

            int ad2 = (int)ad2s.Average();
            AD2 = ad2;

            //推送timegrid事件
            TimeGridEvent?.Invoke(this,
                new TimeGridEventArgs()
                {
                    Time = lastUpdateADTime,
                    Ad1s = ad1s,
                    Ad2s = ad2s
                });

            lastUpdateADTime = Now;
        }

        //没有滞后处理，没有机架修正
        void OnPoll_miniGrid()
        {
            int grid = Position / PosOfGrid;

            if (!Misc.MyBase.ISVALIDATA(lastGrid))
            {
                lastGrid = grid;
                lastGridTime = Now;
                stopwatch_gridTime.Restart();
                return;
            }

            if (lastGrid == grid)
            {
                if (Now - lastGridTime > TimeSpan.FromSeconds(2))
                {
                    //太慢，超过2秒还没走完一个grid
                    //强制缩少数据
                    lastGridTime = Now;
                }
                return;
            }
            if (stopwatch_gridTime.Elapsed < TimeSpan.FromSeconds(0.1)) 
            {
                //太快，控制推送频率
                return;
            }
            stopwatch_gridTime.Restart();

            //方向
            Misc.DIRECTION direction;
            if (lastGrid < grid)
                direction = Misc.DIRECTION.FORWARD;
            else
                direction = Misc.DIRECTION.BACKWARD;

            var dataList = mTimeGridAdvHelper.GetData(lastGridTime);
            var endTime = mTimeGridAdvHelper.NewestTime;

            //数据滞后
            if (!TimeGridAdvHelperExt.AdLagCorr(dataList, ref endTime, ADLag, AD2Lag))
            {
                //throw new Exception("数据量太少,无法滞后修正");
                return;
            }

            //获取grid图
            TimeGridAdvHelperExt.ToGrid(dataList, PosOfGrid, GridLen, 
                out int grid_start, out int grid_end,
                out int[] gridsOfAd1, out int[] gridsOfAd2,
                isMiniGrid: true);

            if (gridsOfAd1 == null)
                return;

            if (grid_start >= GridLen)
                return;

            //需要推送grid
            MiniGridEvent?.Invoke(this, new MiniGridEventArgs()
            {
                direction = direction,
                grid_start = grid_start,
                posOfGrid = PosOfGrid,
                buf = gridsOfAd1,
                buf2= gridsOfAd2,
            });

            lastGrid = grid;
            lastGridTime = Now;
        }


        void OnPoll_TimeGridAdv()
        {
            if (!IsTimeToPushTimeGridAdv)
                return;

            IsTimeToPushTimeGridAdv = false;
            
            if (TimeGridAdv2Event == null)
                return;

            //触发全部高级版的 timegrid
            bool ret = mTimeGridAdvHelper.GetLastRunningTime(out DateTime beginTime, out DateTime endTime, out DRIVE_MAN_ORDER order);
            if (!ret)
                return;

            var eventArgs = GetTimeGridAdv2Event(beginTime);

            //获取ad列表
            var dataList = eventArgs.DataList;
            int dataCnt = dataList.Count();
            if (dataCnt == 0)
                return;
            
            endTime = eventArgs.EndTime;
            var direction = eventArgs.Direction;

            //生成grid图
            TimeGridAdvHelperExt.ToGrid(dataList, PosOfGrid, GridLen,
                out int grid_start, out int grid_end,
                out int[] gridsOfAd1, out int[] gridsOfAd2);

            //触发事件
            TimeGridAdv2Event?.Invoke(this, new TimeGridAdv2EventArgs
            {
                Direction = direction,
                EndTime = endTime,
                DataList= dataList
            });
        }
    }

    class CalSpeed
    {
        DateTime last_now;
        DateTime pos1_changed_time;
        DateTime pos2_changed_time;
        DateTime last_pos1_changed_time;
        DateTime last_pos2_changed_time;
        int last_pos1;
        int last_pos2;
        int pos1;
        int pos2;

        public bool Cal(DateTime now, out int speed1, out int speed2)
        {
            speed1 = 0;
            speed2 = 0;
            if (last_now == DateTime.MinValue && now != DateTime.MinValue)
            {
                last_now = now;
                last_pos1_changed_time = pos1_changed_time;
                last_pos2_changed_time = pos2_changed_time;
                last_pos1 = pos1;
                last_pos2 = pos2;
                return false;
            }

            if (now - last_now < TimeSpan.FromSeconds(0.5))
                return false;

            bool isInvalid = false;
            if (pos1_changed_time != last_pos1_changed_time && last_pos1_changed_time != DateTime.MinValue)
            {
                var spd = (int)((pos1 - last_pos1) / (pos1_changed_time - last_pos1_changed_time).TotalSeconds);
                if (Math.Abs(spd) > 100000)
                {
                    //肯定是溢出了，
                    isInvalid = true;
                }
                else
                {
                    speed1 = spd;
                }
            }
            else
            {
                //停了
                speed1 = 0;
            }

            if (pos2_changed_time != last_pos2_changed_time && last_pos2_changed_time != DateTime.MinValue)
            {
                var spd=  (int)((pos2 - last_pos2) / (pos2_changed_time - last_pos2_changed_time).TotalSeconds);
                if (Math.Abs(spd) > 100000)
                {
                    //肯定是溢出了，
                    isInvalid = true;
                }
                else {
                    speed2 = spd;
                }
            }
            else
            {
                //停了
                speed2 = 0;
            }

            last_now = now;
            last_pos1_changed_time = pos1_changed_time;
            last_pos2_changed_time = pos2_changed_time;
            last_pos1 = pos1;
            last_pos2 = pos2;

            if (isInvalid)
                return false;
            else
                return true;

        }

        public void SetPos1(DateTime changedTime, int pos)
        {
            pos1_changed_time = changedTime;
            pos1 = pos;
        }
        public void SetPos2(DateTime changedTime, int pos)
        {
            pos2_changed_time = changedTime;
            pos2 = pos;
        }
        public void Reset()
        {
            last_now = DateTime.MinValue;

            pos1_changed_time = DateTime.MinValue;
            pos1 = 0;
            last_pos1_changed_time = DateTime.MinValue;
            last_pos1 = 0;

            pos2_changed_time = DateTime.MinValue;
            pos2 = 0;
            last_pos2_changed_time = DateTime.MinValue;
            last_pos2 = 0;


        }
    }

    /// <summary>
    /// 正反转 grid数据 缓存区
    /// </summary>
    class SGrid
    {
        public const int GRID_MAX_SIZE = 1000;
        int size;
        public int[][] data = new int[2][];//data[0]=forword, data[1]=backward
        public SGrid()
        {
            //清空所有数据
            SetSize(GRID_MAX_SIZE);
        }
        public void SetSize(int size)
        {
            this.size = size;
            data[0] = new int[size];
            data[1] = new int[size];
            Clear();
        }
        public void Clear()
        {
            //清空所有数据
            for (int i = 0; i < size; i++)
            {
                data[0][i] = Misc.MyBase.NULL_VALUE;
                data[1][i] = Misc.MyBase.NULL_VALUE;
            }
        }
    }
}
