using Misc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FlyADBase
{
    /// <summary>
    /// 高级timegrid助手
    /// 使用说明：
    /// ADPool 只能储存 1min数据, 其它XXXPool 保存1.5min数据
    /// 
    /// 
    /// 获取最后一次运行动作的开始与结束时间
    /// bool ret = mTimeGridAdvHelper.GetLastRunningTime(out DateTime beginTime, out DateTime endTime, out int marker, out DRIVE_MAN_ORDER order);
    /// 
    /// 获取 指定时间范围的 timegrid 数据
    /// var adList = mTimeGridAdvHelper.GetAD(beginTime, endTime, out DateTime reponse_endTime);
    ///
    /// 获取 指定时间范围的 pos 数据
    /// var posList = mTimeGridAdvHelper.GetPos(reponse_endTime,adList.Count());
    /// 
    /// 获取 指定时间范围的 pos2 数据
    /// var pos2List = mTimeGridAdvHelper.GetPos2(reponse_endTime,adList.Count());
    /// 
    /// 获取 指定时间范围的 输入口 数据
    /// var istatusList = mTimeGridAdvHelper.GetIStatus(reponse_endTime,adList.Count());
    /// 
    /// 转为 grid 数据
    /// int posLen = 8900;
    /// var grid = ToGrid(adList, posList, posOfGrid = 10, gridLen = posLen/10);
    /// 
    /// </summary>
    public class TimeGridAdvHelper
    {
        NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

        #region 一分钟数据缓存池, 目标每个AD数据（1ms） 都有对应的其它数据
        /// <summary>
        /// AD数据池时间间隔为1ms, 肯定最少有一个数据 
        /// 每个数据包（200个数据）理论间隔是200ms， 但实际上不是。 
        /// 数据输出时，输出的每个数据必须是1ms, 需要通过数据包的systick线性填充数据
        /// </summary>
        public List<DateTimeUnit3> ADPool = new List<DateTimeUnit3>();

        /// <summary>
        /// pos 数据池, 肯定最少有一个数据
        /// </summary>
        public List<DateTimeUnit> PosPool = new List<DateTimeUnit>();

        /// <summary>
        /// 输入端口 数据池, 肯定最少有一个数据
        /// </summary>
        public List<DateTimeUnit2> IStatusPool = new List<DateTimeUnit2>();

        /// <summary>
        /// pos2 数据池, 肯定最少有一个数据
        /// </summary>
        public List<DateTimeUnit> Pos2Pool = new List<DateTimeUnit>();

        /// <summary>
        /// DriveStatus 数据池, 肯定最少有一个数据
        /// </summary>
        public List<DateTimeUnit4> DriveStatusPool = new List<DateTimeUnit4>();
        #endregion

        public DateTime NewestTime;

        /// <summary>
        /// 
        /// </summary>
        public void Init()
        {
            Clear();
        }



        /// <summary>
        /// 添加ad数据，且判断数据量是否够触发事件, 时间间隔为 1ms
        /// </summary>
        /// <param name="end_dt"></param>
        /// <param name="adArray"></param>
        public void AddAD(DateTime end_dt, int[] adArray)
        {
            if (adArray.Count() == 0)
                return;
            //------------------------------------------------------------------------------------------
            //AD数据添加
            //DateTime dt = DateTime.MinValue;
            ADPool.Add(new DateTimeUnit3() { dt = end_dt, datas = adArray});

            //long ticks = (long)((ts_ms * adArray.Count()) * TimeSpan.TicksPerMillisecond);
            NewestTime = end_dt;
            LimitPool();
        }
        void LimitPool() 
        {
            if (NewestTime == DateTime.MinValue)
                return;

            if (ADPool.Count() == 0)
                return;

            //限制的时间
            var limitTime_ad = NewestTime - TimeSpan.FromMinutes(1);
            if (limitTime_ad - ADPool.First().dt < TimeSpan.FromMinutes(0.2))
            {
                //比限定的多了0.2min 才会动作
                return;
            }



            //限制的时间
            var limitTime_other = NewestTime - TimeSpan.FromMinutes(1.5);

            LimitSimplePool(ADPool, limitTime_ad);
            LimitSimplePool(PosPool, limitTime_other);
            LimitSimplePool(Pos2Pool, limitTime_other);
            LimitSimplePool(DriveStatusPool, limitTime_other);
            LimitSimplePool(IStatusPool, limitTime_other);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">IDateTimeUnit</typeparam>
        /// <param name="pool">缓存区</param>
        /// <param name="cap_min">缓存区容量单位min</param>
        void LimitSimplePool<T>(List<T> pool, DateTime limitTime) where T : IDateTimeUnit 
        {

            //删除Pos 数据池
            int removeCnt = 0;
            for (int i = 0; i < pool.Count(); i++)
            {
                var cell = pool[i];
                if (cell.dt < limitTime)
                {
                    //需要删除
                    removeCnt = i + 1;
                }
                else
                {
                    break;
                }
            }

            if (removeCnt > 0)
            {
                //不能全部数据删除完,必须留一个

                if (removeCnt == pool.Count())
                {
                    removeCnt--;
                    if (removeCnt <= 0)
                        return;//不用删除了
                }

                pool.RemoveRange(0, removeCnt);
            }
        }

        /// <summary>
        /// 添加脉冲， 脉冲不一样，返回true
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="time"></param>
        public bool AddPos(DateTime time, int pos)
        {
            return AddSimpleData(PosPool, time, pos);
        }


        /// <summary>
        /// 添加驱动状态, 返回true，则driveStatus变化了
        /// </summary>
        /// <param name="time"></param>
        /// <param name="order"></param>
        /// <param name="driveStatus"></param>
        /// <param name="marker"></param>
        public bool AddDriveStatus(DateTime time,DRIVE_MAN_ORDER order,  DRIVE_MAN_STATUS driveStatus, int marker)
        {
            var pool = DriveStatusPool;
            if (pool.Count > 0)
            {
                if (pool.Last().dt > time)
                {
                    //异常，把之前的数据删除
                    pool.Clear();
                }
                else if (pool.Last().dt == time)
                {
                    //删除之前那个
                    pool.RemoveAt(pool.Count() - 1);
                }
            }
            
            pool.Add(new DateTimeUnit4() { dt = time, order = order, driveStatus = driveStatus, marker = marker });
            
            if (pool.Count() > 1)
            {
                return pool[pool.Count() - 2].driveStatus != driveStatus;
            }
            return false;
        }
        /// <summary>
        /// 添加 基本数据，当数据与上一次不一样，返回true
        /// </summary>
        /// <param name="pool"></param>
        /// <param name="time"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        bool AddSimpleData(List<DateTimeUnit> pool, DateTime time, int data) {

            if (pool.Count > 0)
            {
                if (pool.Last().dt > time)
                {
                    //异常，把之前的数据删除
                    //TODO, AD盒有bug， IO 推送， 的systick 比 pos 的systick 要慢， 
                    

                    //正常的操作，全删除
                    //pool.Clear();

                    //数据插入就算了
                    for (int i = 1; i < pool.Count(); i++) {
                        int index = pool.Count() - 1 - i;
                        if (time > pool[index].dt)
                        {
                            //找到了
                            pool.Insert(index, new DateTimeUnit() { dt = time, data = data });
                        }
                        else if (time == pool[index].dt) {
                            //什么都不干
                            return false;
                        }
                    }
                    //找完了，不插了
                }
                else if (pool.Last().dt == time) 
                {
                    //删除之前那个
                    //pool.RemoveAt(pool.Count() - 1);

                    return false;
                }
            }
            pool.Add(new DateTimeUnit() { dt = time, data = data });
            if (pool.Count > 1)
            {
                return pool[pool.Count() - 2].data != data;
            }
            else {
                return false;
            }
        }

        /// <summary>
        /// 添加脉冲2， 脉冲2不一样，返回true
        /// </summary>
        /// <param name="pos2"></param>
        /// <param name="time"></param>
        public bool AddPos2(DateTime time, int pos2)
        {
            return AddSimpleData(Pos2Pool, time, pos2);
        }

        /// <summary>
        /// 添加输入口状态
        /// </summary>
        /// <param name="time"></param>
        /// <param name="istatus"></param>
        public bool AddIStatus(DateTime time, UInt16 istatus)
        {
            var pool = IStatusPool;
            if (pool.Count > 0)
            {
                if (pool.Last().dt >= time)
                {
                    //异常，把之前的数据删除
                    pool.Clear();
                }
            }

            IStatusPool.Add(new DateTimeUnit2() { dt = time, istatus = istatus });

            if (IStatusPool.Count > 1)
                return IStatusPool[IStatusPool.Count - 2].istatus != istatus;
            else
                return false;
        }

        /// <summary>
        /// 添加输入口状态
        /// </summary>
        /// <param name="time"></param>
        /// <param name="istatus"></param>
        /// <param name="pos"></param>
        /// <param name="pos2"></param>
        public bool AddIStatus(DateTime time, UInt16 istatus, int pos, int pos2, out UInt16 ichanged)
        {
            ichanged = 0;

            var pool = IStatusPool;
            if (pool.Count > 0)
            {
                if (pool.Last().dt >= time)
                {
                    //异常，把之前的数据删除
                    pool.Clear();
                }
            }

            IStatusPool.Add(new DateTimeUnit2() { dt = time, istatus = istatus, isPosValid = true,  pos = pos, pos2 = pos2 });

            
            AddPos(time, pos);
            AddPos2(time, pos2);

            if (IStatusPool.Count > 1)
            {
                ichanged = (UInt16)(IStatusPool[IStatusPool.Count - 2].istatus ^ istatus);
                return IStatusPool[IStatusPool.Count - 2].istatus != istatus;
            }
            else
                return false;
        }

        /// <summary>
        /// 添加开始位置
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="time"></param>
        public void AddPos_Default(DateTime time, int pos)
        {
            if (PosPool.Count() == 0)
            {
                AddPos(time,pos);
            }
        }
        
        /// <summary>
        /// 添加开始位置
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="time"></param>
        public void AddPos2_Default(DateTime time, int pos)
        {
            if (Pos2Pool.Count() == 0)
            {
                AddPos2(time,pos);
            }
        }


        /// <summary>
        /// 添加开始驱动状态
        /// </summary>
        /// <param name="time"></param>
        /// <param name="order"></param>
        /// <param name="dirveStatus"></param>
        /// <param name="marker"></param>
        public void AddDriveStatus_Default(DateTime time, DRIVE_MAN_ORDER order, DRIVE_MAN_STATUS dirveStatus, int marker)
        {
            if (IStatusPool.Count() == 0)
            {
                AddDriveStatus(time, order, dirveStatus, marker);
            }
        }

        /// <summary>
        /// 添加开始状态
        /// </summary>
        /// <param name="istatus"></param>
        /// <param name="time"></param>
        public void AddIStatus_Default(DateTime time, UInt16 istatus)
        {
            if (IStatusPool.Count() == 0)
            {
                IStatusPool.Add(new DateTimeUnit2() { dt = time, istatus = istatus});
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public void Clear()
        {
            PosPool.Clear();
            ADPool.Clear();
            IStatusPool.Clear();
            Pos2Pool.Clear();
            DriveStatusPool.Clear();
        }


        /// <summary>
        /// 获取最后一次运行的时间段
        /// </summary>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <param name="marker"></param>
        /// <param name="order"></param>
        /// <returns></returns>
        public bool GetLastRunningTime(out DateTime begin, out DateTime end, out int marker, out DRIVE_MAN_ORDER order) {
            begin = DateTime.MinValue;
            end = DateTime.MinValue;
            marker = 0;
            order = DRIVE_MAN_ORDER.IDLE;
            //1. 找 最后一个 driveStatus!=running的
            int idxOfStop = -1;
            for (int i = DriveStatusPool.Count() - 1; i >= 0; i--)
            {

                var cell = DriveStatusPool[i];
                if (cell.driveStatus != DRIVE_MAN_STATUS.RUNNING)
                {
                    //对！！它是。但继续向前找找看
                    idxOfStop = i;
                }
                else
                {
                    if (idxOfStop != -1)
                    {
                        //之前已经找到了，
                        break;
                    }
                }
            }
            if (idxOfStop == -1)
            {
                //一直在running，还没停下来
                return false;
            }

            //2. 最向前找running的
            int idxOfRunning = -1;
            for (int i = idxOfStop - 1; i >= 0; i--)
            {
                var cell = DriveStatusPool[i];
                if (cell.driveStatus == DRIVE_MAN_STATUS.RUNNING)
                {
                    //对！！它是。但继续向前找找看
                    idxOfRunning = i;
                }
                else
                {
                    //没了
                    break;
                }
            }

            if (idxOfRunning == -1)
            {
                //一直停着，没有运行过
                return false;
            }

            begin = DriveStatusPool[idxOfRunning].dt;
            end = DriveStatusPool[idxOfStop].dt;
            order = DriveStatusPool[idxOfStop].order;
            marker = DriveStatusPool[idxOfStop].marker;
            return true;
        }


        /// <summary>
        /// 获取最后一次运行中的timegrid;
        /// 找到最后一个 driveStatus!=running的, 最向前找running的
        /// </summary>
        /// <param name="begin">开始时间</param>
        /// <param name="end">结束时间</param>
        /// <param name="reponse_endTime">返回数据的最后一个数据的时间，数据间隔为 TimeGridAdvHelperExt.ts_ad_ms</param>
        /// <returns></returns>
        public List<int> GetAD(DateTime begin, DateTime end, out DateTime reponse_endTime)
        {
            return TimeGridAdvHelperExt.GetAD(ADPool, begin, end, out reponse_endTime);
        }
        public List<int> GetAD(DateTime begin)
        {
            return TimeGridAdvHelperExt.GetAD(ADPool, begin );
        }

        /// <summary>
        /// 输出以 1ms 为间隔脉冲列表
        /// </summary>
        /// <param name="end_dt">结束时间点</param>
        /// <param name="cnt">数据长度</param>
        /// <returns></returns>
        public List<int> GetPos(DateTime end_dt, int cnt) {

            return TimeGridAdvHelperExt.GetPos(PosPool, end_dt, cnt);
        }

        public List<int> GetPos2(DateTime end_dt, int cnt)
        {
            return TimeGridAdvHelperExt.GetPos(Pos2Pool, end_dt, cnt);
        }

        public List<UInt16> GetIStatus(DateTime end_dt, int cnt)
        {
            return TimeGridAdvHelperExt.GetIStatus(IStatusPool, end_dt, cnt);
        }

        public void GetIStatusRange(
            int istatusIndex,
            DateTime beginTime, DateTime endTime,
            out List<Range> data0, out List<Range> data1)
        {
            TimeGridAdvHelperExt.GetIStatusRange(IStatusPool, PosPool, true, istatusIndex, beginTime, endTime, out data0, out data1);
        }

    }
    public interface IDateTimeUnit 
    {
        DateTime dt { get; set; }
    }
    public struct DateTimeUnit: IDateTimeUnit
    {
        public int data;
        public DateTime dt { get; set; }
        public override string ToString()
        {
            return $"{dt.Ticks} | {data}";
        }
    }

    public struct DateTimeUnit4 : IDateTimeUnit
    {
        public DateTime dt { get; set; }
        public DRIVE_MAN_ORDER order;
        public DRIVE_MAN_STATUS driveStatus;
        public int marker;

        public override string ToString()
        {
            return $"{dt.Ticks} | {order} | {driveStatus} | {marker}";
        }
    }
    public struct DateTimeUnit3 : IDateTimeUnit
    {
        /// <summary>
        /// 数据结束点
        /// </summary>
        public DateTime dt { get; set; }

        /// <summary>
        /// 数据列
        /// </summary>
        public int[] datas;
        

        public override string ToString()
        {
            return $"{dt.Ticks} | cnt={datas.Count()}";
        }
    }

    public struct DateTimeUnit2: IDateTimeUnit
    {
        public DateTime dt { get; set; }
        public UInt16 istatus;
        public bool isPosValid;
        public int pos;
        public int pos2;
        public override string ToString()
        {
            if(isPosValid)
                return $"{dt.Ticks} | ({istatus:X2}) | 1={pos} | 2={pos2}";
            else
                return $"{dt.Ticks} | ({istatus:X2})";

        }
    }

    public static class TimeGridAdvHelperExt 
    {
        /// <summary>
        /// 导出的数据间隔是1ms 一个， 导出的数据量肯定比要求给的更多
        /// </summary>
        /// <param name="adPool">AD缓存区(每个数据间隔1ms)</param>
        /// <param name="begin">开始时间点</param>
        /// <param name="end">结束时间点</param>
        /// <param name="reponse_endTime">实际导出的数据结束时间点</param>
        /// <returns></returns>
        public static List<int> GetAD(List<DateTimeUnit3> adPool, DateTime begin, DateTime end, out DateTime reponse_endTime) 
        {
            reponse_endTime = DateTime.MinValue;

            var reponse2 = GetAD2(adPool, begin, end);
            if (reponse2.Count() == 0)
                return new List<int>();

            DateTime lastTime = reponse2.Last().dt;
            reponse_endTime = lastTime;

            return GetAD(reponse2);
        }

        /// <summary>
        /// 导出的数据间隔是1ms 一个
        /// </summary>
        /// <param name="adPool">AD缓存区(每个数据间隔1ms)</param>
        /// <returns></returns>
        public static List<int> GetAD(List<DateTimeUnit3> adPool)
        {
            return GetAD(adPool, DateTime.MinValue);
        }

        /// <summary>
        /// 导出的数据间隔是1ms 一个
        /// </summary>
        /// <param name="adPool">AD缓存区</param>
        /// <param name="begin">开始时间点</param>
        /// <returns></returns>
        public static List<int> GetAD(List<DateTimeUnit3> adPool, DateTime begin)
        {
            List<int> reponse = new List<int>();

            //导出数据的时间
            DateTime idealTime = adPool.Last().dt;
            

            //每个ad包的每个数据不是准确的1ms，肯定比1.28ms大，每次需要准
            for (int i = 0; i < adPool.Count(); i++)
            {
                int idx = adPool.Count() - 1 - i;
                var adCell = adPool[idx];
                //当前缓存区包，每个数据的时间间隔
                //TimeSpan curr_ad_ts;

                //计算这个包每个数据的时间间隔
                DateTime actTime = adCell.dt;//从缓存区获取数据的时间

                for (int j = 0; j < adCell.datas.Count(); j++)
                {
                    int idx2 = adCell.datas.Count() - 1 - j;
                    int ad = adCell.datas[idx2];
                    actTime -= TimeSpan.FromMilliseconds(1);//导出数据时间累加

                    while (idealTime > actTime)
                    {
                        reponse.Add(ad);//导出数据
                        idealTime -= TimeSpan.FromMilliseconds(1);//导出数据时间累加
                    }
                }
                if (idealTime < begin)
                    goto _end;//完成
            }
            _end:
            //反转
            reponse.Reverse();
            return reponse;

        }

        /// <summary>
        /// 获取 时间在 begin~end 的 AD数据包
        /// </summary>
        /// <param name="adPool">AD缓存区</param>
        /// <param name="begin">开始时间点</param>
        /// <param name="end">结束时间点</param>
        /// <returns></returns>
        static List<DateTimeUnit3> GetAD2(List<DateTimeUnit3> adPool, DateTime begin, DateTime end)
        {
            //ad 包的总数量不可能多，从头找也没所谓
            //TODO, 当ad包，每个包只有一个数据时，包数很庞大。 需要查找加速
            List<DateTimeUnit3> reponse = new List<DateTimeUnit3>();
            for (int i = 0; i < adPool.Count(); i++)
            {
                var cell = adPool[i];
                if (cell.dt < begin)
                    continue;
                else if (cell.dt <= end)
                    reponse.Add(cell);
                else
                    break;
            }
            return reponse;
        }


        /// <summary>
        /// 获取平均速度, 单位 脉冲/min
        /// </summary>
        /// <param name="posPool">脉冲缓存区</param>
        /// <param name="begin">开始时间点</param>
        /// <param name="end">结束时间点</param>
        /// <returns></returns>
        public static double GetSpeed(List<DateTimeUnit> posPool, DateTime begin, DateTime end) {
            int searchIdx = posPool.Count() - 1;
            int end_pos = GetPos(posPool, end, ref searchIdx);
            int begin_pos = GetPos(posPool, begin, ref searchIdx);
            return (end_pos - begin_pos) / (end - begin).TotalMinutes;
        }

        /// <summary>
        /// 输出以 1ms 为间隔脉冲列表
        /// </summary>
        /// <param name="posPool">脉冲缓存区</param>
        /// <param name="end_dt">结束时间点</param>
        /// <param name="cnt">数据长度</param>
        /// <returns></returns>
        public static List<int> GetPos(List<DateTimeUnit> posPool, DateTime end_dt, int cnt)
        {
            //从后面开始找
            int searchIdx = posPool.Count() - 1;

            List<int> reponse = new List<int>();

            DateTime lastTime = end_dt;
            for (int i = 0; i < cnt; i++)
            {
                reponse.Add(GetPos(posPool, lastTime, ref searchIdx));
                lastTime -= TimeSpan.FromMilliseconds(1);
            }
            //反转
            reponse.Reverse();
            return reponse;
        }


        /// <summary>
        /// 获取时间对应的 位置点
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="posPool">脉冲缓存池</param>
        /// <param name="searchIdx">查询开始序号</param>
        /// <returns></returns>
        public static int GetPos(List<DateTimeUnit> posPool, DateTime dt, ref int searchIdx)
        {
            //找到 时间落在 [idx，idx+1] 之间的 序号
            int index = searchIndex(posPool, dt, searchIdx);
            searchIdx = index;

            if (index == posPool.Count() - 1)
            {
                //发生在未来，返回最后一个脉冲，不进行线性预测
                return posPool[index].data;
            }
            else if (index < 0) {
                //发生在过去，返回第一个脉冲，不进行线性预测
                return posPool[0].data;
            }


            return GetPosLinear(posPool, dt, index);
        }

        /// <summary>
        /// 线性插值算 pos;
        /// 以 [idx，idx+1] 的比例， 给出 time 计算 pos 
        /// </summary>
        /// <param name="posPool"></param>
        /// <param name="time"></param>
        /// <param name="idx"></param>
        /// <returns></returns>
        static int GetPosLinear(List<DateTimeUnit> posPool, DateTime time, int idx)
        {
            if (posPool.Count() <= 0)//不应该存在
            {
                throw new Exception("没数据，还没准备好，不能获取");
            }

            if (posPool.Count() == 1)
                return posPool.First().data;

            if (idx < 0)
                idx = 0;

            if (idx >= posPool.Count() - 1)
                idx = posPool.Count() - 2;
            var cell0 = posPool[idx];
            var cell1 = posPool[idx + 1];
            double d_pos = cell0.data - cell1.data;
            if (d_pos == 0)
                return cell0.data;

            TimeSpan ts = cell0.dt - cell1.dt;
            double p = d_pos * (time - cell1.dt).Ticks / ts.Ticks;

            return (int)(p + cell1.data);
        }


        enum SearchState
        {
            ContinueLastTime,
            Back,
            ForwToCheck
        }
        /// <summary>
        /// 找到 时间 dt 落在 [idx，idx+1] 之间的 idx;
        /// 返回 -1，证明dt 在列表在前面， 或列表数据量为=0,
        /// 当dt 大于 列表最后一个数， 返回最后一个数据的id
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list">列表</param>
        /// <param name="dt"></param>
        /// <param name="searchStartIdx">从列表的某个位置开始向前查找</param>
        /// <returns></returns>
        public static int searchIndex<T>(List<T> list, DateTime dt, int searchStartIdx) where T : IDateTimeUnit
        {
            if (list.Count() == 0)
                return -1;

            int max_index = list.Count() - 1;

            if (dt >= list[max_index].dt)
                return max_index;

            if (dt < list[0].dt)
                return -1;


            int index = searchStartIdx;
            SearchState state = SearchState.ContinueLastTime;

            if ((index < 0) || (index >= max_index))//重新开始
            {
                index = max_index;
                state = SearchState.Back;
            }
            else if (state == SearchState.ContinueLastTime)
            {
                if (list[index].dt <= dt)
                {
                    //可能找到了
                    //再向正方向看看
                    state = SearchState.ForwToCheck;
                }
                else
                {
                    state = SearchState.Back;
                }
            }

            if (state == SearchState.ForwToCheck)
            {
                while (true)
                {
                    DateTime time = list[index].dt;
                    if (time > dt)
                    {
                        //就是之前那个
                        index--;
                        return index;
                    }
                    else
                    {
                        index++;
                    }
                    if (index > max_index)
                    {
                        //最后一个就是了
                        throw new Exception("之前没有判断出 时间点在列表后，异常！！");
                        //return max_index;
                    }
                }
            }
            else//SearchState.Back
            {
                while (true)
                {
                    DateTime time = list[index].dt;
                    if (time <= dt)
                    {
                        //就是它
                        return index;
                    }
                    index--;
                    if (index < 0)
                    {
                        //不可能发生
                        throw new Exception("之前没有判断出 时间点在列表前，异常！！");
                    }
                }
            }
        }



        /// <summary>
        /// 输出以 1ms 为间隔 输入口状态 列表
        /// </summary>
        /// <param name="iStatusPool"></param>
        /// <param name="end_dt"></param>
        /// <param name="cnt"></param>
        /// <returns></returns>
        public static List<UInt16> GetIStatus(List<DateTimeUnit2> iStatusPool, DateTime end_dt, int cnt) 
        {
            int searchIdx = iStatusPool.Count() - 1;

            List<UInt16> reponse = new List<UInt16>();
            DateTime lastTime = end_dt;
            for (int i = 0; i < cnt; i++)
            {
                reponse.Add(GetIStatus(iStatusPool, lastTime, ref searchIdx));
                lastTime -= TimeSpan.FromMilliseconds(1);
            }
            //反转
            reponse.Reverse();
            return reponse;
        }

        /// <summary>
        /// 根据时间点，获取输入口状态；
        /// 时间 dt 落在 [searchIdx，searchIdx+1]
        /// </summary>
        /// <param name="iStatusPool"></param>
        /// <param name="dt"></param>
        /// <param name="searchIdx"></param>
        /// <returns></returns>
        public static UInt16 GetIStatus(List<DateTimeUnit2> iStatusPool, DateTime dt, ref int searchIdx)
        {
            int index = searchIndex(iStatusPool, dt, searchIdx);

            if (index < 0)
                index = 0;
            searchIdx = index;
            return (UInt16)iStatusPool[index].istatus;
        }


        /// <summary>
        /// data0 的 输入口=false 脉冲范围；data1 的 输入口=true 脉冲范围；
        /// </summary>
        /// <param name="iStatusPool">输入口池</param>
        /// <param name="posPool">脉冲池</param>
        /// <param name="isPos1">是pos1?</param>
        /// <param name="istatusIndex">输入口序号</param>
        /// <param name="beginTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="data0">false脉冲范围</param>
        /// <param name="data1">true脉冲范围</param>
        public static void GetIStatusRange( 
            List<DateTimeUnit2> iStatusPool, List<DateTimeUnit> posPool, bool isPos1,
            int istatusIndex, DateTime beginTime, DateTime endTime, out List<Range> data0, out List<Range> data1)
        {
            int istatus_search_idx = iStatusPool.Count - 1;
            int pos_search_idx = posPool.Count - 1;
            data0 = new List<Range>();
            data1 = new List<Range>();
            UInt16 istatus = GetIStatus(iStatusPool, beginTime, ref istatus_search_idx);
            int beginPos = GetPos(posPool, beginTime, ref pos_search_idx);
            int endPos = GetPos(posPool, endTime, ref pos_search_idx);

            DIRECTION direction = endPos > beginPos ? DIRECTION.FORWARD : DIRECTION.BACKWARD;

            bool istatus_no_b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);

            Range r = new Range();

            r.Begin = beginPos;

            for (int i = istatus_search_idx + 1; i < iStatusPool.Count(); i++)
            {
                istatus = iStatusPool[i].istatus;
                int pos;
                if (isPos1)
                    pos = iStatusPool[i].pos;
                else
                    pos = iStatusPool[i].pos2;

                bool b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);
                if (iStatusPool[i].dt >= endTime)
                {
                    //结束，这是最后！！！
                    break;
                }
                else
                {
                    if (b != istatus_no_b)
                    {
                        r.End = pos;
                        if (istatus_no_b)
                            data1.Add(r);
                        else
                            data0.Add(r);
                        istatus_no_b = b;
                        r = new Range();
                        r.Begin = pos;
                    }
                }
            }
            r.End = beginPos;
            if (istatus_no_b)
                data1.Add(r);
            else
                data0.Add(r);

            if (direction == DIRECTION.FORWARD)
            {
                for (int i = 0; i < data0.Count(); i++)
                {
                    data0[i].End -= 1;
                }
                for (int i = 0; i < data1.Count(); i++)
                {
                    data1[i].End -= 1;
                }
            }
            else
            {
                for (int i = 0; i < data0.Count(); i++)
                {

                    data0[i].End += 1;
                    int t = data0[i].End;
                    data0[i].End = data0[i].Begin;
                    data0[i].Begin = t;
                }
                data0.Reverse();


                for (int i = 0; i < data1.Count(); i++)
                {
                    data1[i].End += 1;
                    int t = data1[i].End;
                    data1[i].End = data1[i].Begin;
                    data1[i].Begin = t;
                }
                data1.Reverse();
            }
        }

        /// <summary>
        /// data0 的 输入口=false 序号范围；data1 的 输入口=true 序号范围；
        /// timeOffset 为时间偏移，用于多AD盒情况，iStatusPool内的全部时间都会+timeOffset
        /// </summary>
        /// <param name="iStatusPool">输入口池</param>
        /// <param name="istatusIndex">输入口序号</param>
        /// <param name="beginTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="data0">false脉冲范围</param>
        /// <param name="data1">true脉冲范围</param>
        public static void GetIStatusRange(
            List<DateTimeUnit2> iStatusPool, 
            int istatusIndex, DateTime beginTime, DateTime endTime,
            out List<Range_DateTime> data0, out List<Range_DateTime> data1)
        {

            int istatus_search_idx = iStatusPool.Count - 1;
            data0 = new List<Range_DateTime>();
            data1 = new List<Range_DateTime>();
            UInt16 istatus = GetIStatus(iStatusPool, beginTime, ref istatus_search_idx);

            bool istatus_no_b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);

            Range_DateTime r = new Range_DateTime();
            r.Begin = beginTime;
            int i = istatus_search_idx + 1;
            while (i < iStatusPool.Count()) {

                istatus = iStatusPool[i].istatus;

                bool b = Misc.MyBase.CHECKBIT(istatus, istatusIndex);
                if (iStatusPool[i].dt >= endTime)
                {
                    //结束，这是最后！！！

                    break;
                }
                else
                {
                    if (b != istatus_no_b)
                    {
                        r.End = iStatusPool[i].dt;
                        if (istatus_no_b)
                            data1.Add(r);
                        else
                            data0.Add(r);
                        istatus_no_b = b;
                        r = new Range_DateTime();
                        r.Begin = iStatusPool[i].dt;
                    }
                }
                i++;
            }
            r.End = endTime;
            if (istatus_no_b)
                data1.Add(r);
            else
                data0.Add(r);


        }



        static void toGrid_step1(int[] grids_sum, int[] grids_cnt, int gridLen, int grid_idx, int ad)
        {
            if ((grid_idx < 0) || (grid_idx >= gridLen))
            {
                return;
            }

            if (!Misc.MyBase.ISVALIDATA(ad))
                return;

            grids_cnt[grid_idx]++;
            grids_sum[grid_idx] += ad;
        }

        /// <summary>
        /// 输出 grid图。 adList[x-adLag] 对应 posList[x]
        /// </summary>
        /// <param name="adList"></param>
        /// <param name="posList"></param>
        /// <param name="posOfGrid"></param>
        /// <param name="gridLen"></param>
        /// <param name="adLag"></param>
        /// <returns></returns>
        public static int[] ToGrid(List<int> adList, List<int> posList, int posOfGrid, int gridLen, int adLag=0) 
        {
            return ToGrid(adList, posList, posOfGrid, gridLen, out int grid_start, out int grid_end, adLag);
        }


        /// <summary>
        /// 输出 grid图。 adList[x-adLag] 对应 posList[x]
        /// </summary>
        /// <param name="adList"></param>
        /// <param name="posList"></param>
        /// <param name="posOfGrid"></param>
        /// <param name="gridLen"></param>
        /// <param name="adLag"></param>
        /// <returns></returns>
        public static int[] ToGrid(List<int> adList, List<int> posList, int posOfGrid, int gridLen, out int grid_start, out int grid_end, int adLag = 0, bool isMiniGrid=false)
        {
            int[] grids_sum = new int[gridLen];
            int[] grids_cnt = new int[gridLen];

            int grid_idx_last = -1;
            grid_start = Misc.MyBase.NULL_VALUE;
            grid_end = Misc.MyBase.NULL_VALUE;
            //grid_idx 应该要连续变化，不是，那就插值
            for (int j = 0; j < adList.Count() && j < posList.Count(); j++)
            {
                int index = j - adLag;
                if (index < 0 || index >= adList.Count())
                    continue;

                int ad = adList[index];
                int pos = posList[j];
                int grid_idx = pos / posOfGrid;
                //grid不能小于0 或 大于等于 gridLen
                if (grid_idx < 0 || grid_idx >= gridLen)
                    continue;

                if (!Misc.MyBase.ISVALIDATA(grid_start))
                {
                    grid_start = grid_idx;
                    grid_end = grid_idx;
                }
                else
                {
                    if (grid_idx < grid_start)
                        grid_start = grid_idx;
                    if (grid_idx > grid_end)
                        grid_end = grid_idx;
                }
                if (grid_idx_last != -1 && (Math.Abs(grid_idx_last - grid_idx) > 1))
                {
                    //步子太大，需要插值
                    if (grid_idx_last < grid_idx)
                    {
                        for (int i = grid_idx_last; i < grid_idx; i++)
                        {
                            toGrid_step1(grids_sum, grids_cnt, gridLen, i, ad);
                        }
                    }
                    else
                    {
                        for (int i = grid_idx_last; i > grid_idx; i--)
                        {
                            toGrid_step1(grids_sum, grids_cnt, gridLen, i, ad);
                        }
                    }
                }
                toGrid_step1(grids_sum, grids_cnt, gridLen, grid_idx, ad);
                grid_idx_last = grid_idx;
            }

            for (int i = 0; i < gridLen; i++)
            {
                if (grids_cnt[i] == 0)
                    grids_sum[i] = Misc.MyBase.NULL_VALUE;
                else
                    grids_sum[i] = (int)(grids_sum[i] / grids_cnt[i]);
            }

            if (isMiniGrid)
            {
                //只导出 grid_start~grid_end 数据
                if (Misc.MyBase.ISVALIDATA(grid_start))
                {
                    return grids_sum.Skip(grid_start).Take(grid_end - grid_start + 1).ToArray();
                }
                else {
                    return null;//一个数据都没有
                }
            }
            else
            {
                return grids_sum;
            }
        }
    }
    public class Range_DateTime 
    {
        public DateTime Begin;
        public DateTime End;
    }
}
