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

namespace FlyADBase
{
    public class TimeGridAdvHelper
    {


        /// <summary>
        /// AD数据池时间间隔为1.28ms, 肯定最少有一个数据
        /// </summary>
        RList<DataTimeUnit> ADPool = new RList<DataTimeUnit>(60000);
        /// <summary>
        /// pos 数据池, 肯定最少有一个数据
        /// </summary>
        RList<DataTimeUnit> PosPool = new RList<DataTimeUnit>(60000);

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


        public void Init()
        {
            Clear();
        }


        /// <summary>
        /// 添加ad数据，且判断数据量是否够触发事件
        /// </summary>
        /// <param name="ads"></param>
        /// <param name="start_dt"></param>
        public void AddAD(int[] ads, DateTime start_dt, double ts_ms)
        {
            //------------------------------------------------------------------------------------------
            //AD数据添加
            for (int i = 0; i < ads.Count(); i++)
            {
                DateTime dt = start_dt + TimeSpan.FromTicks((long)(TimeSpan.TicksPerMillisecond * i * ts_ms));
                ADPool.RAdd(new DataTimeUnit() { data = ads[i], dt = dt });
            }
        }

        public void AddPos(int pos, DateTime time)
        {
            if (PosPool.Count() > 0)
            {
                if (time < PosPool.Last().dt)
                {
                    //异常
                    //把前面的都删除
                    PosPool.Clear();
                }
                else if (time == PosPool.Last().dt)
                {
                    return;
                }
            }
            PosPool.RAdd(new DataTimeUnit() { data = pos, dt = time });
        }

        public void AddIStatus(UInt16 istatus, int pos, DateTime time)
        {
            if (IStatusPool.Count() > 0)
            {
                if (time < IStatusPool.Last().dt)
                {
                    //异常
                    //把前面的都删除
                    IStatusPool.Clear();
                }
            }

            IStatusPool.RAdd(new DataTimeUnit2() { istatus = istatus, pos = pos, dt = time });

            //也添加到 PosPool
            //AddPos(pos, time);
        }

        public void AddPos_Default(int pos, DateTime time)
        {
            if (PosPool.Count() == 0)
            {
                AddPos(pos, time);
            }
        }
        public void AddIStatus_Default(UInt16 istatus, int pos, DateTime time)
        {
            if (IStatusPool.Count() == 0)
            {
                AddIStatus(istatus, pos, time);
            }
        }
        public void Clear()
        {
            PosPool.Clear();
            ADPool.Clear();
            IStatusPool.Clear();

            istatus_start_idx = -1;
        }

        /// <summary>
        /// 给定结束时间点,开始grid, 找到pos数据池 对应的开始序号 与 结束序号
        /// </summary>
        /// <param name="start_pos">开始脉冲</param>
        /// <param name="endtime">结束时间点</param>
        /// <param name="start_idx">输出 pos数据池开始序号</param>
        /// <param name="end_idx">输出 pos数据池结束序号</param>
        /// <returns>false 发生在数据池前面， true成功获取</returns>
        bool GetIdxFromPosPool(int start_pos, DateTime endtime, out int start_idx, out int end_idx)
        {
            start_idx = -1;
            end_idx = -1;

            for (int i = 0; i < PosPool.Count(); i++)
            {
                int idx = PosPool.Count() - 1 - i;
                if (PosPool[idx].dt <= endtime)
                {
                    //从这个点开始找
                    end_idx = idx;
                    break;
                }
            }
            if (end_idx == -1)
            {
                //endtime 发生在过去很久以前
                return false;
            }

            int pos_last = int.MinValue;
            int i_last = -1;
            for (int i = end_idx; i >= 0; i--)
            {

                int pos = PosPool[i].data;

                //if (pos == start_pos)//找到了！！！！
                //{
                //    pos_last = pos;
                //    continue;
                //}

                if (pos_last == int.MinValue)
                {
                    pos_last = pos;
                    i_last = i;
                    continue;
                }

                if (pos_last == pos)
                {
                    continue;
                }

                if (Math.Abs(pos - start_pos) > Math.Abs(pos_last - start_pos))
                {
                    //越找越远，
                    //上一个就是了
                    start_idx = i_last;
                    return true;
                }
                else if (Math.Abs(pos - start_pos) == Math.Abs(pos_last - start_pos))
                {
                    //跨过了
                    start_idx = i_last;
                    return true;
                }

                pos_last = pos;
                i_last = i;
            }
            //找完
            start_idx = 0;
            return true;
        }


        /// <summary>
        /// 上一次查找istatus的序号
        /// </summary>
        int istatus_start_idx = -1;
        /// <summary>
        /// 根据时间点，获取输入口状态
        /// </summary>
        /// <param name="time"></param>
        /// <param name="start_idx">用于加快查找速度</param>
        /// <returns></returns>
        UInt16 GetIStatus(DateTime time)
        {
            int start_idx = istatus_start_idx;
            if (start_idx >= 0 && start_idx < IStatusPool.Count())
            {
                for (int i = start_idx; i >= 0; i--)
                {
                    int idx = i;
                    if (time >= IStatusPool[idx].dt)
                    {
                        if (idx == start_idx)//有可能start_idx 位置 本来就在前面，看看后面再决定 
                        {
                            for (int j = start_idx + 1; j < IStatusPool.Count(); j++)
                            {
                                idx = j;
                                if (time < IStatusPool[idx].dt)
                                {
                                    //找到了
                                    istatus_start_idx = idx - 1;
                                    return (UInt16)IStatusPool[istatus_start_idx].istatus;
                                }
                            }
                            //找到最后
                            istatus_start_idx = idx;
                            return (UInt16)IStatusPool[istatus_start_idx].istatus;
                        }
                        //找到
                        istatus_start_idx = idx;
                        return (UInt16)IStatusPool[istatus_start_idx].istatus;
                    }
                }

            }
            else
            {
                //没有以前的记录
                //从列表的后面向前查找
                for (int i = 0; i < IStatusPool.Count(); i++)
                {
                    int idx = IStatusPool.Count() - 1 - i;
                    if (time >= IStatusPool[idx].dt)
                    {
                        //找到
                        istatus_start_idx = idx;
                        return (UInt16)IStatusPool[istatus_start_idx].istatus;
                    }
                }
            }

            //在前面发生的！！！
            if (IStatusPool.Count() > 0)
            {
                istatus_start_idx = 0;
                return (UInt16)IStatusPool[istatus_start_idx].istatus;
            }
            else
            {
                //异常，一个数据都没有
                istatus_start_idx = -1;
                return (UInt16)0xff;
            }

        }

        /// <summary>
        /// 根据时间点，获取输入口状态
        /// </summary>
        /// <param name="time"></param>
        /// <param name="start_idx">用于加快查找速度</param>
        /// <returns></returns>
        UInt16 GetIStatus(DateTime time, out int istatus_idx)
        {
            UInt16 istatus = GetIStatus(time);
            istatus_idx = istatus_start_idx;
            return istatus;
        }

        /// <summary>
        /// 以 [idx，idx+1] 的比例， 给出 time 计算 pos 
        /// </summary>
        /// <param name="time"></param>
        /// <param name="idx"></param>
        /// <returns></returns>
        int GetPos(DateTime time, int idx)
        {
            if (PosPool.Count() < 0)
                return int.MinValue;

            if (PosPool.Count() == 1)
                return PosPool[0].data;

            if (idx < 0)
                idx = 0;

            if (idx >= PosPool.Count())
                idx = PosPool.Count() - 2;

            double d_pos = PosPool[idx].data - PosPool[idx + 1].data;

            TimeSpan ts = PosPool[idx].dt - PosPool[idx + 1].dt;
            double p = d_pos * (time - PosPool[idx + 1].dt).Ticks / ts.Ticks;

            return (int)(p + PosPool[idx + 1].data);
        }

        /// <summary>
        /// 获取时间对应的 位置点
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        int GetPos(DateTime dt)
        {
            int index = -1;
            for (int i = 0; i < PosPool.Count(); i++)//从后面开始查找
            {
                int idx = PosPool.Count() - 1 - i;
                if (PosPool[idx].dt <= dt)
                {
                    //找到了
                    index = idx;
                    break;
                }
            }

            if (index == PosPool.Count() - 1)
            {
                index = index - 1;
            }
            else if (index == -1)
            {
                index = 0;
            }

            return GetPos(dt, index);
        }


        public List<GridAdvUnit> GetGridAdv(DateTime start_dt, DateTime end_dt)
        {
            List<GridAdvUnit> result = new List<GridAdvUnit>();

            for (int i = 0; i < ADPool.Count(); i++)
            {
                int idx = ADPool.Count() - 1 - i;
                if (ADPool[idx].dt < start_dt)
                {
                    break;
                }
                else if (ADPool[idx].dt <= end_dt)
                {
                    GridAdvUnit u = new GridAdvUnit();
                    u.dt = ADPool[idx].dt;
                    u.ad = ADPool[idx].data;

                    u.pos = GetPos(u.dt);
                    u.istatus = GetIStatus(u.dt);
                    result.Add(u);
                }
            }
            result.Reverse();
            return result;
        }



        #region 子功能，grid 的 istatus 变化

        class gridistatuspush
        {
            public int istatus_no;
            public event GridIStatusEventHander GridIStatusEvent;
            public void Notify(object sender, GridIStatusEventArgs e)
            {
                GridIStatusEvent?.Invoke(sender, e);
            }
            public bool IsNoEvent()
            {
                if (GridIStatusEvent == null)
                    return true;
                else
                    return false;
            }
        }
        List<gridistatuspush> mGridIStatusEventList = new List<gridistatuspush>();

        /// <summary>
        /// 添加事件
        /// </summary>
        /// <param name="istatus_no"></param>
        /// <param name="func"></param>
        public void GridIStatusEventAdd(int istatus_no, GridIStatusEventHander func)
        {
            var gs = from _g in mGridIStatusEventList
                     where _g.istatus_no == istatus_no
                     select _g;
            gridistatuspush g;
            if (gs.Count() > 0)
            {
                g = gs.First();
                g.GridIStatusEvent += func;
            }
            else
            {
                g = new gridistatuspush() { istatus_no = istatus_no };
                g.GridIStatusEvent += func;
                mGridIStatusEventList.Add(g);
            }
        }
        /// <summary>
        /// 删除事件
        /// </summary>
        /// <param name="istatus_no"></param>
        /// <param name="func"></param>
        public void GridIStatusEventDel(int istatus_no, GridIStatusEventHander func)
        {
            var gs = from _g in mGridIStatusEventList
                     where _g.istatus_no == istatus_no
                     select _g;
            gridistatuspush g;
            if (gs.Count() > 0)
            {
                g = gs.First();
                g.GridIStatusEvent -= func;
                if (g.IsNoEvent())
                {
                    mGridIStatusEventList.Remove(g);
                }
            }
        }

        /// <summary>
        /// 触发GridIStatusEvent事件
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="pos_start"></param>
        /// <param name="pos_len"></param>
        /// <param name="marker"></param>
        /// <param name="dt"></param>
        public void NotifyGridIStatusEvent(DIRECTION direction, int pos_start, int pos_len, int marker, DateTime dt, object sender)
        {
            if (mGridIStatusEventList.Count() <= 0)
                return;

            int start_idx;
            int end_idx;
            int start_pos;
            int end_pos;
            if (direction == DIRECTION.FORWARD)
            {
                start_pos = pos_start;
                end_pos = pos_start + pos_len - 1;
            }
            else
            {
                start_pos = pos_start + pos_len - 1;
                end_pos = pos_start;
            }



            bool ret = GetIdxFromPosPool(
                start_pos, dt, out start_idx, out end_idx);
            if (ret == false)//异常
                return;

            DateTime start_dt = PosPool[start_idx].dt;
            DateTime end_dt = dt;

            foreach (gridistatuspush g in mGridIStatusEventList)
            {
                List<Range> data0;
                List<Range> data1;


                GetIStatusRange(
                    g.istatus_no, start_dt, end_dt,
                    direction, start_pos, end_pos,
                    out data0, out data1);
                g.Notify(sender, new GridIStatusEventArgs()
                {
                    direction = direction,
                    istatus_no = g.istatus_no,
                    marker = marker,
                    pos_start = pos_start,
                    pos_len = pos_len,
                    data_0 = data0,
                    data_1 = data1
                });
            }
        }


        void GetIStatusRange(
            int istatus_no,
            DateTime start_dt, DateTime end_dt,
            DIRECTION direction, int start_pos, int end_pos,
            out List<Range> data0, out List<Range> data1)
        {
            data0 = new List<Range>();
            data1 = new List<Range>();

            istatus_start_idx = -1;
            int istatus_idx;
            UInt16 istatus = GetIStatus(start_dt, out istatus_idx);

            if (istatus_idx == -1)//异常，列表没有数据
            {
                //全部都是1
                if (direction == DIRECTION.FORWARD)
                    data1.Add(new Range() { Begin = start_pos, End = end_pos });
                else
                    data1.Add(new Range() { Begin = end_pos, End = start_pos });

                return;
            }
            bool istatus_no_b = Misc.MyBase.CHECKBIT(istatus, istatus_no );

            Range r = new Range();

            r.Begin = start_pos;

            for (int i = istatus_idx + 1; i < IStatusPool.Count(); i++)
            {
                istatus = IStatusPool[i].istatus;
                int pos = IStatusPool[i].pos;
                bool b = Misc.MyBase.CHECKBIT(istatus, istatus_no );
                if (IStatusPool[i].dt >= end_dt)
                {
                    //结束，这是最后！！！
                    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 = end_pos;
            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();
            }
        }
        #endregion
    }
    struct DataTimeUnit
    {
        public int data;
        public DateTime dt;
        public override string ToString()
        {
            return data.ToString() + " |" + dt.Ticks.ToString();
        }
    }
    struct DataTimeUnit2
    {
        public UInt16 istatus;
        public int pos;
        public DateTime dt;
        public override string ToString()
        {
            return pos.ToString() + "(" + istatus.ToString("X2") + ") |" + dt.ToString();
        }
    }
}
