using FLY.Thick.Blowing;
using FLY.Thick.Blowing.IService;
using Misc;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FLY.Thick.Blowing360.UI
{

    public class BlowingDetect360Core
    {
        /// <summary>
        /// 辊周长,单位mm
        /// </summary>
        public double RollPerimeter;

        /// <summary>
        /// 辊周长,单位m
        /// </summary>
        double MofR => RollPerimeter / 1000.0;

        /// <summary>
        /// 离开限位 到 撞下一个限位  的 旋转架转动时间, 需要初始值,以后测量出来的
        /// </summary>
        public TimeSpan RenZiJiaPeriod;

        /// <summary>
        /// 人字架到测厚仪膜长 单位m
        /// </summary>
        public double FilmLength;

        /// <summary>
        /// 累计旋转次数
        /// </summary>
        public int RotationCnt;

        /// <summary>
        /// 使用编码器检测旋转架旋转位置模式
        /// </summary>
        public bool IsRPosMode;

        /// <summary>
        /// 人字架 离开限位 到 撞下一个限位 的 旋转架转动总脉冲 单位 脉冲
        /// </summary>
        public int RPosOfR;

        /// <summary>
        /// 原点旋转脉冲 上一次撞限位开关0时, 旋转架脉冲
        /// </summary>
        public int OrgRPos;

        /// <summary>
        /// 刚开机,信号列表一个信号都没有,创造一个时间点,作为信号点
        /// </summary>
        public DateTime DefaultTime;

        /// <summary>
        /// 旋转脉冲列表
        /// </summary>
        public List<RPosData> mRPosList;

        /// <summary>
        /// 辊信号列表
        /// </summary>
        public List<RollCell2> mRollList;

        /// <summary>
        /// 转向信号列表
        /// </summary>
        public List<LimitCell> mLimitList;

        public Misc.DIRECTION Direction;

        /// <summary>
        /// 
        /// </summary>
        public BlowingDetect360Core()
        {

        }

        public void Init(
            double rollPerimeter,
            TimeSpan renZiJiaPeriod,
            double filmLength,
            List<RollCell> rollList,
            List<LimitCell> limitList,
            DateTime defaultTime,
            bool isRPosMode,
            int orgRPos,
            int rPosOfR,
            List<RPosData> rPosList,
            Misc.DIRECTION direction
            )
        {
            Direction = direction;
            RollPerimeter = rollPerimeter;
            RenZiJiaPeriod = renZiJiaPeriod;
            FilmLength = filmLength;

            IsRPosMode = isRPosMode;
            RPosOfR = rPosOfR;
            DefaultTime = defaultTime;
            OrgRPos = orgRPos;

            mRPosList = rPosList;

            mLimitList = limitList;

            RotationCnt = limitList.Count();

            //组装 rPosList 与 rollList
            mRollList = rollList.Select(r => new RollCell2() { Time = r.Time, SysTime = r.SysTime }).ToList();
        }

        int RotationCntToLimitIndex(int rotationcnt)
        {
            if (RotationCnt == 0)
                return -1;
            else
                return rotationcnt - 1 - (RotationCnt - mLimitList.Count);
        }
        /// <summary>
        /// 正反转信号列表中的序号,转旋转次数
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        int LimitIndexToRotationCnt(int index)
        {
            if (RotationCnt == 0)
                return 0;
            else
                return RotationCnt - mLimitList.Count + index + 1;
        }




        /// <summary>
        /// 获取当前在膜上的测量点,对应于膜泡的角度信息, 当时间点,比LimitList.Last.dt+RenZiJiaPeriod还要后,更新RenZiJiaPeriod
        /// 返回 -1, 当前的时间点在列表以前;
        /// 返回 0, 当前的时间点在列表中;
        /// 返回 1, 当前的时间点在列表的未来;
        /// </summary>
        /// <param name="filmWidth">被压扁后的膜宽度</param>
        /// <param name="filmPosH">探头所在膜的横向位置</param>
        /// <param name="dt">测量的时间点</param>
        /// <returns></returns>
        public int GetFilmInfo(out FilmInfo filminfo, DateTime dt)
        {
            double filmLen = FilmLength;


            filminfo = null;



            Misc.DIRECTION direction = Direction;

            if (dt == DateTime.MinValue)
            {
                //TODO, BUG
                return -1;//不在膜的范围内
            }

            int ret = GetAgo(dt, out DateTime ago_dt, out int ago_index);
            if (ret == 1)
                return ret;//发生在未来不处理!!!

            GetAngle(
                ago_dt, 
                out double angle0, out int rotationcnt);

            double velocity = RollList_GetVelocity(ago_index);

            filminfo = new FilmInfo()
            {
                angle1 = angle0,
                angle2 = angle0,
                filmVelocity = velocity,
                direction = direction,
                rotationCnt = rotationcnt,
                inCV = true
            };
            return ret;
        }

        #region 人字架时间点<-->24m后膜时间点
        /// <summary>
        /// 输入当前时间点,找到 刚出人字架时的时间点, 及那时候的线速度;
        /// 返回 -1, 当前的时间点在列表以前;
        /// 返回 0, 当前的时间点在列表中;
        /// 返回 1, 当前的时间点在列表的未来;
        /// </summary>
        /// <param name="dt">当前时间</param>
        /// <param name="ago_dt">24米真实长度</param>
        /// <returns></returns>
        int GetAgo(DateTime dt, out DateTime ago_dt, out int ago_index)
        {
            return GetAgoFix(dt, out ago_dt, out ago_index);
        }
        /// <summary>
        /// 膜距离固定 获取膜距离前的时间点
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="ago_dt"></param>
        /// <param name="ago_index"></param>
        /// <returns></returns>
        int GetAgoFix(DateTime dt, out DateTime ago_dt, out int ago_index)
        {
            ago_dt = DateTime.MinValue;
            ago_index = -1;
            int index;
            int ret = RollList_FindIndex(dt, out index);
            if (ret == 1)//发生在未来,什么都不干
                return ret;

            //纵向位置
            double filmPosition = RollList_GetPosition(index, dt);


            double filmPos0 = filmPosition - FilmLength;
            ret = RollList_GetDateTime(filmPos0, out index, out ago_dt);
            ago_index = index;
            return ret;


        }
        /// <summary>
        /// 输入过去时间,让它再加上膜长时间,得到未来时间;
        /// 返回 -1, 未来的时间在列表以前;
        /// 返回 0, 未来的时间在列表中;
        /// 返回 1, 未来的时间在列表的未来;
        /// </summary>
        /// <param name="dt">过去时间</param>
        /// <param name="dt_future">未来时间</param>
        /// <returns></returns>
        int GetFuture(DateTime dt, out DateTime dt_future)
        {
            return GetFuture(dt, FilmLength, out dt_future);
        }
        /// <summary>
        /// 输入过去时间,让它再加上膜长时间,得到未来时间;
        /// 返回 -1, 未来的时间在列表以前;
        /// 返回 0, 未来的时间在列表中;
        /// 返回 1, 未来的时间在列表的未来;
        /// </summary>
        /// <param name="dt">过去时间</param>
        /// <param name="filmlength">膜长</param>
        /// <param name="dt_future">未来时间</param>
        /// <returns></returns>
        int GetFuture(DateTime dt, double filmlength, out DateTime dt_future)
        {
            dt_future = DateTime.MinValue;
            double position;
            int i;
            int ret = RollList_FindIndex(dt, out i);
            if (ret == -1)
                position = RollList_GetPosition(0, dt);
            else if (ret == 0)
                position = RollList_GetPosition(i, dt);
            else
                return ret;

            position += filmlength;

            return RollList_GetDateTime(position, out i, out dt_future);

        }
        #endregion

        /// <summary>
        /// 计算出角度 0~360° 恒速阶段
        /// </summary>
        /// <param name="dtEnd">旋转结束时间点</param>
        /// <param name="dtStart">旋转开始时间点</param>
        /// <param name="dt">用于转换为角度的时间</param>
        /// <param name="angle">输出: 角度</param>
        void LimitListCal_ConstantVelocity(DateTime dtEnd, DateTime dtStart, DateTime dt, out double angle)
        {
            TimeSpan ts1 = dt - dtStart;
            TimeSpan ts2 = dtEnd - dtStart;

            angle = 360 * ts1.Ticks / ts2.Ticks;
        }

        /// <summary>
        /// 时间点查找旋转角度 0~360°
        /// </summary>
        /// <param name="dt">时间</param>
        /// <param name="modifyparam">当dt 发生在限位列表的未来,是否修改参数,如RenZiJiaPeriod</param>
        /// <param name="angle">角度</param>
        /// <param name="direction">旋转的方向</param>
        /// <param name="rotationCnt">旋转的次数</param>
        /// <param name="inCV">在恒速区</param>
        void GetAngle(DateTime dt, bool modifyparam, out double angle, out int rotationCnt)
        {
            int index;
            ListFindIndexResult ret = LimitList_FindIndex(dt, out index);

            DateTime dt_start;
            DateTime dt_end;

            switch (ret)
            {
                case ListFindIndexResult.Future://发生在未来 
                    {
                        if (dt < DefaultTime)
                            DefaultTime = dt - TimeSpan.FromSeconds(10);

                        //检测是否需要修正 RenZiJiaPeriod
                        if (mLimitList.Count == 0)
                        {
                            dt_start = DefaultTime;
                        }
                        else
                        {
                            dt_start = mLimitList[index].dt_begin;
                        }
                        rotationCnt = LimitIndexToRotationCnt(index);

                        if (dt > (dt_start + RenZiJiaPeriod))
                        {
                            if (modifyparam)
                            {
                                //修正!!!!
                                RenZiJiaPeriod = dt - dt_start + TimeSpan.FromSeconds(10);
                            }
                            else
                            {
                                dt = dt_start + RenZiJiaPeriod;//只是为了看,随便了。 时间停止下来
                            }
                        }

                        dt_end = dt_start + RenZiJiaPeriod;
                        LimitListCal_ConstantVelocity(dt_end, dt_start, dt, out angle);
                    }
                    break;
                case ListFindIndexResult.BetweenCells:
                    {
                        dt_start = mLimitList[index].dt_begin;
                        dt_end = mLimitList[index + 1].dt_begin;
                        rotationCnt = LimitIndexToRotationCnt(index);
                        LimitListCal_ConstantVelocity(dt_end, dt_start, dt, out angle);
                    }
                    break;
                //case ListFindIndexResult.Past://过去 
                default:
                    {

                        dt_start = DefaultTime;
                        dt_end = mLimitList[0].dt_begin;
                        rotationCnt = LimitIndexToRotationCnt(-1);
                        LimitListCal_ConstantVelocity(dt_end, dt_start, dt, out angle);
                    }
                    break;
            }

            if (Direction == DIRECTION.BACKWARD)
                angle = 360 - angle;
        }

        public double GetAngleByGlobalRPos(int globalRPos)
        {
            //OrgRPos 为 -RAngle/2 

            int rPos = globalRPos - OrgRPos;

            //360°对应的旋转脉冲
            int rPosOf360 = RPosOfR;

            while (rPos < 0)
                rPos += rPosOf360;
            while (rPos > rPosOf360)
                rPos -= rPosOf360;

            double angle = 360 * rPos / RPosOfR;



            //角度范围是 0°~ 360°
            return angle;
        }


        #region RollList 查找
        /// <summary>
        /// 获取 [idx,idx+1] 之间 的 位置, 单位m
        /// </summary>
        /// <param name="idx"></param>
        /// <param name="dt"></param>
        /// <returns></returns>
        double RollList_GetPosition(int idx, DateTime dt)
        {
            DateTime dt1 = mRollList[idx + 1].Time;
            double filmPos1 = (idx + 1) * MofR;
            DateTime dt0 = mRollList[idx].Time;
            double filmPos0 = (idx) * MofR;

            return (filmPos1 - filmPos0) * (dt - dt0).Ticks / (dt1 - dt0).Ticks + filmPos0;
        }
        /// <summary>
        /// 获取 [idx,idx+1] 之间 的 速度 m/min
        /// </summary>
        /// <param name="idx"></param>
        /// <returns></returns>
        double RollList_GetVelocity(int idx)
        {
            DateTime dt1 = mRollList[idx + 1].SysTime;
            DateTime dt2 = mRollList[idx].SysTime;

            return MofR / dt1.Subtract(dt2).TotalMinutes;
        }
        /// <summary>
        /// 获取 [idx,idx+1] 之间 的 时间
        /// </summary>
        /// <param name="idx"></param>
        /// <param name="filmPosition">纵向位置m</param>
        /// <returns></returns>
        DateTime RollList_GetDateTime(int idx, double filmPosition)
        {
            //TODO 经常出错,position 无限小,idx=0
            DateTime dt1 = mRollList[idx + 1].Time;
            double filmPos1 = (idx + 1) * MofR;
            DateTime dt2 = mRollList[idx].Time;
            double filmPos2 = (idx) * MofR;

            return dt2.Add(new TimeSpan((long)((dt1 - dt2).Ticks * (filmPosition - filmPos2) / (filmPos1 - filmPos2))));
        }
        /// <summary>
        /// 通过 filmPosition 查找时间
        /// 返回 -1,这个过去的时间;
        /// 返回 0,在列表中;
        /// 返回 1,发生在未来的时间;
        /// </summary>
        /// <param name="filmPosition">单位m</param>
        /// <returns></returns>
        int RollList_GetDateTime(double filmPosition, out int idx, out DateTime dt)
        {
            idx = 0;
            dt = DateTime.MinValue;
            if (filmPosition < 0)
                return -1;

            if (mRollList.Count == 0)
                return 1;

            idx = (int)(filmPosition / MofR);
            if (idx >= mRollList.Count - 1)
            {
                return 1;
            }
            dt = RollList_GetDateTime(idx, filmPosition);
            return 0;
        }

        /// <summary>
        /// dt 这个时间,在是 index 与 index+1 中间发生的
        /// 返回 -1,这个过去的时间;
        /// 返回 0,在列表中;
        /// 返回 1,发生在未来的时间;
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        int RollList_FindIndex(DateTime dt, out int index)
        {
            index = 0;
            if (mRollList.Count <= 1)//没数据,所以是发生在未来的
                return 1;

            for (int i = mRollList.Count - 1; i >= 0; i--)
            {
                if (dt >= mRollList[i].Time)
                {
                    //找到了
                    if (i == (mRollList.Count - 1))//这个是未来的时间点,还没发生!!! 
                    {
                        index = i;
                        return 1;
                    }
                    else
                    {
                        index = i;
                        return 0;
                    }
                }
            }

            //这个在过去的时间
            return -1;
        }

        #endregion


        enum ListFindIndexResult
        {
            /// <summary>
            /// 发生在未来, index.dt_end ~ 未来, 可能 index都还没有发生
            /// </summary>
            Future,
            /// <summary>
            /// 发生在index.dt_begin ~ index.dt_end
            /// </summary>
            InCell,
            /// <summary>
            /// 应该发生在index.dt_begin ~ index.dt_end, 但 index.dt_end还没有数据
            /// </summary>
            MaybeInCell,
            /// <summary>
            /// 发生在 index.dt_end ~ (index+1).dt_begin
            /// </summary>
            BetweenCells,
            /// <summary>
            /// 发生在 过去
            /// </summary>
            Past
        }
        /// <summary>
        /// 找到 列表中 (开始时间,结束时间) 小于 dt 的信号点
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        ListFindIndexResult LimitList_FindIndex(DateTime dt, out int index)
        {
            index = 0;
            
            if (mLimitList.Count <= 0)//没数据,所以是发生在未来的
                throw new Exception("参数出错 mLimitList.Count == 0");


            for (int i = mLimitList.Count - 1; i >= 0; i--)
            {
                LimitCell lc = mLimitList[i];
                if (dt >= lc.dt_begin)
                {
                    //找到了
                    index = i;
                    if (lc.dt_end == DateTime.MinValue)
                    {
                        return ListFindIndexResult.MaybeInCell;
                    }
                    else if (dt <= mLimitList[i].dt_end)
                    {
                        return ListFindIndexResult.InCell;
                    }
                    else if (i == (mLimitList.Count - 1))//这个是未来的时间点,还没发生!!! 
                    {
                        return ListFindIndexResult.Future;
                    }
                    else
                    {
                        return ListFindIndexResult.BetweenCells;
                    }
                }
            }

            //这个在过去的时间
            return ListFindIndexResult.Past;
        }



        /// <summary>
        /// 获取膜角度
        /// </summary>
        /// <param name="dt">时间点</param>
        /// <param name="angle">上面的膜对应的膜角度</param>
        /// <param name="direction">人字架正转?反转? FIX, 停止!</param>
        /// <param name="rotationCnt">扫描次数</param>
        /// <param name="inCV">在恒速阶段</param>
        bool GetAngle(
            DateTime dt, 
            out double angle,
            out int rotationCnt)
        {
            //不修改旋转速度!!!!!!!!!!!!
            GetAngle( dt, false, out angle, out rotationCnt);

            double a = 0;
            a = GetValidAngle(a + angle);

            angle = a;;
            return true;
        }

        /// <summary>
        /// 限制角度在 [0~360)
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        double GetValidAngle(double a)
        {
            if (a >= 360)
                a -= 360;
            else if (a < 0)
                a += 360;
            return a;
        }
    }
}