using AutoMapper;
using FLY.FilmCasting.AutoDie.Common;
using FLY.FilmCasting.AutoDie.IService;
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>
    /// 以前 正反转加热分离，所以有两个 HeatCell
    /// 现在不分正反转，只有一个HeatCell
    /// </summary>
    public class HeatCell : IHeatCellService
    {
        #region IHeatCell 接口 成员变量

        #region 参数
        /// <summary>
        /// HeatOffset = ThickPercent * Kp
        /// </summary>
        public double Kp { get; set; } = 3;


        /// <summary>
        /// 平滑的阀值，只有超出阀值，才平滑
        /// </summary>
        public double ThresholdHeatSigma { get; set; } = 10;


        /// <summary>
        /// 自控时，不能调整总体上升下降
        /// </summary>
        public bool IsForbidUpDown { get; set; } = false;



        /// <summary>
        /// 厚度控制线 ， 厚度% 大于 厚度控制线 自动才会加热控制  
        /// </summary>
        public int CtrlLine { get; set; } = 2;

        /// <summary>
        /// 最大加热量 默认100%
        /// </summary>
        public int MaxHeat { get; set; } = 100;

        #endregion

        #region 状态


        /// <summary>
        /// 对应的100% 数据，单位是% 数量是ChannelCnt, 数据肯定是完整的！！！！！
        /// </summary>
        [DoNotCheckEquality]
        public double[] ThickPercents { get; set; }


        /// <summary>
        /// 当前加热策略改变的时间
        /// </summary>
        public DateTime LastHeatsChangedTime { get; private set; }

        /// <summary>
        /// 当前方向的加热策略，必须保证 0~100
        /// </summary>
        public int[] Heats { get; protected set; }


        private int Base { get; set; } = 0;




        /// <summary>
        /// 加热偏移量
        /// </summary>
        [DoNotCheckEquality]
        public int[] Offsets { get; protected set; }


        /// <summary>
        /// Heats+Offsets+Base
        /// </summary>
        [DoNotCheckEquality]
        public int[] PreHeats { get; protected set; }


        /// <summary>
        /// 加热量 每3个计算的sigma,最大值
        /// </summary>
        public double MaxHeatSigma { get; set; } = 0;


        #endregion

        #endregion

        public int ChannelCnt { get; set; }
        int BoltCnt;
        HeatBuf mHeatBuf;
        //IBlowingService blowingService;
        public Func<object, bool> PreHeatApplyEvent;
        public event Action<object> AfterHeatApplyEvent;
        public event Action<object> AfterClearOffsetsEvent;


        public HeatCell()
        {
            if (!Load())
                Save();

            checkParamErr();
            Init2(88);

        }
        void checkParamErr()
        {
            if (Kp < 1 || Kp > 10)
            {
                Kp = 3;
            }

            if (ThresholdHeatSigma < 1 || ThresholdHeatSigma > 50)
            {
                ThresholdHeatSigma = 10;
            }

            if (CtrlLine < 0 || CtrlLine > 10)
            {
                CtrlLine = 2;
            }
        }
        public void Init(HeatBuf heatBuf)
        {
            mHeatBuf = heatBuf;
        }
        //public void Init3(IBlowingService blowingService)
        //{
        //    this.blowingService = blowingService;
        //    this.blowingService.PropertyChanged += BlowingService_PropertyChanged;
        //}

        //private void BlowingService_PropertyChanged(object sender, PropertyChangedEventArgs e)
        //{
        //    //当复位区号改变了，同时也修改heatCell 的 thickPercents
        //    if (e.PropertyName == nameof(this.blowingService.OrgBoltNo))
        //    {
        //        if (mHeatBuf.MoveThickPercents(this.blowingService.OrgBoltNo))
        //            SetThickPercents(mHeatBuf.ThickPercents);
        //    }
        //}

        public void Init2(int channelCnt)
        {
            ChannelCnt = channelCnt;

            Base = 0;
            MaxHeatSigma = 0;

            ThickPercents = new double[ChannelCnt];
            Heats = new int[ChannelCnt];
            PreHeats = new int[ChannelCnt];
            Offsets = new int[ChannelCnt];

        }

        /// <summary>
        /// 自动模式！！！！！
        /// </summary>
        public void Auto()
        {
            if (mHeatBuf.mData.Count() == 0)
                return;
            //&& mHeatBuf.IsStable()

            if (mHeatBuf.Stability == STABILITY.OK_CORREL)//稳定 与 待定时 加热
            {
                //转为 加热棒数量
                var thickPercents = ThickPercents;

                int[] offsets = new int[ChannelCnt];
                for (int i = 0; i < ChannelCnt; i++)
                {
                    if (!double.IsNaN(thickPercents[i]) && 
                        mHeatBuf.BoltIsStable[i] && //稳定
                        (Math.Abs(thickPercents[i]) > CtrlLine))//在控制线已外
                    {
                        offsets[i] = (int)Math.Round(thickPercents[i] * Kp);
                    }
                }

                Offsets = offsets;
                UpdatePreHeats();//这句一定要加！！！！ 不然没法平滑，没法加热

                if (!IsForbidUpDown)//禁止整体上移下降
                    ModifyBase();//自动整体上移下降

                Smooth();//平滑,它会导致Offset被修改，<0的都被删除掉

                HeatApply();//执行加热
            }
            
        }


        public void SetThickPercents(double[] thkpercents)
        {
            // Math.Round(double.NaN , 2) 也是 NaN 
            ThickPercents = thkpercents.Select(tp => Math.Round(tp, 2)).ToArray();
        }

        #region IHeatCell 接口 成员函数



        /// <summary>
        /// 手动调整加热增量
        /// </summary>
        /// <param name="idx"></param>
        /// <param name="change"></param>
        public void ModifyOffset(int idx, int change)
        {
            if (idx < ChannelCnt && idx >= 0)
            {
                Offsets[idx] += change;
                UpdatePreHeats();
            }
            NotifyPropertyChanged(nameof(Offsets));
        }

        /// <summary>
        /// 参数是 baseheatoffset 的增量
        /// </summary>
        /// <param name="change"></param>
        public void ModifyBase(int change)
        {
            Base += change;
            UpdatePreHeats();
        }

        /// <summary>
        /// 调整BaseHeatOffset，令Heats 在范围内
        /// </summary>
        void ModifyBase()
        {
            int[] preheat = new int[ChannelCnt];
            for (int i = 0; i < ChannelCnt; i++)
                preheat[i] = Heats[i] + Offsets[i] + Base;

            int min = preheat.Min();
            int max = preheat.Max();
            int avg = (int)preheat.Average();

            int base_offset;
            int maxHeat = MaxHeat;

            if ((min < 0) && (max > maxHeat))
            {
                //功率极限了
                //保存平均值为50
                base_offset = avg - maxHeat / 2;
            }
            else if (max > maxHeat)
            {

                //整体往下降
                base_offset = max - maxHeat;

                if (base_offset > (avg - maxHeat / 2))//移得太多
                    base_offset = (avg - maxHeat / 2);
            }
            else
            {
                base_offset = min;
                if (base_offset < 0)
                {
                    if (base_offset < (avg - maxHeat / 2))//升得太多
                        base_offset = (avg - maxHeat / 2);
                }
            }

            Base -= base_offset;

            UpdatePreHeats();
        }

        /// <summary>
        /// 清空HeatOffset
        /// </summary>
        public void ClearOffsets()
        {
            Base = 0;

            for (int i = 0; i < ChannelCnt; i++)
            {
                Offsets[i] = 0;
            }
            NotifyPropertyChanged(nameof(Offsets));
            UpdatePreHeats();

            AfterClearOffsetsEvent?.Invoke(this);
        }

        /// <summary>
        /// 清空Heats
        /// </summary>
        public void ClearPreHeats()
        {
            int[] heats = new int[ChannelCnt];
            Array.Clear(heats, 0, ChannelCnt);
            ModifyPreHeats(heats);
        }

        /// <summary>
        /// 调整PreHeats
        /// </summary>
        /// <param name="heats"></param>
        public void ModifyPreHeats(int[] heats)
        {

            if (heats.Count() != ChannelCnt)
                return;
            Base = 0;
            for (int i = 0; i < ChannelCnt; i++)
            {
                PreHeats[i] = heats[i];
                Offsets[i] = PreHeats[i] - Heats[i];
            }
            NotifyPropertyChanged(nameof(Offsets));
            NotifyPropertyChanged(nameof(PreHeats));
            UpdateHeatSigmas();
        }
        public void ModifyHeats(int[] heats)
        {
            if (heats.Count() != ChannelCnt)
                return;

            bool isChanged = false;
            Base = 0;
            for (int i = 0; i < ChannelCnt; i++)
            {
                if (Heats[i] != heats[i])
                {
                    isChanged = true;
                    Heats[i] = heats[i];
                    Offsets[i] = PreHeats[i] - Heats[i];
                }
            }
            if (isChanged)
            {
                NotifyPropertyChanged(nameof(Offsets));
                NotifyPropertyChanged(nameof(Heats));
                LastHeatsChangedTime = DateTime.Now;
            }
        }
        /// <summary>
        /// 利用测厚仪的数据，计算出HeatOffsets
        /// </summary>
        public void Cal()
        {
            var thickpercents = ThickPercents;// Common.MyMath.ZoomOut(ThickPercents, BoltCnt / ChannelCnt);
            int[] offsets = new int[ChannelCnt];
            for (int i = 0; i < ChannelCnt; i++)
            {
                offsets[i] = (int)Math.Round(thickpercents[i] * Kp);
            }
            Offsets = offsets;
            UpdatePreHeats();
        }

        #region PreHeats 平滑处理
        double[] GetHeatSigmas(int[] heats)
        {
            var heatsigmas = new double[ChannelCnt];
            for (int i = 0; i < ChannelCnt; i++)
            {
                var dat = getSubArray(heats, i);
                heatsigmas[i] = dat.Sigma();
            }
            return heatsigmas;
        }
        void UpdateHeatSigmas()
        {
            var heatsigmas = GetHeatSigmas(PreHeats);
            MaxHeatSigma = heatsigmas.Max();
        }

        T[] getSubArray<T>(T[] array, int index)
        {
            T[] dat = new T[3];
            for (int j = 0; j < 3; j++)
            {
                int idx = index - 1 + j;
                if (idx < 0)
                    idx += array.Count();
                else if (idx >= array.Count())
                    idx -= array.Count();

                dat[j] = array[idx];
            }
            return dat;
        }
        /// <summary>
        /// 平滑，不断修改PreHeats 直到HeatsSigma 合格
        /// </summary>
        public void Smooth()
        {
            int[] heats = PreHeats.ToArray();
            int[] heats2 = new int[ChannelCnt];
            bool hasChanged = false;
            bool hasChanged2 = false;
            double[] heatsigmas;
            do
            {
                //1.更新sigma
                heatsigmas = GetHeatSigmas(heats);

                //2.检测HeatsSigma， 哪里比较大，对该位置 滤波
                for (int i = 0; i < heats.Count(); i++)
                {
                    if (heatsigmas[i] > (ThresholdHeatSigma)) //不行，要滤波！！！
                    {
                        //连续3个滤波
                        int[] dat = getSubArray(heats, i);
                        heats2[i] = (int)Math.Round(dat.Average());

                        //if (heats2[i] == heats[i]) 
                        //{ 
                        //    //居然一样，修改旁边的两个的值
                        //    if (i > 0) {
                        //        int idx = i-1;
                        //        dat = getSubArray(heats, idx);
                        //        heats2[idx] = (int)Math.Round(dat.Average());
                        //    }

                        //    if (i == heats.Count() - 1)
                        //    {
                        //        int idx = 0;
                        //        dat = getSubArray(heats, idx);
                        //        heats2[idx] = (int)Math.Round(dat.Average());
                        //    }
                        //}
                    }
                    else
                    {
                        heats2[i] = heats[i];
                    }
                }

                //3.有滤波行为，继续求循环，求sigma，判断。。。。。
                hasChanged = false;
                for (int i = 0; i < heats.Count(); i++)
                {
                    if (heats[i] != heats2[i])
                    {
                        heats[i] = heats2[i];
                        hasChanged = true;
                    }
                }
                if (hasChanged)
                    hasChanged2 = true;

            } while (hasChanged);

            if (hasChanged2)
            {
                for (int i = 0; i < ChannelCnt; i++)
                {
                    PreHeats[i] = heats[i];
                    Offsets[i] = PreHeats[i] - Heats[i] - Base;
                }
                NotifyPropertyChanged(nameof(PreHeats));
                NotifyPropertyChanged(nameof(Offsets));
                MaxHeatSigma = heatsigmas.Max();
            }
        }
        #endregion
        /// <summary>
        /// 加热输出应用
        /// </summary>
        public void HeatApply()
        {
            if (PreHeatApplyEvent != null)
            {
                if (!PreHeatApplyEvent(this))
                {
                    return;
                }
            }

            for (int i = 0; i < ChannelCnt; i++)
            {
                Offsets[i] = 0;

                int heat = PreHeats[i];
                if (heat < 0)
                {
                    //需要整体提升
                    heat = 0;
                }
                else if (heat > 100)
                {
                    //需要整体调低
                    heat = 100;
                }
                Heats[i] = heat;

            }
            NotifyPropertyChanged(nameof(Offsets));
            NotifyPropertyChanged(nameof(Heats));
            LastHeatsChangedTime = DateTime.Now;
            Base = 0;

            AfterHeatApplyEvent?.Invoke(this);
        }

        /// <summary>
        /// 修改Offsets, Base 后，执行PreHeats = Heats + Offsets + Base
        /// </summary>
        void UpdatePreHeats()
        {
            for (int i = 0; i < ChannelCnt; i++)
            {
                int preheat = Heats[i] + Offsets[i] + Base;

                if (preheat > 100)
                    preheat = 100;
                else if (preheat < 0)
                    preheat = 0;
                PreHeats[i] = preheat;
            }
            UpdateHeatSigmas();
            NotifyPropertyChanged(nameof(PreHeats));
        }

        public void Apply()
        {
            Save();
        }
        #endregion


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

                }
                return false;
            }
            return false;
        }
        void Save()
        {
            try
            {
                var jsonDb = HeatCellJsonDb.Mapper.Map<HeatCellJsonDb>(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 HeatCellJsonDb
    {
        public static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c =>
        {
            c.CreateMap<HeatCell, HeatCellJsonDb>().ReverseMap();
        }));

        #region 参数
        /// <summary>
        /// HeatOffset = ThickPercent * Kp
        /// </summary>
        public double Kp = 3;


        /// <summary>
        /// 自动对位
        /// </summary>
        public bool IsAutoONo = true;

        /// <summary>
        /// 最大加热量
        /// </summary>
        public int MaxHeat = 100;

        /// <summary>
        /// 平滑的阀值，只有超出阀值，才平滑
        /// </summary>
        public double ThresholdHeatSigma = 10;


        /// <summary>
        /// 自控时，不能调整总体上升下降
        /// </summary>
        public bool IsForbidUpDown = false;



        /// <summary>
        /// 厚度控制线 ， 厚度% 大于 厚度控制线 自动才会加热控制  
        /// </summary>
        public int CtrlLine = 2;



        #endregion
    }
}
