using FLY.FilmCasting.AutoDie.IService;
using FLY.FilmCasting.AutoDie.Server.Model;
using FLY.Thick.Base.UI;
using GalaSoft.MvvmLight.Command;
using LiveCharts;
using LiveCharts.Configurations;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using IBulkDbService = FLY.FilmCasting.AutoDie.IService.IBulkDbService;

namespace FLY.FilmCasting.AutoDie.UI.Client
{
    public class PgHeatAnalyseVm : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        #region 图表控制
        public Func<double, string> YFormatter { get; private set; }
        public int XMax { get; protected set; }
        #endregion


        /// <summary>
        /// 厚度%
        /// </summary>
        public ChartValues<double> ThickPercentDiffs { get; } = new ChartValues<double>();
        public ChartValues<double> HeatDiffs { get; } = new ChartValues<double>();
        public ChartValues<double> HeatDiffEffects { get; } = new ChartValues<double>();

        public object MapperHeats { get; private set; }

        [PropertyChanged.DependsOn(nameof(Kp))]
        public object MapperThickPercents { get; private set; }


        #region 参数
        /// <summary>
        /// 比例因子
        /// </summary>
        public double Kp { get; set; } = 3;

        /// <summary>
        /// 复位区号
        /// </summary>
        public int OrgBoltNo { get; set; } = 1;

        /// <summary>
        /// 收缩率
        /// </summary>
        public double UniformShrinkPercent { get; set; } = 0;

        #endregion
        #region 状态
        /// <summary>
        /// 当前厚度%变化极差 单位%
        /// </summary>
        public double CurrMaxMin { get; set; } = -1;

        /// <summary>
        /// 当前加热与厚度相关性
        /// </summary>
        public double CurrHeatThickR { get; set; } = -1;


        /// <summary>
        /// 当前加热与厚度 比例
        /// </summary>
        public double CurrKp { get; set; } = -1;

        /// <summary>
        /// 数据异常，不能比较
        /// </summary>
        public bool IsError { get; set; } = false;
        #endregion

        #region 分区表
        public ObservableCollection<BoltMapCell> BoltMap { get; } = new ObservableCollection<BoltMapCell>();

        /// <summary>
        /// 加热生效曲线,允许为空，则不生效, 必须是 2,3,5,3,2 这样对称的数组
        /// </summary>
        public int[] HeatEffectCurve { get; set; }

        public bool IsUsedMap { get; set; }

        /// <summary>
        /// 参数修改，不会触发 偏差数据更新
        /// </summary>
        public bool IsSkipUpdateD { get; set; }

        #endregion

        public RelayCommand KpCollapseCmd { get; }
        public RelayCommand KpExpandCmd { get; }

        public RelayCommand ApplyCmd { get; }

        public RelayCommand TestMapCmd { get; }

        public RelayCommand DelMapCmd { get; }

        public UcThickHeatVm[] ucThickHeatVms;

        IFeedbackHeatService feedback;
        IHeatBufService heatBuf;
        IHeatCellService heatCell;

        public PgHeatAnalyseVm()
        {
            YFormatter = (y) =>
            {
                string text = y + "%";
                return $"{text,6}";
            };

            MapperHeats = Mappers.Xy<double>()
                .X((value, index) =>
                {
                    return index + 1;
                })
                .Y((value, index) =>
                {
                    return value;
                });

            MapperThickPercents = Mappers.Xy<double>()
                .X((value, index) =>
                {
                    return index + 1;
                })
                .Y((value) =>
                {
                    return value * Kp;
                });

            KpCollapseCmd = new RelayCommand(KpCollapse);
            KpExpandCmd = new RelayCommand(KpExpand);

            ApplyCmd = new RelayCommand(Apply);
            TestMapCmd = new RelayCommand(TestMap);
            DelMapCmd = new RelayCommand(DelMap);
        }



        public void Init(
            IFeedbackHeatService feedback,
            IHeatBufService heatBuf,
            IHeatCellService heatCell,
            IBulkDbService bulkDbService
            )
        {
            this.feedback = feedback;
            this.heatBuf = heatBuf;
            this.heatCell = heatCell;

            Misc.BindingOperations.SetBinding(
                this.heatCell, nameof(this.heatCell.Kp),
                this, nameof(Kp));

            Misc.BindingOperations.SetBinding(
                this.heatBuf, nameof(this.heatBuf.HeatEffectCurve),
                this, nameof(HeatEffectCurve));

            Misc.BindingOperations.SetBinding(
                this.heatBuf, nameof(this.heatBuf.Boltmap),
                () =>
                {
                    BoltMap.Clear();
                    if (this.heatBuf.Boltmap != null)
                    {
                        for (int i = 0; i < this.heatBuf.Boltmap.Count(); i++)
                        {
                            BoltMap.Add(this.heatBuf.Boltmap[i].Clone());
                        }
                    }
                    if (BoltMap.Count() > 0)
                        IsUsedMap = true;
                });


            ucThickHeatVms = new UcThickHeatVm[2];
            for (int i = 0; i < 2; i++)
            {
                ucThickHeatVms[i] = new UcThickHeatVm();
                ucThickHeatVms[i].Init($"记录{i + 1}", bulkDbService, 0 - i, Kp);
                Misc.BindingOperations.SetBinding(this, nameof(Kp), ucThickHeatVms[i], nameof(UcThickHeatVm.Kp));
                ucThickHeatVms[i].FrameUpdated += PgHeatAnalyseVm_FrameUpdated;
            }

            Misc.BindingOperations.SetBinding(this.feedback, nameof(this.feedback.ChannelCnt), () =>
            {
                XMax = this.feedback.ChannelCnt + 1;
            });

            UpdateD();

            this.PropertyChanged += PgHeatAnalyseVm_PropertyChanged;

        }

        private void PgHeatAnalyseVm_FrameUpdated()
        {
            UpdateD();
        }

        string[] propertyNames_updateD = new string[] { 
            nameof(Kp), nameof(HeatEffectCurve), nameof(IsUsedMap) };
        private void PgHeatAnalyseVm_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (propertyNames_updateD.Contains(e.PropertyName))
            {
                if (IsSkipUpdateD)
                    return;
                UpdateD();
            }
        }

        bool IsValid()
        {
            if (!ucThickHeatVms.All(thvm => thvm.IsVaild))
                return false;

            if (ucThickHeatVms[0].frame.Thicks.Count() != ucThickHeatVms[1].frame.Thicks.Count())
                return false;

            if (ucThickHeatVms[0].frame.Heats.Count() != ucThickHeatVms[1].frame.Heats.Count())
                return false;

            if (feedback.ChannelCnt != ucThickHeatVms[0].frame.Heats.Count())
                return false;

            if (feedback.ChannelCnt != ucThickHeatVms[0].frame.Thicks.Count())
                return false;

            if (ucThickHeatVms[0].frame.FilmLength != ucThickHeatVms[1].frame.FilmLength)
                return false;//膜距离不同不能比较

            if (Math.Abs( ucThickHeatVms[0].frame.FilmWidth - ucThickHeatVms[1].frame.FilmWidth)>100)
                return false;//膜宽差异大于100mm 不能比较

            if (Math.Abs(ucThickHeatVms[0].frame.Avg - ucThickHeatVms[1].frame.Avg) > 10)
                return false;//厚度差异大于10um 不能比较

            if (ucThickHeatVms[0].frame.BoltMapInfo.LeftEmptyBolts != ucThickHeatVms[1].frame.BoltMapInfo.LeftEmptyBolts)
                return false;//左右无效螺丝 不同不能比较

            if (ucThickHeatVms[0].frame.BoltMapInfo.RightEmptyBolts != ucThickHeatVms[1].frame.BoltMapInfo.RightEmptyBolts)
                return false;//左右无效螺丝 不同不能比较

            if (ucThickHeatVms[0].frame.BoltMapInfo.UniformShrinkPercent != ucThickHeatVms[1].frame.BoltMapInfo.UniformShrinkPercent)
                return false;//收缩率 不同不能比较

            if (ucThickHeatVms[0].frame.BoltMapInfo.MidOffset != ucThickHeatVms[1].frame.BoltMapInfo.MidOffset)
                return false;//偏移 不同不能比较

            if (ucThickHeatVms[0].frame.BoltMapInfo.BoltCnt != ucThickHeatVms[1].frame.BoltMapInfo.BoltCnt)
                return false;//螺丝数量 不同不能比较

            if (ucThickHeatVms[0].frame.BoltMapInfo.BoltWidth != ucThickHeatVms[1].frame.BoltMapInfo.BoltWidth)
                return false;//螺丝宽度 不同不能比较

            return true;
        }
        class CallDiffResult
        {
            public double[] thkPercentDiffs;
            public double[] heatDiffs;
            public double[] heatEffects;
            public double maxmin;
            public double r;
            public double kp;

        }
        CallDiffResult CalDiff(IEnumerable<BoltMapCell> boltmap, IEnumerable<double> heatEffectCurve)
        {
            if (!IsValid())
            {
                //失败，加热棒数据不一致 
                return null;
            }

            CallDiffResult result = new CallDiffResult();
            double[] thickpercents0 = ucThickHeatVms[0].GetThickPercents(boltmap);
            int[] heats0 = ucThickHeatVms[0].GetHeats();

            double[] thickpercents1 = ucThickHeatVms[1].GetThickPercents(boltmap);
            int[] heats1 = ucThickHeatVms[1].GetHeats();

            int channelCnt = feedback.ChannelCnt;

            result.thkPercentDiffs = new double[channelCnt];
            result.heatDiffs = new double[channelCnt];
            for (int i = 0; i < channelCnt; i++)
            {
                result.thkPercentDiffs[i] = thickpercents0[i] - thickpercents1[i];
                result.heatDiffs[i] = heats1[i] - heats0[i];
            }
            double heatDiffAvg = result.heatDiffs.Average();
            for (int i = 0; i < channelCnt; i++)
            {
                result.heatDiffs[i] -= heatDiffAvg;
            }
            result.heatEffects = FLY.FilmCasting.AutoDie.Common.MyMath.Filter(result.heatDiffs, heatEffectCurve);


            //-----------------------------------------------------------------------------------------------------------------------------------------
            //厚度%差 极差，判断可信度用
            double min = result.heatDiffs.Min();
            double max = result.heatDiffs.Max();
            result.maxmin = max - min;

            //-----------------------------------------------------------------------------------------------------------------------------------------
            //当前数据的加热与厚度相关性
            result.r = Misc.MyMath.Correl(result.thkPercentDiffs, result.heatEffects);

            //-----------------------------------------------------------------------------------------------------------------------------------------
            //当前数据的加热与厚度 比例
            double b;
            double a;
            Misc.MyMath.Linest(result.heatDiffs, result.thkPercentDiffs, out a, out b);
            result.kp = a;
            return result;
        }
        void UpdateD()
        {
            ThickPercentDiffs.Clear();
            HeatDiffs.Clear();
            HeatDiffEffects.Clear();

            var result = CalDiff(IsUsedMap ? BoltMap.ToArray() : null, HeatEffectCurve == null ? null : HeatEffectCurve.Select(d => (double)d));

            if (result != null)
            {
                ThickPercentDiffs.AddRange(result.thkPercentDiffs);
                HeatDiffs.AddRange(result.heatDiffs);
                HeatDiffEffects.AddRange(result.heatEffects);
                CurrMaxMin = result.maxmin;
                CurrHeatThickR = result.r;
                CurrKp = result.kp;
                IsError = false;
            }
            else
            {
                IsError = true;
                CurrMaxMin = -1;
                CurrHeatThickR = -1;
                CurrKp = -1;
            }
        }
        private void DelMap()
        {
            BoltMap.Clear();
        }

        private void TestMap()
        {
            if (!checkValid_map())
            {
                FLY.ControlLibrary.Window_WarningTip.Show("异常", "分区表格式出错");
                return;
            }
            UpdateD();
        }
        bool IsMapSame()
        {
            int cnt = 0;
            if (this.heatBuf.Boltmap != null)
                cnt = this.heatBuf.Boltmap.Count();

            if (BoltMap.Count() != cnt)
                return false;
            for (int i = 0; i < cnt; i++)
            {
                BoltMapCell modify = BoltMap[i];
                BoltMapCell used = this.heatBuf.Boltmap[i];

                if (modify.NewNo != used.NewNo)
                    return false;

                if (modify.OldNo != used.OldNo)
                    return false;
            }
            return true;
        }
        bool checkValid_map()
        {
            bool map_err = false;
            bool g_o = false;
            bool g_n = false;
            //Map 检测
            //OldNo,NewNo不能 <1 >NBolts
            //Map OldNo 必须从小到大排列
            //Map NewNo 必须从小到大排列
            for (int i = 0; i < BoltMap.Count(); i++)
            {
                if (BoltMap[i].NewNo < 1 || BoltMap[i].NewNo > feedback.ChannelCnt)
                {
                    map_err = true;
                    break;
                }
                if (BoltMap[i].OldNo < 1 || BoltMap[i].OldNo > feedback.ChannelCnt)
                {
                    map_err = true;
                    break;
                }

                int i_next = i + 1;
                if (i_next >= BoltMap.Count())
                    break;

                if (BoltMap[i].OldNo >= BoltMap[i_next].OldNo)
                {
                    if (!g_o)
                        g_o = true;
                    else
                    {
                        map_err = true;
                        break;
                    }

                }

                if (BoltMap[i].NewNo >= BoltMap[i_next].NewNo)
                {
                    if (!g_n)
                        g_n = true;
                    else
                    {
                        map_err = true;
                        break;
                    }

                }
            }
            return !map_err;
        }
        bool CheckValid_HeatEffectCurve()
        {
            int[] list = HeatEffectCurve;

            if ((list == null) || (list.Count() == 0))
            {
                return false;
            }
            int cnt = list.Count();
            if (cnt % 2 != 1)
                return false;

            for (int i = 0; i < list.Count() / 2; i++)
            {
                int idx0 = i;
                int idx_0 = list.Count() - 1 - idx0;
                if (list[idx0] != list[idx_0])
                {
                    return false;
                }
                int idx1 = idx0 + 1;
                if (list[idx0] > list[idx1])
                {
                    return false;
                }
            }
            return true;
        }
        private void Apply()
        {
            if (!WdPassword.Authorize("AutoDie"))
                return;

            if (IsUsedMap && BoltMap.Count() > 0)
            {
                this.heatBuf.Boltmap = BoltMap.ToArray();
            }
            else {
                this.heatBuf.Boltmap = null;
            }

            if (CheckValid_HeatEffectCurve())
            {
                heatBuf.HeatEffectCurve = HeatEffectCurve;
            }
            heatBuf.Apply();

            heatCell.Kp = Kp;
            heatCell.Apply();

            FLY.ControlLibrary.Window_Tip.Show("通知", "应用成功", TimeSpan.FromSeconds(2));
        }


        private void KpExpand()
        {
            if (Kp < 6)
                Kp += 0.1;
        }

        private void KpCollapse()
        {
            if (Kp > 0.2)
                Kp -= 0.1;
        }
    }
}
