using FLY.FeedbackRenZiJia.IService;
using FLY.Thick.Base.UI;
using FLY.Thick.Blowing.IService;
using GalaSoft.MvvmLight.Command;
using LiveCharts;
using LiveCharts.Configurations;
using Misc;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IBulkDbService = FLY.FeedbackRenZiJia.IService.IBulkDbService;

namespace FLY.FeedbackRenZiJia.UI.Client
{
    public class PgHeatAnalyseVm : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        #region 图表控制
        public Func<double, string> YFormatter { get; private set; }
        public int XMax { get; private 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; }

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

        public bool IsMoveByTakeEffect { get; set; } = true;
        public bool IsMoveByDiffCTime { get; set; } = true;

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



        /// <summary>
        /// 旋转角度
        /// </summary>
        public double RAngle { get; set; } = 350;



        /// <summary>
        /// 反向
        /// </summary>
        public bool IsReversed { get; set; } = false;
        #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; }
        /// <summary>
        /// 自动计算 过程消息
        /// </summary>
        public string AutoMsg { get; private set; }
        #endregion

        public RelayCommand KpCollapseCmd { get; }
        public RelayCommand KpExpandCmd { get; }
        public RelayCommand LeftCmd { get; }
        public RelayCommand RightCmd { get; }
        public RelayCommand CollapseCmd { get; }
        public RelayCommand ExpandCmd { get; }
        public RelayCommand AutoCmd { get; }
        public RelayCommand ApplyCmd { get; }

        public RelayCommand TestMapCmd { get; }

        public RelayCommand DelMapCmd { get; }



        public UcThickHeatVm[] ucThickHeatVms;

        IFeedbackHeatService feedback;
        IHeatBufService heatBuf;
        IHeatCellService heatCell;
        FLY.Thick.Blowing.IService.IBlowingService blowingService;
        FLY.Thick.Blowing.IService.IBlowingDetectService blowingDetectService;
        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);
            LeftCmd = new RelayCommand(Left);
            RightCmd = new RelayCommand(Right);
            CollapseCmd = new RelayCommand(Collapse);
            ExpandCmd = new RelayCommand(Expand);
            AutoCmd = new RelayCommand(Auto);
            ApplyCmd = new RelayCommand(Apply);
            TestMapCmd = new RelayCommand(TestMap);
            DelMapCmd = new RelayCommand(DelMap);
        }



        public void Init(
            IFeedbackHeatService feedback,
            IHeatBufService heatBuf,
            IHeatCellService heatCell,
            IBulkDbService bulkDbService,
            FLY.Thick.Blowing.IService.IBlowingService blowingService,
            FLY.Thick.Blowing.IService.IBlowingDetectService blowingDetectService
            )
        {
            this.feedback = feedback;
            this.heatBuf = heatBuf;
            this.heatCell = heatCell;
            this.blowingService = blowingService;
            this.blowingDetectService = blowingDetectService;

            Misc.BindingOperations.SetBinding(
                blowingService, nameof(blowingService.BType),
                this, nameof(BType));

            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(
                blowingService, nameof(blowingService.OrgBoltNo),
                this, nameof(OrgBoltNo));

            Misc.BindingOperations.SetBinding(
                blowingDetectService, nameof(blowingDetectService.RAngle),
                this, nameof(RAngle));

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

            Misc.BindingOperations.SetBinding(
                blowingService, nameof(blowingService.IsUsedMap),
                this, nameof(IsUsedMap));


            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));
                Misc.BindingOperations.SetBinding(this, nameof(IsMoveByTakeEffect), ucThickHeatVms[i], nameof(UcThickHeatVm.IsMoveByTakeEffect));
                Misc.BindingOperations.SetBinding(this, nameof(IsMoveByDiffCTime), ucThickHeatVms[i], nameof(UcThickHeatVm.IsMoveByDiffCTime));
                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(RAngle), nameof(OrgBoltNo), nameof(Kp), nameof(HeatEffectCurve), nameof(IsUsedMap), nameof(IsReversed) };
        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.NBolts != ucThickHeatVms[0].frame.Thicks.Count())
                return false;

            //if (Math.Abs(ucThickHeatVms[0].frame.FilmLength - ucThickHeatVms[1].frame.FilmLength) > 1) 
            //{
            //    //偏差大于1m
            //    return false;//膜距离不同不能比较
            //}

            //if (Math.Abs(ucThickHeatVms[0].frame.RAngle - ucThickHeatVms[1].frame.RAngle) > 20)
            //{
            //    //偏差大于20°
            //    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(int orgBoltNo, double rAngle, bool isReversed, IEnumerable<BoltMapCell> boltmap, IEnumerable<double> heatEffectCurve)
        {
            if (!IsValid())
            {
                //失败,加热棒数据不一致 
                return null;
            }

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

            double[] thickpercents1 = ucThickHeatVms[1].GetThickPercents(orgBoltNo, rAngle, boltmap, isReversed);
            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.FeedbackRenZiJia.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(OrgBoltNo, RAngle, IsReversed, 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 (blowingService.Map != null)
                cnt = blowingService.Map.Count();

            if (BoltMap.Count() != cnt)
                return false;
            for (int i = 0; i < cnt; i++)
            {
                BoltMapCell modify = BoltMap[i];
                BoltMapCell used = blowingService.Map[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.NBolts)
                {
                    map_err = true;
                    break;
                }
                if (BoltMap[i].OldNo < 1 || BoltMap[i].OldNo > feedback.NBolts)
                {
                    map_err = true;
                    break;
                }

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

                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 (IsReversed)
            {
                FLY.ControlLibrary.Window_WarningTip.Show("应用失败", "当前图是反向,需要先去调换旋转架的正反向信号线", TimeSpan.FromSeconds(2));
                return;
            }
            if (!WdPassword.Authorize("AirRing"))
                return;


            blowingService.IsUsedMap = IsUsedMap;
            blowingService.Map = BoltMap.ToArray();
            blowingService.OrgBoltNo = OrgBoltNo;

            blowingService.Apply();

            blowingDetectService.RAngle = RAngle;
            blowingDetectService.Apply();


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

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

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

        private async void Auto()
        {
            if (!IsValid())
            {
                return;
            }
            IsSkipUpdateD = true;
            AutoMsg = "0%";

            double rAngle_min = 300;
            double rAngle_max = 358;

            int total_cal_cnt = feedback.NBolts + (int)(rAngle_max - rAngle_min + 1);
            int cal_cnt = 0;
            await Task.Factory.StartNew(() =>
            {
                int nbolts = feedback.NBolts;

                double bestr = -1;
                int orgBoltNo = OrgBoltNo;
                int bestOrgBoltNo = orgBoltNo;
                var boltmap = IsUsedMap ? BoltMap.ToArray() : null;
                double bestRAngle = RAngle;
                bool isReversed = IsReversed;
                IEnumerable<double> heatEffectCurve = HeatEffectCurve == null ? null : HeatEffectCurve.Select(d => (double)d);
                for (int i = 0; i < nbolts; i++)
                {
                    var result = CalDiff(orgBoltNo, bestRAngle, isReversed, boltmap, heatEffectCurve);

                    if (result.r > bestr)
                    {
                        bestr = result.r;
                        bestOrgBoltNo = orgBoltNo;
                    }
                    orgBoltNo++;

                    if (orgBoltNo > nbolts)
                        orgBoltNo = 1;

                    cal_cnt++;
                    AutoMsg = (1.0 * cal_cnt / total_cal_cnt).ToString("P0");
                }

                if (BType == BlowingType.Fix)
                {

                    for (double rAngle = rAngle_min; rAngle <= rAngle_max; rAngle++)
                    {
                        var result = CalDiff(bestOrgBoltNo, rAngle, isReversed, boltmap, heatEffectCurve);
                        if (result.r > bestr)
                        {
                            bestr = result.r;
                            bestRAngle = rAngle;
                        }
                        cal_cnt++;
                        AutoMsg = (1.0 * cal_cnt / total_cal_cnt).ToString("P0");
                    }
                    //锁住,不用更新画面
                    RAngle = bestRAngle;
                }
                //锁住,不用更新画面
                OrgBoltNo = bestOrgBoltNo;
            });
            
            UpdateD();
            Kp = CurrKp;
            IsSkipUpdateD = false;
            AutoMsg = "100%";
        }

        private void Expand()
        {
            if (RAngle < 358)
                RAngle += 1;
        }

        private void Collapse()
        {
            if (RAngle > 280)
                RAngle -= 1;
        }

        private void Right()
        {
            if (OrgBoltNo < feedback.NBolts)
                OrgBoltNo++;
            else
                OrgBoltNo = 1;
        }

        private void Left()
        {
            if (OrgBoltNo > 1)
                OrgBoltNo--;
            else
                OrgBoltNo = feedback.NBolts;
        }

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

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