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;
using System.Windows.Threading;

namespace FLY.Thick.FilmCasting.UI.UiModule
{
    public class GiantMixScanGraph2Vm : 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 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; } = 100;
        
        /// <summary>
        /// 曲线显示模式
        /// </summary>
        public bool IsLineMode { get; protected set; }
        
        /// <summary>
        /// 测量 开始时间
        /// </summary>
        public DateTime Time { get; protected set; }

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

        /// <summary>
        /// 数据库中的序号, 最后一幅图的Id
        /// </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>();

        //Average-2sigma
        protected ChartValues<double> UpperValues = new ChartValues<double>();

        //Average-2sigma
        protected ChartValues<double> LowerValues = new ChartValues<double>();

        //double 转 XY
        protected object Mapper;

        protected FilmCastingProfileParam profileParam;
        protected GiantMixScanGraph2ItemParam graphparam;
        protected IBulkDbService bulkDb;
        //protected Lc_ScanData scanData;

        /// <summary>
        /// 组件中 存在的 缓存, scanDatas[0] 是最早的数据 
        /// </summary>
        protected List<Lc_ScanData> scanDatas = new List<Lc_ScanData>();
        protected double[] Averages { get; set; }
        protected double[] Sigmas { get; set; }

        protected ParamDictionary paramDictionary;
        protected DispatcherTimer timer;

        /// <summary>
        /// 正在下载中
        /// </summary>
        bool isDownloading = false;
        /// <summary>
        /// 需要下载以前的数据
        /// </summary>
        bool isDownloadPrev = false;
        /// <summary>
        /// 需要下载最新的数据
        /// </summary>
        bool isDownloadNext = false;


        public GiantMixScanGraph2Vm()
        {
            #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 + 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

        }

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

            Misc.BindingOperations.SetBinding(this.graphparam, nameof(graphparam.IsLineMode), this, nameof(IsLineMode));
            UpdateSeriesType();
            UpdateY();
            UpdateX();

            ClearValues();

            Id = -1;
            Download();

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

            Init2();
        }
        protected void ClearValues() 
        {
            Values.Clear();
            UpperValues.Clear();
            LowerValues.Clear();
        }
        protected virtual void Init2()
        {
            // 绑定全局的 X轴 间隔
            paramDictionary.SetBinding(this, nameof(XStep), Base.UI.ParamDistItemKeys.XStep, XStep);
            
        }


        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))
            {
                isDownloadNext = true;
                Download();
            }
            else if (e.PropertyName == nameof(FObjBase.FObjServiceClient.IsConnected))
            {
                if ((bulkDb as FObjBase.FObjServiceClient).IsConnected)
                {
                    //删除全部缓存，重新下载
                    scanDatas.Clear();
                    Download();
                }
            }
        }
        void mixChanged() 
        {
            if (scanDatas.Count == graphparam.Mix) {
                return;
            }

            if (scanDatas.Count > graphparam.Mix)
            {
                //删除多余的
                scanDatas.RemoveRange(0, scanDatas.Count - graphparam.Mix);
                scanDatasChanged();
            }
            else {
                isDownloadPrev = true;
                //添加更多
                Download();
            }
        }
        public void MarkFromTime()
        {
            if (scanDatas.Count() == 0)
                return;
            graphparam.FromId = scanDatas.Last().ID;
            GiantMixScanGraph2Params.Current.Save();
            clearScanDatas();
        }

        void clearScanDatas() 
        {
            //清空数据
            scanDatas.Clear();

            //清除全部数据
            ClearValues();

            Averages = null;
            Sigmas = null;

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

            //更新Target
            UpdateY();

            UpdateX();
        }

        public void ClearFromTime()
        {
            graphparam.FromId = 0;
            GiantMixScanGraph2Params.Current.Save();
            scanDatas.Clear();
            Download();
        }


        private void Graphparam_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(graphparam.Mix))
            {
                mixChanged();
            }
            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();
            }
            else if (e.PropertyName == nameof(graphparam.IsLineMode)) 
            {
                UpdateSeriesType();
            }

        }


        protected void UpdateSeriesType()
        {
            if (graphparam.IsLineMode)
            {
                LineSeries lineSeries_avg = new LineSeries
                {
                    Values = Values,
                    StrokeThickness = 5,
                    Stroke = new SolidColorBrush(System.Windows.Media.Colors.Black),
                    Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent),
                    PointGeometry = null,
                };
                lineSeries_avg.SetValue(System.Windows.Controls.Panel.ZIndexProperty, 10);

                LineSeries lineSeries_upper = new LineSeries
                {
                    Values = UpperValues,
                    StrokeThickness = 2,
                    Stroke = AreaColors[1],
                    Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent),
                    PointGeometry = null
                };

                LineSeries lineSeries_lower = new LineSeries
                {
                    Values = LowerValues,
                    StrokeThickness = 2,
                    Stroke = AreaColors[1],
                    Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent),
                    PointGeometry = null
                };

                Series = new SeriesCollection
                {
                    lineSeries_avg,
                    lineSeries_upper,
                    lineSeries_lower
                };
            }
            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 });
            }

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

            //确保没有负数，Y轴显示范围正确
            if (tolerance < MIN_TOLERANCE)
                tolerance = MIN_TOLERANCE;

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

        void updateThicks() 
        {
            if (this.scanDatas.Count() != 0)
            {
                int dataCnt = this.scanDatas.Last().Thicks.Count();
                double[] averages = new double[dataCnt];                
                double[] sigmas = new double[dataCnt];

               
                for (int j = 0; j < dataCnt; j++) 
                {
                    List<double> list = new List<double>();
                    for (int i = 0; i < this.scanDatas.Count(); i++)
                    {
                        int index = this.scanDatas.Count() - 1 - i;
                        var thks = this.scanDatas[index].Thicks;
                        if (j >= thks.Count())
                            break;
                        list.Add(thks[j]);
                    }

                    averages[j] = list.AverageNoNull();
                    sigmas[j] = list.Sigma();
                }
               

                Averages = averages;
                Sigmas = sigmas;
            }
            else {
                Averages = null;
                Sigmas = null;
            }
        }
        protected virtual void UpdateAverage()
        {
            if (this.scanDatas.Count() == 0)
            {
                Time = DateTime.MinValue;
                EndTime = DateTime.MinValue;
                Id = -1;
                Position = -1;
                Velocity = -1;
                FilmWidth = -1;

                Average = double.NaN;
                Max = double.NaN;
                Min = double.NaN;
                Sigma2 = double.NaN;

                return;
            }
            var scanData = this.scanDatas.Last();
            Time = this.scanDatas.First().Time;
            EndTime = scanData.EndTime;
            Id = scanData.ID;
            Position = scanData.FilmPosition;
            Velocity = scanData.FilmVelocity;
            FilmWidth = scanData.FilmWidth;
            


            List<double> list = new List<double>();
            double[] values = Averages;
            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 Download()
        {
            var client = bulkDb as FObjBase.Reflect.Reflect_SeviceClient;
            if (!client.IsConnected)//还没连接上。。。。
                return;
            if (!client.IsSynced)//还没同步完成。。。。
                return;

            if (isDownloading)//已经在下载中了，等等。。。。
                return;

            long id;
            int count;
            if (scanDatas.Count() == 0)
            {
                //需要下载全部数据
                id = bulkDb.LastScanDataId;
                count = graphparam.Mix;

                isDownloadNext = false;
                isDownloadPrev = false;
            }
            else {
                //1. graphparam.Mix 变化了，需要下载
                //2. bulkDb.LastScanDataId 更新了，需要下载更多

                if (isDownloadPrev)
                {
                    isDownloadPrev = false;

                    id = scanDatas.First().ID - 1;
                    count = graphparam.Mix - scanDatas.Count;
                }
                else
                {
                    isDownloadNext = false;

                    id = bulkDb.LastScanDataId;
                    count = (int)(id - scanDatas.Last().ID);
                    if (count < 0)
                    {
                        //异常，未来的数据，清空 scanDatas
                        scanDatas.Clear();
                        count = graphparam.Mix;
                    }
                }
            }


            if (graphparam.FromId > 0)
            {
                int c = (int)(id - graphparam.FromId);
                if (c < 0)
                    c = 0;
                if (c < count)
                    count = c;
            }

            if (count <= 0)
            {
                //什么都不需要做
                return;
            }

            isDownloading = true;
            bulkDb.GetRecentFrames(
                new Pack_GetRecentFramesRequest() {
                    Id = id,
                    Count = count
                },
                (object asyncContext, object retData) =>
                {
                    Pack_GetRecentFramesReponse reponse = retData as Pack_GetRecentFramesReponse;

                    var reponse_scanDatas = reponse.scanDatas;
                    if (reponse_scanDatas == null)
                    {

                    }
                    else
                    {
                        scanDatas.AddRange(reponse_scanDatas);
                        scanDatas.Sort((s1, s2) => s1.ID.CompareTo(s2.ID));
                        //TODO 把重复的删除

                        //删除超数量数据
                        if (scanDatas.Count > graphparam.Mix)
                            scanDatas.RemoveRange(0, scanDatas.Count - graphparam.Mix);
                    }

                    scanDatasChanged();

                    isDownloading = false;

                    //再次检测 是否需要下载

                    Download();
                }, this);
        }

        void scanDatasChanged() 
        {

            updateThicks();

            Mix = scanDatas.Count;

            //清除全部数据
            ClearValues();

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

            //更新Target
            UpdateY();

            UpdateX();


            UpdateValues();
        }

        protected void UpdateValues() 
        {
            if (Averages != null)
            {
                Values.AddRange(Averages);

                double[] uppers = new double[Averages.Count()];
                double[] lowers = new double[Averages.Count()];
                for (int i = 0; i < Averages.Count(); i++)
                {
                    if (!double.IsNaN(Averages[i]) && !double.IsNaN(Sigmas[i]))
                        uppers[i] = Averages[i] + Sigmas[i] * 2;
                    else
                        uppers[i] = Averages[i];

                    if (!double.IsNaN(Averages[i]) && !double.IsNaN(Sigmas[i]))
                        lowers[i] = Averages[i] - Sigmas[i] * 2;
                    else
                        lowers[i] = Averages[i];
                }

                UpperValues.AddRange(uppers);
                LowerValues.AddRange(lowers);
            }
        }

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