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;
        const int MARKNO_TARGET_DELAY = 2;
        #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 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 DataEnd { get; set; } = NULL_VALUE;
        #endregion

        protected List<Brush> AreaColors;

        protected ChartValues<double> Values = new ChartValues<double>();
        protected List<double> datas = new List<double>();
        protected object Mapper;
        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 = "";
                if (graphparam.IsPercent)
                {
                    double target = Target;
                    if (target > 0)
                    {
                        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");
                    }
                }
                else
                {
                    text = y.ToString("F1");
                }
                return $"{text,6}";
            };
            XFormatter = (x) =>
            {
                //全部+1
                return $"{x + 1}";
            };

            Mapper = Mappers.Xy<double>()
                .X((value, index) =>
                {
                    return index;
                })
                .Y(value => value);
            #endregion

            Series = new SeriesCollection
            {
                new LineSeries
                {
                    Values = Values,
                    StrokeThickness = 3,
                    Stroke = new SolidColorBrush(Colors.DarkBlue),
                    Fill =AreaColors[4],
                    PointGeometry = null,
                    Configuration = Mapper
                }

            };
            //((Series)Series[0]).SetBinding(Column2Series.YAxisCrossingProperty, new Binding(nameof(Target)) { Source = this });
        }

        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;
            Init();
        }
        void Init()
        {

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

            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;
            bulkDb.GetTempFrame(GetTempFrameHandler, null);

        }


        void GetTempFrameHandler(object asyncContext, object retData) 
        {
            BulkDbTempFrameChangedEventArgs reponse = retData as BulkDbTempFrameChangedEventArgs;

            Time = reponse.Time;
            datas.Clear();

            if (reponse.D != null) {
                for (int i = 0; i < reponse.StartIndex; i++) {
                    datas.Add(double.NaN);
                }
                datas.AddRange(reponse.D);
            }

            //清除全部数据
            Values.Clear();
            
            //更新平均值,它会引起 YFormatter 刷新
            UpdateAverage();

            //更新Target
            UpdateY();

            
            //必须先更新Y轴, X轴资料，最后再画线！！！！！！！
            //不然 YAxisCrossing 会有机会无效

            Values.AddRange(datas);
        }
        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();

            //更新Target
            UpdateY();

            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))
            {
                UpdateY();
            }
            else if (e.PropertyName == nameof(profileParam.TolerancePercent))
            {
                UpdateY();
            }
        }

        protected void BulkDb_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(FObjBase.FObjServiceClient.IsConnected))
            {
                if ((bulkDb as FObjBase.FObjServiceClient).IsConnected)
                {
                    bulkDb.GetTempFrame(GetTempFrameHandler, null);
                }
            }
        }

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



        protected void UpdateY()
        {

            //使用 profileParam 的参数显示
            UpdateY_profileParam();




        }

        void UpdateY_profileParam()
        {
            bool isAutoTarget = false;
            double target = this.profileParam.Target;
            if (graphparam.IsAutoTarget)
            {
                if (!double.IsNaN(Average))
                {
                    target = Average;
                    isAutoTarget = true;
                }
                else {
                    if (!double.IsNaN(Target)) {
                        //没有数据 且之前能显示，不更新界面。
                        return;
                    }

                }
            }

            if (isAutoTarget)
            {
                if (!double.IsNaN(Target))
                {
                    if (profileParam.TolerancePercent == TolerancePercent) {
                        //公差没变
                        if (target < ToleranceYMax && target > ToleranceYMin) {
                            //变化很小, 不调整显示
                            return;
                        }
                    }
                }
            }


            double tolerance = this.profileParam.TolerancePercent * target;


            TolerancePercent = profileParam.TolerancePercent;
            Target = target;
            ToleranceYMax = target + tolerance;
            ToleranceYMin = target - tolerance;
            Tolerance2YMax = target + tolerance * 2;
            Tolerance2YMin = target - tolerance * 2;

            YMax = target + tolerance * graphparam.YRangePercent;
            YMin = target - tolerance * graphparam.YRangePercent;
        }

        protected void UpdateX()
        {
            if (profileParam.ScanRange.IsValid)
            {
                XMin = profileParam.ScanRange.Begin - 1;
                XMax = profileParam.ScanRange.End + 1;
            }
        }



        void UpdateMaxMinTextAsync()
        {
            PollModule.Current.Poll_JustOnce(UpdateMaxMinText, this, MARKNO_TEXTUP);
        }
        protected void UpdateMaxMinText() 
        {
            if (graphparam.IsPercent && (Average > 0))
            {
                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;
    }
}
