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

namespace FLY.Thick.Blowing.UI.Fix.Client
{

    public class BlowingDetectCore
    {
        /// <summary>
        /// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位°
        /// </summary>
        public double RAngle;
        /// <summary>
        /// 辊周长,单位mm
        /// </summary>
        public double RollPerimeter;
        /// <summary>
        /// 离开限位 到 撞下一个限位  的 旋转架转动时间, 需要初始值,以后测量出来的
        /// </summary>
        public TimeSpan RenZiJiaPeriod;
        /// <summary>
        /// 人字架到测厚仪膜长 单位m
        /// </summary>
        public double FilmLength;
        /// <summary>
        /// 累计旋转次数
        /// </summary>
        public int RotationCnt;
        /// <summary>
        /// 旋转架为立式,测厚仪安装在二牵引前面,膜距离 与 旋转角度 一起变化, 旋转角度越大 膜距离越大
        /// </summary>
        public bool Is3D;

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

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

        /// <summary>
        /// 当为 立式旋转架时,膜距离与旋转角度有关系
        /// 列表的长度为 200, 列表的头为信号0离开, 尾为信号1进入
        /// </summary>
        public List<double> mFilmLength3D;

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

        }

        //public void Init(IBlowingDetect blowingDetect,
        //    List<RollCell> rolls,
        //    List<LimitCell> limits,
        //    List<double> filmLength3D
        //    )
        //{
        //    RAngle = blowingDetect.RAngle;
        //    RollPerimeter = blowingDetect.RollPerimeter;
        //    RenZiJiaPeriod = blowingDetect.RenZiJiaPeriod;
        //    FilmLength = blowingDetect.FilmLength;
        //    Is3D = blowingDetect.Is3D;

        //    mFilmLength3D = filmLength3D;
        //    mRollList = rolls;
        //    mLimitList = limits;
        //    RotationCnt = limits.Count();
        //}

        public void Init(
            double RAngle,
            double RollPerimeter,
            TimeSpan RenZiJiaPeriod,
            double FilmLength,
            bool Is3D,
            List<RollCell> rolls,
            List<LimitCell> limits,
            List<double> filmLength3D
            )
        {
            this.RAngle = RAngle;
            this.RollPerimeter = RollPerimeter;
            this.RenZiJiaPeriod = RenZiJiaPeriod;
            this.FilmLength = FilmLength;
            this.Is3D = Is3D;

            mFilmLength3D = filmLength3D;
            mRollList = rolls;
            mLimitList = limits;
            RotationCnt = limits.Count();
        }

        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>
        /// 通过角度 获取 膜距离
        /// </summary>
        /// <param name="angle">-180~180</param>
        /// <returns></returns>
        double GetFilmLength3D(double angle)
        {
            if (mFilmLength3D == null || mFilmLength3D.Count() == 0)
                return FilmLength;


            angle += RAngle / 2;
            int idx = (int)(mFilmLength3D.Count() * angle / RAngle);
            if (idx < 0) idx = 0;
            else if (idx >= mFilmLength3D.Count()) idx = mFilmLength3D.Count() - 1;
            return FilmLength + mFilmLength3D[idx];
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        double GetFilmLength3D(DateTime dt)
        {
            GetAngle(dt, out double angle, out DIRECTION direction, out int rotationcnt, out bool inCV);

            return GetFilmLength3D(angle);
        }


        public class FilmInfo
        {
            /// <summary>
            /// 上层 位于膜泡的角度 0~360°
            /// </summary>
            public double angle;
            /// <summary>
            /// 膜泡旋转方向
            /// </summary>
            public Misc.DIRECTION direction;
            /// <summary>
            /// 线速度
            /// </summary>
            public double filmVelocity;
            /// <summary>
            /// 累计旋转次数
            /// </summary>
            public int rotationCnt;
            /// <summary>
            /// 在匀速阶段; 在限位信号 开始~结束 期间 inCV = false
            /// </summary>
            public bool inCV;
        }

        /// <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;
            if (Is3D)
                filmLen = GetFilmLength3D(dt);

            filminfo = null;



            DateTime ago_dt;
            double velocity;
            double angle0;
            Misc.DIRECTION direction;
            int rotationcnt;
            bool inCV;
            if (dt == DateTime.MinValue)
            {
                //TODO, BUG
            }
            int ret = GetAgo(dt, filmLen, out ago_dt, out velocity);


            if (ret == 1)
                return ret;//发生在未来不处理!!!

            GetAngle(
                ago_dt, 
                out angle0, out direction, out rotationcnt, out inCV);

            filminfo = new FilmInfo()
            {
                angle = angle0,
                filmVelocity = velocity,
                direction = direction,
                rotationCnt = rotationcnt,
                inCV = inCV
            };
            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, double filmlength, out DateTime ago_dt, out double ago_velocity)
        {

            ago_dt = DateTime.MinValue;
            ago_velocity = 0;
            int i;
            int ret = RollList_FindIndex(dt, out i);
            //发生在未来也要计算
            if (ret == 1)
            {
                return ret;
            }

            double position = RollList_GetPosition(i, dt);
            position -= filmlength * 1000;
            ret = RollList_GetDateTime(out i, position, out ago_dt);
            ago_velocity = RollList_GetVelocity(i);
            return ret;
        }

        /// <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 * 1000;

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

        }

        #endregion

        /// <summary>
        /// 计算出角度 -180~180 恒速阶段
        /// </summary>
        /// <param name="dtEnd">旋转结束时间点</param>
        /// <param name="dtStart">旋转开始时间点</param>
        /// <param name="noStart">开始时间点对应的限位 0 or 1</param>
        /// <param name="dt">用于转换为角度的时间</param>
        /// <param name="angle">输出: 角度</param>
        /// <param name="direction">输出: 方向</param>
        void LimitListCal_ConstantVelocity(DateTime dtEnd, DateTime dtStart, int noStart, DateTime dt, out double angle, out Misc.DIRECTION direction)
        {
            TimeSpan ts1 = dt - dtStart;
            TimeSpan ts2 = dtEnd - dtStart;

            angle = RAngle * ts1.Ticks / ts2.Ticks;

            if (noStart == 0)
            {
                angle = -RAngle / 2 + angle;
                direction = Misc.DIRECTION.FORWARD;
            }
            else
            {
                angle = RAngle / 2 - angle;
                direction = Misc.DIRECTION.BACKWARD;
            }
        }

        /// <summary>
        /// 时间点查找旋转角度 -180~180
        /// </summary>
        /// <param name="dt">时间</param>
        /// <param name="angle"></param>
        /// <param name="direction"></param>
        /// <param name="rotationCnt"></param>
        void LimitListSearch(
            DateTime dt, 
            out double angle, out Misc.DIRECTION direction, out int rotationCnt, out bool inCV)
        {
            int index;
            ListFindIndexResult ret = LimitList_FindIndex(dt, out index);

            DateTime dt_start;
            DateTime dt_end;
            int no;

            switch (ret)
            {
                case ListFindIndexResult.Future://发生在未来 
                    {
                        //检测是否需要修正 RenZiJiaPeriod
                        if (mLimitList.Count == 0)//不允许存在
                        {
                            throw new Exception("参数出错 mLimitList.Count == 0");
                        }
                        else
                        {
                            dt_start = mLimitList[index].dt_end;
                            no = mLimitList[index].no;
                        }
                        rotationCnt = LimitIndexToRotationCnt(index);

                        if (dt > (dt_start + RenZiJiaPeriod))
                        {
                            dt = dt_start + RenZiJiaPeriod;//只是为了看,随便了。 时间停止下来
                        }

                        dt_end = dt_start + RenZiJiaPeriod;
                        inCV = true;
                        LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
                    }
                    break;
                case ListFindIndexResult.BetweenCells:
                    {
                        dt_start = mLimitList[index].dt_end;
                        dt_end = mLimitList[index + 1].dt_begin;
                        no = mLimitList[index].no;
                        rotationCnt = LimitIndexToRotationCnt(index);
                        inCV = true;
                        LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
                    }
                    break;
                case ListFindIndexResult.Past://过去 
                    {
                        if (mLimitList[0].no == 0)
                            no = 1;
                        else
                            no = 0;
                        
                        dt_start = mLimitList[0].dt_begin - (mLimitList[1].dt_begin - mLimitList[0].dt_end);
                        dt_end = mLimitList[0].dt_begin;

                        rotationCnt = LimitIndexToRotationCnt(-1);
                        inCV = true;
                        LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
                    }
                    break;
                case ListFindIndexResult.InCell://TODO
                    {
                        no = mLimitList[index].no;
                        rotationCnt = LimitIndexToRotationCnt(index);
                        inCV = false;

                        if (no == 0)
                        {
                            angle = -RAngle / 2;
                            direction = Misc.DIRECTION.FORWARD;
                        }
                        else
                        {
                            angle = RAngle / 2;
                            direction = Misc.DIRECTION.BACKWARD;
                        }
                    }
                    break;
                default:
                    //case ListFindIndexResult.MaybeInCell:
                    {
                        no = mLimitList[index].no;
                        rotationCnt = index + 1;
                        inCV = false;

                        if (no == 0)
                        {
                            angle = -RAngle / 2;
                            direction = Misc.DIRECTION.FORWARD;
                        }
                        else
                        {
                            angle = RAngle / 2;
                            direction = Misc.DIRECTION.BACKWARD;
                        }
                    }
                    break;
            }
        }

        #region RollList 查找
        /// <summary>
        /// 获取 [idx,idx+1] 之间 的 位置
        /// </summary>
        /// <param name="idx"></param>
        /// <param name="dt"></param>
        /// <returns></returns>
        double RollList_GetPosition(int idx, DateTime dt)
        {
            DateTime dt1 = mRollList[idx + 1].dt;
            double position1 = (idx + 1) * RollPerimeter;
            DateTime dt2 = mRollList[idx].dt;
            double position2 = (idx) * RollPerimeter;

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

            return RollPerimeter / 1000 / dt1.Subtract(dt2).TotalMinutes;
        }
        /// <summary>
        /// 获取 [idx,idx+1] 之间 的 时间
        /// </summary>
        /// <param name="idx"></param>
        /// <param name="position"></param>
        /// <returns></returns>
        DateTime RollList_GetDateTime(int idx, double position)
        {
            //TODO 经常出错,position 无限小,idx=0
            DateTime dt1 = mRollList[idx + 1].dt;
            double position1 = (idx + 1) * RollPerimeter;
            DateTime dt2 = mRollList[idx].dt;
            double position2 = (idx) * RollPerimeter;

            return dt2.Add(new TimeSpan((long)((dt1 - dt2).Ticks * (position - position2) / (position1 - position2))));
        }

        /// <summary>
        /// 通过 position 查找时间
        /// 返回 -1,这个过去的时间;
        /// 返回 0,在列表中;
        /// 返回 1,发生在未来的时间;
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        int RollList_GetDateTime(out int idx, double position, out DateTime dt)
        {
            idx = 0;
            dt = DateTime.MinValue;

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

            for (int i = mRollList.Count - 1; i > 0; i--)
            {
                if (position >= ((i) * RollPerimeter))
                {
                    //找到了
                    idx = i;
                    if (i == (mRollList.Count - 1))
                        return 1;
                    else
                    {
                        dt = RollList_GetDateTime(i, position);
                        return 0;
                    }

                }
            }
            //TODO, 这里会出错,position无限小
            idx = 0;
            dt = RollList_GetDateTime(0, position);
            //发生在过去
            return -1;
        }

        /// <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].dt)
                {
                    //找到了
                    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 Misc.DIRECTION direction, out int rotationCnt, out bool inCV)
        {
            inCV = true;

            direction = Misc.DIRECTION.FORWARD;
            rotationCnt = 0;

            
            //不修改旋转速度!!!!!!!!!!!!
            LimitListSearch(
                dt, 
                out angle, out direction, out rotationCnt, out inCV);

            double a = 0;
            a += angle;
            if (a >= 360)
                a -= 360;
            else if (a < 0)
                a += 360;

            angle = a;;
            return true;
        }
    }
}