using FLY.Thick.FilmCasting.Common;
using FLY.Thick.FilmCasting.IService;
using LiveCharts;
using LiveCharts.Configurations;
using System;
using System.Collections.Generic;
using System.ComponentModel;

using System.Linq;
using System.Windows.Media;
using System.Windows.Threading;

namespace FLY.Thick.FilmCasting.UI.UiModule
{
    public class TrendGraphVm : ITrendGraphVm
    {
        /// <summary>
        /// 自动切换为 最新视图模式 倒计时 初始值
        /// </summary>
        const int AutoKeepTotalSec = 60;
        protected const int Len = 100;
        /// <summary>
        /// Y轴%显示，最小的中值
        /// </summary>
        const int MIN_PERCENT_TARGET = 5;
        /// <summary>
        /// Y轴最小公差范围
        /// </summary>
        const double MIN_TOLERANCE = 0.01;

        public event PropertyChangedEventHandler PropertyChanged;

        #region 图表控制
        public double YMax { get; set; } = double.NaN;
        public double YMin { 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 Func<double, string> YFormatter { get; set; }
        public Func<double, string> XFormatter { get; set; }

        public Func<double, string> YFormatter2 { get; set; }
        #endregion

        #region 界面统计值
        /// <summary>
        /// 平均值
        /// </summary>
        public double Average { 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;

        /// <summary>
        /// 混合数
        /// </summary>
        public int Mix { get; protected set; } = 2;

        /// <summary>
        /// Values.First().Time
        /// </summary>
        public DateTime BeginTime { get; protected set; }
        /// <summary>
        /// Values.Last().Time
        /// </summary>
        public DateTime EndTime { get; protected set; }

        /// <summary>
        /// Values.Last().Id
        /// </summary>
        public long Id { get; protected set; } = -1;

        /// <summary>
        /// 最新的Id
        /// </summary>
        public long NewestId { get; protected set; } = -1;

        /// <summary>
        /// 查找时间
        /// </summary>
        public DateTime SearchTime { get; set; }

        #endregion

        #region 查询模式
        /// <summary>
        /// 只要 Number被改变,且不为0, AutoKeepNewestTimeRemaining就会被设为10s.
        /// 当 AutoKeepNewestTimeRemaining = 0, Number就会被设置为0
        /// </summary>
        public int AutoKeepNewestTimeRemaining { get; protected set; }

        /// <summary>
        /// 当前就是最新的视图
        /// </summary>
        public bool IsNewest { get; protected set; }

        /// <summary>
        /// 加载中
        /// </summary>
        public bool IsLoading { get; protected set; }
        #endregion

        protected List<Brush> AreaColors;

        public ChartValues<TrendValue> Values { get; } = new ChartValues<TrendValue>();

        public object MapperPositive { get; protected set; }
        public object MapperNegative { get; protected set; }
        public object MapperValue { get; protected set; }

        protected TrendGraphItemParam graphparam;
        IBulkDbService bulkDb;
        protected FilmCastingProfileParam profileParam;
        DispatcherTimer timer;
        public TrendGraphVm()
        {
            #region 与数据无关界面参数

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

            YFormatter = (y) =>
            {
                return $"{y,6:P1}";
            };

            YFormatter2 = (y) =>
            {
                return $"{y,6:F1}";
            };

            XFormatter = (x) =>
            {
                int index = (int)x;
                if (index < Values.Count() && index >= 0)
                    return Values[index].Time.ToString("HH:mm");
                else
                    return "";
            };
            MapperPositive = Mappers.Xy<TrendValue>()
                .X((value, index) => index)
                .Y(value => ToSigma2Percent(value))
                .Fill((value) => ToBrush(value));

            MapperNegative = Mappers.Xy<TrendValue>()
                .X((value, index) => index)
                .Y(value => -ToSigma2Percent(value))
                .Fill((value) => ToBrush(value));

            MapperValue = Mappers.Xy<TrendValue>()
                .X((value, index) => index)
                .Y(value => value.Value)
                .Stroke((value) =>
                {
                    if (value.ProfileID % 2 == 0)
                    {
                        return null;
                    }
                    else
                    {
                        return System.Windows.Media.Brushes.DarkBlue;
                    }
                });

            #endregion
        }
        Brush ToBrush(TrendValue value)
        {
            double percent = ToSigma2Percent(value);
            if (double.IsNaN(percent))
            {
                return AreaColors[0];
            }

            if ((percent > Tolerance2YMax) || (percent < -Tolerance2YMax))
            {
                return AreaColors[2];
            }
            else if ((percent > ToleranceYMax) || (percent < -ToleranceYMax))
            {
                return AreaColors[1];
            }
            else
            {
                return AreaColors[0];
            }
        }
        public virtual void Init(
            TrendGraphItemParam graphparam,
            IBulkDbService bulkDb,
            FilmCastingProfileParam profileParam)
        {
            this.graphparam = graphparam;
            this.bulkDb = bulkDb;
            this.profileParam = profileParam;


            Misc.BindingOperations.SetBinding(graphparam, nameof(graphparam.Mix), this, nameof(Mix));

            UpdateY();
            UpdateX();

            Values.Clear();
            Id = -1;
            IsNewest = true;
            
            Misc.BindingOperations.SetBinding(bulkDb, nameof(bulkDb.LastScanDataId), this, nameof(NewestId));

            ToNewest();

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

            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += (s, e) =>
            {
                if (IsNewest)
                    return;

                if (AutoKeepNewestTimeRemaining > 0)
                    AutoKeepNewestTimeRemaining--;
                if (AutoKeepNewestTimeRemaining <= 0)
                {
                    ToNewest();
                }
            };
            this.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == nameof(IsNewest))
                {
                    timer.IsEnabled = !IsNewest;
                }
            };
        }

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

        }

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



        private void Graphparam_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ((e.PropertyName == nameof(graphparam.Mix)) ||
                (e.PropertyName == nameof(graphparam.Interval)))
            {
                ToNewest();
            }
            else if (e.PropertyName == nameof(graphparam.YRangePercent))
            {
                UpdateY();
            }
        }
        protected void UpdateY()
        {
            double tolerance = this.profileParam.TolerancePercent;
            if (tolerance < MIN_TOLERANCE) {
                tolerance = MIN_TOLERANCE;
            }
            ToleranceYMax = tolerance;
            ToleranceYMin = -tolerance;
            Tolerance2YMax = tolerance * 2;
            Tolerance2YMin = -tolerance * 2;

            YMax = tolerance * graphparam.YRangePercent;
            YMin = -tolerance * graphparam.YRangePercent;
        }
        protected void UpdateX()
        {
            XMax = Len;
        }

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

        double ToSigma2Percent(TrendValue v)
        {
            if (double.IsNaN(v.Value))
            {
                return double.NaN;
            }
            else if (v.Value < MIN_PERCENT_TARGET)
            {
                return double.NaN;
            }
            else
            {
                return 2 * v.Sigma / v.Value;
            }
        }
        protected void UpdateAverage()
        {
            var sigma2s = this.Values.Select(v => ToSigma2Percent(v));

            var list = from v in sigma2s where !double.IsNaN(v) select v;

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

        }

        void Update4LastScanDataIdChanged()
        {
            if (!IsConnected)
                return;
            if (IsLoading)
                return;

            if (!IsNewest)
                return;

            int cnt;
            if (Id == -1)
            {
                cnt = Len;
                Values.Clear();
            }
            else
            {
                cnt = (int)(bulkDb.LastScanDataId - Id);
                cnt /= graphparam.Interval;

                if (cnt > Len)
                    cnt = Len;
            }

            if (cnt == 0)
                return;

            IsLoading = true;
            bulkDb.GetTrend(
                new Pack_GetTrendRequest()
                {
                    Id = 0,
                    Mix = graphparam.Mix,
                    Interval = graphparam.Interval,
                    Count = cnt
                },
                GetTrendReponse, this);
        }
        void GetTrendReponse(object asyncContext, object retData)
        {
            IsLoading = false;
            Pack_GetTrendReponse reponse = retData as Pack_GetTrendReponse;

            //向Values 后面添加数据
            if (reponse.Values != null && reponse.Values.Count > 0)
            {
                //从尾向前排的，需要先反转！！！！
                reponse.Values.Reverse();

                bool isAdd = true;
                if (Values.Count() > 0)
                {

                    if (reponse.Values.First().Time > Values.Last().Time)
                    {
                        //Values 的后面接
                    }
                    else if (reponse.Values.Last().Time < Values.First().Time)
                    {
                        //Values 的前面接
                        isAdd = false;
                    }
                    else
                    {
                        //不能接上
                        Values.Clear();
                    }
                }

                if (isAdd)
                {
                    Values.AddRange(reponse.Values);

                    //限制Values长度
                    int remove_cnt = Values.Count() - Len;
                    for (int i = 0; i < remove_cnt; i++)
                        Values.RemoveAt(0);
                }
                else 
                {
                    //Values 的前面接
                    Values.InsertRange(0, reponse.Values);

                    //限制Values长度
                    int remove_cnt = Values.Count() - Len;
                    for (int i = 0; i < remove_cnt; i++)
                        Values.RemoveAt(Values.Count() - 1);
                }



                Id = Values.Last().ScanDataID;
                SearchTime = Values.Last().Time;

                BeginTime = Values.First().Time;
                EndTime = Values.Last().Time;

                long newestId = graphparam.Interval * (bulkDb.LastScanDataId / graphparam.Interval);
                if (Id == newestId)
                    IsNewest = true;
                else
                    IsNewest = false;

                UpdateAverage();
            }
            else
            {
                long newestId = graphparam.Interval * (bulkDb.LastScanDataId / graphparam.Interval);
                if (Id == newestId)
                    IsNewest = true;
                else
                    IsNewest = false;
            }


        }
        public void Search(long lastId)
        {
            if (lastId <= 0)
                return;

            if (!IsConnected)
                return;
            if (IsLoading)
                return;
            AutoKeepNewestTimeRemaining = AutoKeepTotalSec;
            IsNewest = false;
            IsLoading = true;

            //TODO,计算，是否有重叠的数据
            bulkDb.GetTrend(
                new Pack_GetTrendRequest()
                {
                    Id = lastId,
                    Mix = graphparam.Mix,
                    Interval = graphparam.Interval,
                    Count = Len
                },
                GetTrendReponse, this);
        }
        bool IsConnected
        {
            get
            {
                return (bulkDb as FObjBase.FObjServiceClient).IsConnected;
            }
        }
        public void Search(DateTime lastTime)
        {
            if (!IsConnected)
                return;
            if (IsLoading)
                return;
            AutoKeepNewestTimeRemaining = AutoKeepTotalSec;
            IsNewest = false;
            IsLoading = true;
            //TODO,计算，是否有重叠的数据
            bulkDb.GetTrend(
                new Pack_GetTrendRequest()
                {
                    Mix = graphparam.Mix,
                    Interval = graphparam.Interval,
                    Count = Len,
                    IsSearchByTime = true,
                    Time = lastTime
                },
                GetTrendReponse, this);
        }

        public void PreView()
        {
            long lastId = Id - Values.Count() * graphparam.Interval;
            Search(lastId);
        }

        public void NextView()
        {

            Search(Id + Len * graphparam.Interval);
        }

        public void ToNewest()
        {
            if (!IsConnected)
                return;
            if (IsLoading)
                return;

            IsNewest = true;
            IsLoading = true;
            bulkDb.GetTrend(
                new Pack_GetTrendRequest()
                {
                    Id = 0,
                    Mix = graphparam.Mix,
                    Interval = graphparam.Interval,
                    Count = Len
                },
                GetTrendReponse, this);
        }

        /// <summary>
        /// 最后一卷数据
        /// </summary>
        public void LastView() 
        {
            if (!IsConnected)
                return;

            if (IsLoading)
                return;

            if (Values.Count == 0)
                return;

            var profileId = Values.Last().ProfileID;
            var values = Values.ToList();
            int startIndex = values.Count() - 1;
            for (int i = 1; i < values.Count(); i++) 
            {
                int index = values.Count() - 1 - i;
                if (values[index].ProfileID != profileId) 
                {
                    //就是它的后一个
                    break;
                }
                startIndex = index;
            }

            values.RemoveRange(0, startIndex);
            Values.Clear();
            Values.AddRange(values);
        }


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

    public interface ITrendGraphVm : INotifyPropertyChanged
    {

        #region 查询模式

        /// <summary>
        /// 自动变为显示最新数据，的剩余秒数
        /// </summary>
        int AutoKeepNewestTimeRemaining { get; }

        /// <summary>
        /// 当前就是最新的视图
        /// </summary>
        bool IsNewest { get; }

        /// <summary>
        /// 加载中
        /// </summary>
        bool IsLoading { get; }
        #endregion

        /// <summary>
        /// 显示最新数据
        /// </summary>
        void ToNewest();

        /// <summary>
        /// WHERE ID 小于 lastId AND ID % graphparam.Interval == 0 ORDER BY ID DESC LIMIT graphparam.Len
        /// </summary>
        /// <param name="lastId"></param>
        void Search(long lastId);

        /// <summary>
        /// WHERE Time 小于 lastTime AND ID % graphparam.Interval == 0 ORDER BY ID DESC LIMIT graphparam.Len
        /// </summary>
        /// <param name="lastId"></param>
        void Search(DateTime lastTime);

        /// <summary>
        /// 下一页
        /// </summary>
        void PreView();

        /// <summary>
        /// 上一页
        /// </summary>
        void NextView();

        /// <summary>
        /// 最后一卷数据
        /// </summary>
        void LastView();
    }

}
