using FLY.Thick.FilmCasting.Common;
using FLY.Thick.FilmCasting.IService;
using FLY.Thick.FilmCasting.Server.Model;
using FObjBase;
using LiveCharts;
using LiveCharts.Configurations;
using LiveCharts.Wpf;
using Misc;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security.RightsManagement;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;

namespace FLY.Thick.FilmCasting.UI.UiModule
{
    public class TempScanGraphVm : INotifyPropertyChanged
    {
        const int MARKNO_TEXTUP = 1;

        /// <summary>
        /// Y轴%显示，最小的中值
        /// </summary>
        const int MIN_PERCENT_TARGET = 5;
        /// <summary>
        /// Y轴最小公差范围
        /// </summary>
        const int MIN_TOLERANCE = 1;
        #region 图表控制
        public double YMax { get; set; } = double.NaN;
        public double YMin { get; set; } = double.NaN;
        public double Target { get; set; } = double.NaN;
        public double TolerancePercent { get; set; } = 0.05;
        public double Tolerance { get; set; } = MIN_TOLERANCE;
        public double ToleranceYMax { get; set; } = double.NaN;
        public double ToleranceYMin { get; set; } = double.NaN;
        public double Tolerance2YMax { get; set; } = double.NaN;
        public double Tolerance2YMin { get; set; } = double.NaN;
        public double XMax { get; set; } = double.NaN;
        public double XMin { get; set; } = double.NaN;

        public int XStep { get; set; } = 10;

        public Func<double, string> YFormatter { get; set; }
        public Func<double, string> XFormatter { get; set; }

        public SeriesCollection Series { get; set; }

        #endregion



        #region 界面统计值
        /// <summary>
        /// 平均值
        /// </summary>
        public double Average { get; protected set; } = double.NaN;

        public double Sigma2 { get; protected set; } = double.NaN;
        /// <summary>
        /// 最大值
        /// </summary>
        public double Max { get; protected set; } = double.NaN;
        /// <summary>
        /// 最小值
        /// </summary>
        public double Min { get; protected set; } = double.NaN;

        public string Sigma2Text { get; protected set; }
        public string MaxText { get; protected set; }
        public string MinText { get; protected set; }

        /// <summary>
        /// 测量时间
        /// </summary>
        public DateTime Time { get; protected set; }

        const int NULL_VALUE = -100000;

        /// <summary>
        /// 总数量
        /// </summary>
        public int BoltCnt { get; set; } = 100;

        public int DataBegin { get; set; } = NULL_VALUE;
        public int DataBeginOfBoltNo => DataBegin + 1;
        public int DataEnd { get; set; } = NULL_VALUE;
        public int DataEndOfBoltNo => DataEnd + 1;
        #endregion

        protected List<Brush> AreaColors;

        protected ChartValues<double> Values = new ChartValues<double>();
        protected List<double> datas = new List<double>();
        protected object Mapper;
        protected object Mapper2;
        protected FilmCastingProfileParam profileParam;
        protected TempScanGraphItemParam graphparam;
        IBulkDbService bulkDb;
        ParamDictionary paramDictionary;
        IBoltMapFilmCastingService boltMapFilmCastingService;


        public TempScanGraphVm()
        {
            #region 与数据无关界面参数

            AreaColors = FLY.Thick.FilmCasting.UI.Themes.Colors.AreaColors;

            YFormatter = (y) =>
            {
                //只显示 Target,ToleranceYMax,ToleranceYMin,Tolerance2YMax,Tolerance2YMin
                string text = "";
                double target = Target;
                if (graphparam.IsPercent && target > MIN_PERCENT_TARGET)
                {
                    if (y == target)
                    {
                        text = y.ToString("F1");
                    }
                    else
                    {
                        double percent = 100.0 * (y - target) / target;
                        if (percent > 0)
                        {
                            text = $"+{Math.Abs(percent):F1}%";
                        }
                        else
                        {
                            text = $"-{Math.Abs(percent):F1}%";
                        }
                    }
                }
                else
                {
                    text = y.ToString("F1");
                }
                return $"{text,6}";
            };

            XFormatter = (x) =>
            {
                //已经是分区号
                return $"{x}";
            };

            Mapper = Mappers.Xy<double>()
                .X((value, index) =>
                {
                    return index + 1;
                })
                .Y(value => value)
                .Fill((value) =>
                {
                    if ((value > Tolerance2YMax) || (value < Tolerance2YMin))
                    {
                        return AreaColors[2];
                    }
                    else if ((value > ToleranceYMax) || (value < ToleranceYMin))
                    {
                        return AreaColors[1];
                    }
                    else
                    {
                        return AreaColors[0];
                    }
                });
            Mapper2 = Mappers.Xy<double>()
                .X((value, index) =>
                {
                    return index + 1;
                })
                .Y(value => value);
            #endregion

        }

        public void Init(
            TempScanGraphItemParam graphparam,
            IBulkDbService bulkDb,
            ParamDictionary paramDictionary,
            FilmCastingProfileParam profileParam,
            IBoltMapFilmCastingService boltMapFilmCastingService
            )
        {
            this.graphparam = graphparam;
            this.bulkDb = bulkDb;
            this.paramDictionary = paramDictionary;
            this.profileParam = profileParam;
            this.boltMapFilmCastingService = boltMapFilmCastingService;

            paramDictionary.SetBinding(this, nameof(XStep), XStep);

            UpdateSeriesType();
            UpdateY();

            Misc.BindingOperations.SetBinding(boltMapFilmCastingService, nameof(boltMapFilmCastingService.BoltCnt), this, nameof(BoltCnt));


            Misc.BindingOperations.SetBinding(profileParam, nameof(profileParam.DataRange),()=>
            {
                if (profileParam.DataRange.IsValid)
                {
                    DataBegin = profileParam.DataRange.Begin;
                    DataEnd = profileParam.DataRange.End;
                }
                else {
                    DataBegin = NULL_VALUE;
                    DataEnd = NULL_VALUE;
                }
            });
            Misc.BindingOperations.SetBinding(profileParam, nameof(profileParam.ScanRange), () =>
            {
                UpdateX();
            });

            graphparam.PropertyChanged += Graphparam_PropertyChanged;
            profileParam.PropertyChanged += ProfileParam_PropertyChanged;
            this.PropertyChanged += ScanGraphVm_PropertyChanged;
            bulkDb.TempFrameChanged += BulkDb_TempFrameChanged;
        }

        protected void UpdateSeriesType()
        {
            if (graphparam.IsLineMode)
            {
                LineSeries lineSeries_avg = new LineSeries
                {
                    Values = Values,
                    StrokeThickness = 3,
                    Stroke = new SolidColorBrush(Colors.DarkBlue),
                    Fill = AreaColors[4],
                    PointGeometry = null,
                    Configuration = Mapper2
                };
                lineSeries_avg.SetValue(System.Windows.Controls.Panel.ZIndexProperty, 10);
                lineSeries_avg.SetBinding(LineSeries.AreaLimitProperty, new Binding(nameof(Target)) { Source = this });
                
                Series = new SeriesCollection
                {
                    lineSeries_avg
                };
            }
            else
            {
                Series = new SeriesCollection
                {
                    new Column2Series
                    {
                        Values = Values,
                        Stroke = new SolidColorBrush(Colors.Black),
                        StrokeThickness = 1,
                        PointGeometry = null,
                        Configuration = Mapper
                    }
                };
                ((Series)Series[0]).SetBinding(Column2Series.YAxisCrossingProperty, new Binding(nameof(Target)) { Source = this });
            }

        }
        protected void BulkDb_TempFrameChanged(object sender, EventArgs e)
        {
            BulkDbTempFrameChangedEventArgs reponse = e as BulkDbTempFrameChangedEventArgs;
            Time = reponse.Time;

            //当 reponse.D == null, 清空数据

            if (reponse.D == null) {
                //清除全部数据
                datas.Clear();
                Values.Clear();
                return;
            }


            for (int i = 0; i < reponse.D.Count(); i++)
            {
                int index = i + reponse.StartIndex;
                if (index < datas.Count)
                    datas[index] = reponse.D[i];
                else
                {
                    while (index > datas.Count)
                    {
                        datas.Add(double.NaN);
                    }
                    datas.Add(reponse.D[i]);
                }
            }

            //更新平均值,它会引起 YFormatter 刷新
            UpdateAverage();

            PropertyChanged_Average();

            for (int i = 0; i < reponse.D.Count(); i++)
            {
                int index = i + reponse.StartIndex;
                if (index < Values.Count)
                    Values[index] = reponse.D[i];
                else
                {
                    while (index > Values.Count)
                    {
                        Values.Add(double.NaN);
                    }
                    Values.Add(reponse.D[i]);
                }
            }
        }

        static string[] propertyname_updateMaxMinText = new string[] {
            nameof(Average),nameof(Max),nameof(Min),nameof(Sigma2)};

        protected void ScanGraphVm_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {

            if (e.PropertyName == nameof(Target))
            {
                if (graphparam.IsPercent)
                    NotifyPropertyChanged(nameof(YFormatter));
            }

            if (propertyname_updateMaxMinText.Contains(e.PropertyName))
                UpdateMaxMinTextAsync();

        }

        protected void ProfileParam_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(profileParam.Target))
            {
                PropertyChanged_ParamTarget();
            }
            else if (e.PropertyName == nameof(profileParam.TolerancePercent))
            {
                PropertyChanged_ParamTolerancePercent();
            }
        }


        protected void Graphparam_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(graphparam.YRangePercent))
            {
                PropertyChanged_YRangePercent();
            }
            else if (e.PropertyName == nameof(graphparam.IsPercent))
            {
                NotifyPropertyChanged(nameof(YFormatter));
                UpdateMaxMinTextAsync();
            }
            else if (e.PropertyName == nameof(graphparam.IsAutoTarget)) {
                PropertyChanged_IsAutoTarget();
            }
            else if (e.PropertyName == nameof(graphparam.IsLineMode))
            {
                UpdateSeriesType();
            }
        }


        protected void UpdateY()
        {
            PropertyChanged_IsAutoTarget();
        }
        /// <summary>
        /// profileParam.Target  变化了，触发
        /// </summary>
        void PropertyChanged_IsAutoTarget()
        {
            //自动目标值模式 且 已经有平均值
            if (graphparam.IsAutoTarget && (!double.IsNaN(Average)))
            {
                PropertyChanged_Average();
            }
            else {
                Target = profileParam.Target;
                PropertyChanged_Target();
            }
        }

        /// <summary>
        /// profileParam.Target  变化了，触发
        /// </summary>
        void PropertyChanged_ParamTarget() 
        {
            //自动目标值模式 且 已经有平均值
            if (graphparam.IsAutoTarget && (!double.IsNaN(Average))) 
            {
                return; //现在 Y轴 与 产品中的目标值 无关
            }

            Target = profileParam.Target;
            PropertyChanged_Target();
        }

        /// <summary>
        /// profileParam.TolerancePercent  变化了，触发
        /// </summary>
        void PropertyChanged_ParamTolerancePercent()
        {
            TolerancePercent = profileParam.TolerancePercent;
            PropertyChanged_TolerancePercent();
        }

        /// <summary>
        /// TolerancePercent  变化了，触发
        /// </summary>
        void PropertyChanged_TolerancePercent()
        {
            Tolerance = Math.Max(TolerancePercent * Target, MIN_TOLERANCE);
            PropertyChanged_Target_Tolerance_YRangePercent();
        }

        /// <summary>
        /// Target 变化了，触发
        /// </summary>
        void PropertyChanged_Target()
        {
            Tolerance = Math.Max(TolerancePercent * Target, MIN_TOLERANCE);
            PropertyChanged_Target_Tolerance_YRangePercent();
        }

        /// <summary>
        /// target 或 tolerance 或 yrangePercent 变化了，触发
        /// </summary>
        void PropertyChanged_Target_Tolerance_YRangePercent() 
        {
            ToleranceYMax = Target + Tolerance;
            ToleranceYMin = Target - Tolerance;
            Tolerance2YMax = Target + Tolerance * 2;
            Tolerance2YMin = Target - Tolerance * 2;

            YMax = Target + Tolerance * graphparam.YRangePercent;
            YMin = Target - Tolerance * graphparam.YRangePercent;
        }
        
        void PropertyChanged_YRangePercent() 
        {
            YMax = Target + Tolerance * graphparam.YRangePercent;
            YMin = Target - Tolerance * graphparam.YRangePercent;
        }
        void PropertyChanged_Average() 
        {
            if (!graphparam.IsAutoTarget)
                return;

            if (double.IsNaN(Average))
                return;

            if (!double.IsNaN(Target))//之前有显示范围
            {
                double range = YMax - YMin;
                double target = (YMax + YMin) / 2;
                range = range * 2 / 3 / 2;

                if (Math.Abs(target - Average)<range)
                {
                    //变化很小, 不调整显示
                    return;
                }
            }

            Target = Average;
            PropertyChanged_Target();
        }

        protected void UpdateX()
        {
            if (profileParam.ScanRange.IsValid)
            {
                //数据的X轴，是螺丝号，从1开始
                //profileParam.ScanRange 扫描范围 是序号，从0开始
                //当显示全部螺丝时， 扫描范围 = 0~119  （120个螺丝）
                //XMin = 0， XMax = 121
                XMin = profileParam.ScanRange.Begin;
                XMax = profileParam.ScanRange.End + 2;
            }
        }



        void UpdateMaxMinTextAsync()
        {
            PollModule.Current.Poll_JustOnce(UpdateMaxMinText, this, MARKNO_TEXTUP);
        }
        protected void UpdateMaxMinText() 
        {
            if (graphparam.IsPercent && (Average > MIN_PERCENT_TARGET))
            {
                MaxText = $"+{((Max - Average) / Average) * 100.0:F1}%";
                MinText = $"-{((Average - Min) / Average) * 100.0:F1}%";
                Sigma2Text = $"{(Sigma2 / Average) * 100.0:F1}%";
            }
            else
            {
                MaxText = $"{Max:F1}";
                MinText = $"{Min:F1}";
                Sigma2Text = $"{Sigma2:F1}";
            }
        }
        protected void UpdateAverage()
        {
            if (DataBegin < 0)
                return;
            if (DataEnd >= BoltCnt)
                return;
            if (DataEnd <= DataBegin)
                return;

            int beginIndex = DataBegin;
            int endIndex = DataEnd;
            List<double> list = new List<double>();
            for (int i = beginIndex; i <= endIndex; i++) {
                
                if (i < 0)
                {
                    i = 0;
                }
                else if (i >= datas.Count()) {
                    break;
                }
                double v = datas[i];
                if (!double.IsNaN(v))
                    list.Add(datas[i]);
            }

            if (list.Count() > 0)
            {
                Average = list.Average();
                Max = list.Max();
                Min = list.Min();
                Sigma2 = list.Sigma() * 2;
            }
            else
            {
                Average = double.NaN;
                Max = double.NaN;
                Min = double.NaN;
                Sigma2 = double.NaN;
            }

        }
        
        protected void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}
