using AutoMapper;
using FLY.FeedbackRenZiJia.Common;
using FLY.FeedbackRenZiJia.IService;
using FLY.FeedbackRenZiJia.Server.Model;
using FLY.OBJComponents.Server.Model;
using FObjBase;
using Misc;
using Newtonsoft.Json;
using PropertyChanged;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;

namespace FLY.FeedbackRenZiJia.Server
{
    /// <summary>
    /// 存放一堆厚度与加热数据。
    /// 1.用于判断加热是否稳定了;
    /// 2.加热偏移;
    /// 3.加热/厚度
    /// </summary>
    public class HeatBuf : IHeatBufService
    {

        #region 参数


        [DoNotCheckEquality]
        /// <summary>
        /// 加热生效曲线,允许为空,则不生效
        /// </summary>
        public int[] HeatEffectCurve { get; set; } = new int[] { 3, 4, 5, 6, 5, 4, 3 };


        /// <summary>
        /// 厚度稳定范围 %, 偏差在范围内,都是稳定的 
        /// </summary>
        public int StableRange { get; set; } = 2;





        /// <summary>
        /// 对位模式, 加热与厚度相关性阀值, 相关性 >=0.7
        /// </summary>
        public double ThresholdR { get; set; } = 0.7;


        /// <summary>
        /// 对位模式 加热%差 的极差 >= 20% 才能触发  单位%
        /// </summary>
        public int ThresholdMaxMin { get; set; } = 40;


        /// <summary>
        /// %
        /// 最大sigma, 只有当前的sigma  在 最大sigma 与 最小 sigma 内,才能控制
        /// </summary>
        public int ThresholdSigmaMax { get; set; } = 25;

        #endregion

        #region 状态

        /// <summary>
        /// 总延时 =Delay+mRenZiJiaService.FilmLength/mRenZiJiaService.Velocity
        /// </summary>
        public TimeSpan Delay { get; set; } = TimeSpan.FromSeconds(100);



        #region 对位

        /// <summary>
        /// 对位查找范围 默认 ±120°, 最大值 180°,最小值 50°
        /// </summary>
        public int SearchAngleRange { get; set; } = 120;

        /// <summary>
        /// 计算的最佳复位区号
        /// </summary>
        public int BestOrgBoltNo { get; set; } = -1;


        /// <summary>
        /// 计算的最佳复位区号 对应的 加热/厚度 
        /// </summary>
        public double BestKp { get; set; } = -1;


        /// <summary>
        /// 计算的最佳复位区号 对应的 加热与厚度相关性
        /// </summary>
        public double MaxR { get; set; } = -1;


        /// <summary>
        /// 计算的最佳复位区号 对应的 加热%变化极差 单位%
        /// </summary>
        public int MaxMin { get; set; } = -1;


        /// <summary>
        /// MaxR 对应的 数据幅 Id0
        /// </summary>
        public long MaxR_Id0 { get; private set; } = -1;

        /// <summary>
        /// MaxR 对应的 数据幅 Id1
        /// </summary>
        public long MaxR_Id1 { get; private set; } = -1;

        /// <summary>
        /// MaxR 对应的 时间
        /// </summary>
        public DateTime MaxR_Time { get; private set; }
        #endregion

        #region 稳定性
        /// <summary>
        /// 当前检测出来的厚度数据 稳定状态
        /// </summary>
        public STABILITY Stability { get; protected set; } = STABILITY.IDLE;

        /// <summary>
        /// 当前检测出来的厚度数据 的稳定性 
        /// </summary>
        public double CurrR { get; set; } = -1;


        /// <summary>
        /// 当前检测出来的厚度数据 2Sigma
        /// </summary>
        public double Curr2Sigma { get; set; } = -1;


        /// <summary>
        /// 当前检测出来的厚度数据,时间
        /// </summary>
        public DateTime CurrTime { get; set; } = DateTime.MinValue;


        #region 每个分区状态

        [DoNotCheckEquality]
        public bool[] BoltIsStable { get; private set; }

        #endregion

        #endregion

        #endregion


        /// <summary>
        /// HeatCell 的 Kp
        /// </summary>
        public double Kp { get; set; } = 3;


        /// <summary>
        /// 厚度数量
        /// </summary>
        int BoltCnt = 88;

        /// <summary>
        /// 加热数量
        /// </summary>
        int ChannelCnt = 88;

        /// <summary>
        /// 缓存区大小
        /// </summary>
        int BufSize = 60;

        public List<FlyData_FeedbackHeat> mData;

        /// <summary>
        /// 最后一幅数据,单位 channelcnt, 可以有double.NaN
        /// </summary>
        public double[] ThickPercents=null;
        /// <summary>
        /// 最后一幅数据的复位区号
        /// </summary>
        public int OrgBoltNo = 1;

        /// <summary>
        /// 最后一幅加热偏差变化量
        /// </summary>
        public int LastHeatDiffMaxMin = 0;


        static HeatBuf()
        {

        }
        public HeatBuf()
        {
            mData = new List<FlyData_FeedbackHeat>();
            
            if (!Load())
                Save();

            checkParamErr();
        }

        void checkParamErr()
        {
            if (StableRange < 1 || StableRange > 10)
            {
                StableRange = 4;
            }

            if (ThresholdR < 0.1 || ThresholdR > 0.9)
            {
                ThresholdR = 0.6;
            }

            if (ThresholdMaxMin < 10 || ThresholdMaxMin > 90)
            {
                ThresholdMaxMin = 40;
            }
        }
        public void SetToBestThickPercents()
        {
            MoveThickPercents(BestOrgBoltNo);
            //ThickPercents = Common.MyMath.Move(ThickPercents, (BestOrgBoltNo - OrgBoltNo)* ChannelCnt/BoltCnt);
            //OrgBoltNo = BestOrgBoltNo;
            Stability = STABILITY.OK_CORREL;
            bool[] isStables = new bool[ChannelCnt];
            for (int i = 0; i < isStables.Count(); i++)
                isStables[i] = true;

            CurrR = MaxR;
            BoltIsStable = isStables;
        }
        public bool MoveThickPercents(int orgBoltNo)
        {
            if (ThickPercents != null && OrgBoltNo!=orgBoltNo)
            {
                ThickPercents = Common.MyMath.Move(ThickPercents, (orgBoltNo - OrgBoltNo) * ChannelCnt / BoltCnt);
                OrgBoltNo = orgBoltNo;
                return true;
            }
            return false;
        }

        public void Init(int boltCnt, int channelCnt)
        {
            BoltCnt = boltCnt;
            ChannelCnt = channelCnt;

            mData.Clear();
        }



        public bool IsStable()
        {
            if ((int)Stability > 0)
                return true;
            else
                return false;
        }
        public bool IsUnstable()
        {
            if ((int)Stability < 0)
                return true;
            else
                return false;
        }

        /// <summary>
        /// 1.添加数据到缓冲;
        /// 2.判断是否稳定
        /// 3.根据稳定变化,加热变化,计算偏移
        /// </summary>
        public bool Add(Lc_ThickHeat thickHeat)
        {
            //thicks 的数据量一定要是 BoltCnt
            //heats 的数据量一定要是 ChannelCnt
            //不是返回 false

            if (thickHeat.Thicks.Count() != BoltCnt)//异常
                return false;

            if (thickHeat.Heats.Count() != ChannelCnt)//异常
                return false;

            
            //转为 加热棒数量
            var thks = Common.MyMath.ZoomOut(thickHeat.Thicks, BoltCnt / ChannelCnt);
            var thkpercents = Common.MyMath.GetPercent(thks);

            //加热%偏差
            var heatPercents = Common.MyMath.OffsetAvgBe0(thickHeat.Heats.Select(da=>(double)da).ToArray());//平移到平均值为0
            var heatEffectCurve = HeatEffectCurve == null ? null : HeatEffectCurve.Select(da => (double)da);
            heatPercents = Common.MyMath.Filter(heatPercents, heatEffectCurve);

            var thkavg = thickHeat.Thicks.AverageNoNull();



            CurrTime = thickHeat.Time;
            ThickPercents = thkpercents; 
            OrgBoltNo = thickHeat.OrgBoltNo;
            Curr2Sigma = thkpercents.Sigma();



            if (!thickHeat.IsStable)
            {
                //这幅图还没生效,没有用,不放进mData
                //需要更新状态
                Stability = STABILITY.ERROR_NO_ARRIVE;
                CurrR = -1;
                BoltIsStable = new bool[ChannelCnt];
                return true;
            }

            if (Curr2Sigma > ThresholdSigmaMax)
            {
                //这幅图太差,无效,不放进mData
                //需要更新状态
                Stability = STABILITY.ERROR_SIGMA_OVERSIZE;
                CurrR = -1;
                BoltIsStable = new bool[ChannelCnt];
                return true;
            }



            FlyData_FeedbackHeat d = new FlyData_FeedbackHeat()
            {
                //thickHeat = thickHeat,
                Stability = STABILITY.IDLE,
                ThickAvg = thkavg,//肯定有数据
                ThickPercents = thkpercents,
                HeatPercents = heatPercents
            };
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(thickHeat);
            d.thickHeat = Newtonsoft.Json.JsonConvert.DeserializeObject<Lc_ThickHeat>(json);
                
            
            //更新每个分区的稳定状态
            UpdateStable3(d);
            return true;
        }


        void DataAdd(FlyData_FeedbackHeat d)
        {
            if (mData.Count == BufSize)
            {
                mData.RemoveAt(0);
            }
            mData.Add(d);

            return;
        }

        /// <summary>
        /// 更新稳定状态,需要2幅数据才能比较的;
        /// 返回 true:添加进 mData
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        void UpdateStable3(FlyData_FeedbackHeat d)
        {
            if (mData.Count() == 0)//没有以前的数据
            {
                Stability = STABILITY.IDLE;
                goto _error;
            }

            var d_last = mData.Last();
            if (d.thickHeat.RAngle != d_last.thickHeat.RAngle)
            {
                //旋转角度不同,无法比较
                Stability = STABILITY.ERROR_PARAM_CHANGED;
                goto _error;
            }

            if (d.thickHeat.FilmLength != d_last.thickHeat.FilmLength)
            {
                //膜距离不用,无法比较
                Stability = STABILITY.ERROR_PARAM_CHANGED;
                goto _error;
            }

            if (d.ThickAvg <= 0)
            {
                //厚度为负数异常
                Stability = STABILITY.ERROR_THICK_CHANGED;
                goto _error;
            }
            double thk_diff = Math.Abs((double)(d_last.ThickAvg - d.ThickAvg));
            if (thk_diff > 10)
            {
                if (thk_diff / d.ThickAvg > 0.15) {
                    //厚度变化15%, 不能比较
                    Stability = STABILITY.ERROR_THICK_CHANGED;
                    goto _error;
                }
            }
            //获取 厚度%偏差,与 加热%偏差,大小都为 ChannelCnt
            //thickpercents_diff = d.ThickPercents - d_last.ThickPercents
            //heatpercents_diff = d_last.ThickPercents - d.ThickPercents
            GetPercentDatas3(d, d_last,
                out double[] thickpercents_diff, out double[] heatpercents_diff);

            //通过简单的范围比较计算稳定性
            //当Kp, 错位,就会另 稳定性的值很低
            CalR(thickpercents_diff, heatpercents_diff, out bool[] isStables);
            double currR = 1.0 * isStables.Count(b => b) / isStables.Count();
            CurrR = currR;
            BoltIsStable = isStables;
            LastHeatDiffMaxMin = (int)Common.MyMath.CalMaxMin(heatpercents_diff);

            if (CurrR >= ThresholdR)
            {
                Stability = STABILITY.OK_CORREL;

            }
            else
            {
                Stability = STABILITY.ERROR_CORREL;
            }




            //通过平移 达到最大 加热厚度相关性
            double maxOffsetHeatThickR = -1;
            long maxOffsetHeatThickR_id = -1;
            double maxOffsetHeatThickR_maxMin = -1;
            int maxOffsetHeatThickR_orgBoltNo = -1;
            double maxOffsetHeatThickR_kp = -1;
            DateTime last_h_t = DateTime.MinValue;
            //从尾向前找
            for (int i = 0; i < mData.Count(); i++)
            {
                int index = mData.Count() - 1 - i;
                d_last = mData[index];

                //超过2个小时的数据删除
                if (d.thickHeat.Time - d_last.thickHeat.Time > TimeSpan.FromMinutes(120))
                {
                    mData.RemoveRange(0, index);
                    //因为是从尾到前找, 所以这个超时的,已经是最前一个,可以结束了
                    break;
                }

                if (last_h_t != DateTime.MinValue)
                {
                    if (d_last.thickHeat.HTime == last_h_t)
                    {
                        //认为加热一样的,厚度也是一样,不比较了
                        //加热一样,只用最新的那幅数据
                        continue;
                    }
                }
                last_h_t = d_last.thickHeat.HTime;

                GetPercentDatas3(d, d_last,
                    out thickpercents_diff, out heatpercents_diff);


                //加热极差 判断可信度用
                double maxMin = Common.MyMath.CalMaxMin(heatpercents_diff);
                if (maxMin >= ThresholdMaxMin)
                {
                    //通过平移找最大相关性
                    double range_percent =  1.0 * SearchAngleRange / 360;
                    if (range_percent < 0.1 || range_percent > 0.5)
                        range_percent = 0.3;
                    //计算平移
                    Common.MyMath.CalBestMove(heatpercents_diff, thickpercents_diff, range_percent, out int offset, out double heatThickR);


                    if (heatThickR > maxOffsetHeatThickR)
                    {
                        maxOffsetHeatThickR = heatThickR;
                        maxOffsetHeatThickR_orgBoltNo = OrgBoltNoOffset(d.thickHeat.OrgBoltNo, offset * BoltCnt / ChannelCnt);
                        maxOffsetHeatThickR_maxMin = maxMin;
                        maxOffsetHeatThickR_id = d_last.thickHeat.ID;

                        var thickpercents_diff2 = Common.MyMath.Move(thickpercents_diff, offset);

                        //当前数据的加热与厚度 比例
                        double b;
                        double a;
                        Misc.MyMath.Linest(heatpercents_diff, thickpercents_diff2, out a, out b);
                        maxOffsetHeatThickR_kp = a;
                    }
                }

            }

            if (maxOffsetHeatThickR != -1)
            {
                if (maxOffsetHeatThickR >= ThresholdR)
                {
                    BestOrgBoltNo = maxOffsetHeatThickR_orgBoltNo;
                    BestKp = Math.Round(maxOffsetHeatThickR_kp, 1);
                    MaxR = Math.Round(maxOffsetHeatThickR, 2);
                    MaxMin = (int)Math.Round(maxOffsetHeatThickR_maxMin, 0);
                    MaxR_Id0 = maxOffsetHeatThickR_id;
                    MaxR_Id1 = d.thickHeat.ID;
                    MaxR_Time = DateTime.Now;
                }
            }

            DataAdd(d);
            return;
            _error:
            CurrR = -1;
            BoltIsStable = new bool[ChannelCnt];
            //删除前面的
            mData.Clear();
            DataAdd(d);

        }

        int OrgBoltNoOffset(int orgBoltNo, int offset)
        {

            orgBoltNo += offset;
            if (orgBoltNo > BoltCnt)
                orgBoltNo -= BoltCnt;
            else if (orgBoltNo < 1)
                orgBoltNo += BoltCnt;
            return orgBoltNo;
        }
        void GetPercentDatas3(
            FlyData_FeedbackHeat d, FlyData_FeedbackHeat d_last,
            out double[] thickpercents_diff, out double[] heatpercents_diff)
        {
            //厚度%偏差
            double[] thickpercents_last;
            //当两幅图复位区号不一致时,移动以前的数据,使之一致
            if (d.thickHeat.OrgBoltNo != d_last.thickHeat.OrgBoltNo)
            {
                var thks_last = Common.MyMath.Move(d_last.thickHeat.Thicks, d.thickHeat.OrgBoltNo - d_last.thickHeat.OrgBoltNo);

                //转为 加热棒数量
                thks_last = Common.MyMath.ZoomOut(thks_last, BoltCnt / ChannelCnt);

                thickpercents_last = Common.MyMath.GetPercent(thks_last);
            }
            else {
                thickpercents_last = d_last.ThickPercents;
            }

            thickpercents_diff = Common.MyMath.GetDiff(d.ThickPercents, thickpercents_last);

            //加热%偏差
            heatpercents_diff = Common.MyMath.GetDiff(d_last.HeatPercents, d.HeatPercents);//-加热增量, 它是反转的!!!!
        }


        void CalR(double[] thickpercents_diff, double[] heatpercents_diff, out bool[] isStable)
        {
            //降低要求
            //thickpercents_diff = Common.MyMath.Filter(thickpercents_diff, 3);
            //heatpercents_diff = Common.MyMath.Filter(heatpercents_diff, 3);

            double kp = Kp;
            if (kp < 1)
                kp = 1;


            int stableRange = StableRange;

            if (stableRange < 1)
                stableRange = 1;

            isStable = new bool[heatpercents_diff.Count()];
            for (int i = 0; i < heatpercents_diff.Count(); i++)
            {
                if ((!double.IsNaN(thickpercents_diff[i])) &&
                    (Math.Abs(thickpercents_diff[i] - heatpercents_diff[i] / kp) <= stableRange))
                {
                    //稳定!!!!!
                    isStable[i] = true;
                }
            }
        }




        public void Apply()
        {
            Save();
        }

        string file_path = "heatbuf.json";
        bool Load()
        {
            if (File.Exists(file_path))
            {
                try
                {
                    string json = File.ReadAllText(file_path);
                    var jsonDb = JsonConvert.DeserializeObject<HeatBufJsonDb>(json);
                    HeatBufJsonDb.Mapper.Map(jsonDb, this);
                    return true;
                }
                catch
                {
                    //异常,没有json 解码失败

                }
                return false;
            }
            return false;
        }
        void Save()
        {
            try
            {
                var jsonDb = HeatBufJsonDb.Mapper.Map<HeatBufJsonDb>(this);
                string json = JsonConvert.SerializeObject(jsonDb, Formatting.Indented);
                File.WriteAllText(file_path, json);
            }
            catch
            {
                //异常,没有json 编码失败

            }
        }

        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
            }
        }
        #endregion

    }
    public class HeatBufJsonDb {

        public static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c => {
            c.CreateMap<HeatBuf, HeatBufJsonDb>().ReverseMap();
        }));

        #region 参数

        /// <summary>
        /// 对位查找范围 默认 ±120°, 最大值 180°,最小值 50°
        /// </summary>
        public int SearchAngleRange = 120;

        /// <summary>
        /// 加热生效曲线,允许为空,则不生效
        /// </summary>
        public int[] HeatEffectCurve;


        /// <summary>
        /// 厚度稳定范围 %, 偏差在范围内,都是稳定的 
        /// </summary>
        public int StableRange= 4;

        /// <summary>
        /// 对位模式, 加热与厚度相关性阀值, 相关性 >=0.7
        /// </summary>
        public double ThresholdR = 0.7;


        /// <summary>
        /// 对位模式 厚度%差 的极差 >= +4%-4%=8%, 单位%
        /// </summary>
        public int ThresholdMaxMin = 20;


        /// <summary>
        /// %
        /// 最大sigma, 只有当前的sigma  在 最大sigma 与 最小 sigma 内,才能控制
        /// </summary>
        public int ThresholdSigmaMax = 20;


        #endregion
    }


}