using AutoMapper;
using FLY.FilmCasting.AutoDie.Common;
using FLY.FilmCasting.AutoDie.IService;
using FLY.FilmCasting.AutoDie.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.FilmCasting.AutoDie.Server
{
    /// <summary>
    /// 存放一堆厚度与加热数据。
    /// 1.用于判断加热是否稳定了；
    /// 2.加热偏移；
    /// 3.加热/厚度
    /// </summary>
    public class HeatBuf : IHeatBufService
    {

        #region 参数

        /// <summary>
        /// 分区表
        /// </summary>
        public BoltMapCell[] Boltmap { get; set; }

        [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>
        /// 当前检测出来的厚度数据 稳定状态
        /// </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 ChannelCnt = 88;

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

        public List<FlyData_FeedbackHeat> mData;

        /// <summary>
        /// 最后一幅数据,单位 channelcnt, 可以有double.NaN;
        /// 经过了 Map处理
        /// </summary>
        public double[] ThickPercents=null;

        /// <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 Init(int channelCnt)
        {
            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>
        /// 以指定分区表 导出厚度%
        /// </summary>
        /// <param name="boltmap">分区表</param>
        /// <returns></returns>
        public double[] GetThickPercents(Lc_ThickHeat thickHeat)
        {
            double[] thks = thickHeat.Thicks;
            //把数据量放大10倍计算， 移动，放大！！！
            double[] thks_x10 = FLY.FilmCasting.AutoDie.Common.MyMath.ZoomIn(thks, 10);

            //分区表处理
            if (thickHeat.Boltmap != null && thickHeat.Boltmap.Count() != 0)
            {
                //10倍后的分区表
                Dictionary<int, int> map_x10 = new Dictionary<int, int>();

                IEnumerable<BoltMapCell> maps = thickHeat.Boltmap;
                for (int i = 0; i < maps.Count(); i++)
                {
                    int oldIdx_x10 = (maps.ElementAt(i).OldNo - 1) * 10 + 5;
                    int newIdx_x10 = (maps.ElementAt(i).NewNo - 1) * 10 + 5;
                    map_x10.Add(oldIdx_x10, newIdx_x10);
                }
                thks_x10 = FLY.FilmCasting.AutoDie.Common.MyMath.MapX(thks_x10, map_x10);
            }

            //缩小数据量
            thks = FLY.FilmCasting.AutoDie.Common.MyMath.ZoomOut(thks_x10, 10);
            double[] thickpercents = new double[thks.Count()];
            double avg = thks.AverageNoNull();
            if (!double.IsNaN(avg))
            {
                for (int i = 0; i < thks.Count(); i++)
                {
                    thickpercents[i] = (thks[i] - avg) / avg * 100;
                }
            }
            return thickpercents;
        }

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

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

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

            
            //map 处理
            var thkpercents = GetThickPercents(thickHeat);

            //加热%偏差
            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; 
            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();
            double filmWidth_diff = Math.Abs((double)(d.thickHeat.FilmWidth - d_last.thickHeat.FilmWidth));
            if (filmWidth_diff>100)
            {
                //膜宽变化大于100mm，无法比较
                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) //厚度变化大于10um，无法比较
            {
                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, 错位，就会另 稳定性的值很低
            bool[] isStables = CalStable(thickpercents_diff, heatpercents_diff);
            double currR = 1.0 * isStables.Count(b => b) / isStables.Count();
            double currR2 = Misc.MyMath.Correl(thickpercents_diff, heatpercents_diff);
            if (double.IsNaN(currR2))
                currR2 = -1;

            CurrR = Math.Max( currR, currR2 );
            BoltIsStable = isStables;
            LastHeatDiffMaxMin = (int)Common.MyMath.CalMaxMin(heatpercents_diff);

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

            }
            else
            {
                Stability = STABILITY.ERROR_CORREL;
            }

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

        }

        void GetPercentDatas3(
            FlyData_FeedbackHeat d, FlyData_FeedbackHeat d_last,
            out double[] thickpercents_diff, out double[] heatpercents_diff)
        {
            //厚度%偏差
            double[] thickpercents_last;
            thickpercents_last = d_last.ThickPercents;

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

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


        bool[] CalStable(double[] thickpercents_diff, double[] heatpercents_diff)
        {
            //降低要求
            //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;

            bool[] 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;
                }
            }
            return isStable;
        }



        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>
        /// 分区表
        /// </summary>
        public BoltMapCell[] Boltmap;

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


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

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

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


        #endregion
    }


}
