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.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;

namespace FLY.Thick.FilmCasting.UI.UiModule
{
    public class ScanGraphVm : 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 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 int Mix { get; protected set; } = 2;

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

        /// <summary>
        /// 测量时间
        /// </summary>
        public DateTime EndTime { get; protected set; }
        /// <summary>
        /// 数据库中的序号
        /// </summary>
        public long Id { get; protected set; }

        public double Velocity { get; protected set; }
        public double Position { get; protected set; }

        /// <summary>
        /// 膜宽 mm
        /// </summary>
        public double FilmWidth { get; protected set; }

        #endregion

        protected List<Brush> AreaColors;

        //最大只能显示100个数据，大于100， 都会被/2
        protected ChartValues<double> Values = new ChartValues<double>();

        //double 转 XY
        protected object Mapper;

        protected FilmCastingProfileParam profileParam;
        protected ScanGraphItemParam graphparam;
        IBulkDbService bulkDb;
        Lc_ScanData scanData;
        ParamDictionary paramDictionary;
        public ScanGraphVm()
        {
            #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) =>
            {
                return $"{x + 1}";
            };

            Mapper = Mappers.Xy<double>()
                .X((value, index) =>
                {
                    return index;
                })
                .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];
                    }
                });
            #endregion

            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 });
        }

        public void Init(
            ScanGraphItemParam graphparam,
            IBulkDbService bulkDb,
            ParamDictionary paramDictionary,
            FilmCastingProfileParam profileParam
            )
        {
            this.graphparam = graphparam;
            this.bulkDb = bulkDb;
            this.paramDictionary = paramDictionary;
            this.profileParam = profileParam;
            Init();
        }
        void Init()
        {

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

            UpdateY();
            UpdateX();

            Values.Clear();

            Id = -1;
            UpdateId();

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

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

        private void ScanGraphVm_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {

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

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

        }

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

        private void BulkDb_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(bulkDb.LastScanDataId))
            {
                UpdateId();
            }
            else if (e.PropertyName == nameof(FObjBase.FObjServiceClient.IsConnected))
            {
                if ((bulkDb as FObjBase.FObjServiceClient).IsConnected)
                {
                    UpdateId();
                }
            }
        }

        private void Graphparam_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(graphparam.Number))
            {
                UpdateId();
            }
            else if (e.PropertyName == nameof(graphparam.Mix))
            {
                UpdateId();
            }
            else if (e.PropertyName == nameof(graphparam.YRangePercent))
            {
                UpdateY();
            }
            else if (e.PropertyName == nameof(graphparam.IsPercent))
            {
                NotifyPropertyChanged(nameof(YFormatter));
                UpdateY();
                UpdateMaxMinText();

            }
            else if (e.PropertyName == nameof(graphparam.IsAutoTarget))
            {
                UpdateY();
            }
        }



        void UpdateY()
        {
            //使用 profileParam 的参数显示
            UpdateY_profileParam();
        }
        void UpdateY_profileParam()
        {
            double target = this.profileParam.Target;

            if (graphparam.IsAutoTarget)
            {
                if (!double.IsNaN(Average))
                    target = Average;
            }

            double tolerance = this.profileParam.TolerancePercent * target;

            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 UpdateId()
        {
            Update4IdChanged();
        }

        void UpdateMaxMinText()
        {
            PollModule.Current.Poll_JustOnce(() =>
            {
                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}";
                }
            }, this, MARKNO_TEXTUP);
        }

        void UpdateAverage()
        {
            if (this.scanData == null)
            {
                Time = DateTime.MinValue;
                Id = -1;
                Position = -1;
                Velocity = -1;
                FilmWidth = -1;
                return;
            }
            else
            {
                Time = this.scanData.Time;
                EndTime = this.scanData.EndTime;
                Id = this.scanData.ID;
                Position = this.scanData.FilmPosition;
                Velocity = this.scanData.FilmVelocity;
                FilmWidth = this.scanData.FilmWidth;
            }


            List<double> list = new List<double>();
            double[] values = this.scanData.Thicks;
            if (this.profileParam.DataRange.IsValid) {

                int beginIndex = this.profileParam.DataRange.Begin;
                int endIndex = this.profileParam.DataRange.End;

                for (int i = beginIndex; i <= endIndex; i++)
                {
                    if (i < 0)
                        i = 0;
                    if (i >= values.Count())
                        break;
                    double v = values[i];
                    if (!double.IsNaN(v))
                        list.Add(values[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;
            }
        }
        
        void Update4IdChanged()
        {
            if (graphparam.Number > 0
                && bulkDb.LastScanDataId == Id
                && Mix == graphparam.Mix)
            {
                //不需要更新！！！！！！！
                return;
            }
            bulkDb.GetFrame(
                new Pack_GetFrameRequest() { Id = graphparam.Number, Mix = graphparam.Mix },
                (object AsyncContext, object retData) =>
                {
                    Pack_GetFrameReponse reponse = retData as Pack_GetFrameReponse;
                    
                    this.scanData = reponse.scanData;
                    Mix = reponse.Request.Mix;

                    //清除全部数据
                    Values.Clear();

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

                    //更新Target
                    UpdateY();

                    UpdateX();

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

                    if (scanData != null && scanData.Thicks != null)
                    {
                        Values.AddRange(scanData.Thicks);
                    }

                }, this);
        }

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