using FLY.Thick.Base.IService;
using FObjBase;
using LiveCharts;
using Misc;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FLY.Thick.Base.UI;
using FLY.Thick.Base.Common;

namespace FLY.Thick.Base.UI.UiModule
{
    public class FixGraphVm : INotifyPropertyChanged
    {
        #region 图表控制
        /// <summary>
        /// 图表Y轴最大值
        /// </summary>
        public double YMax { get; set; } = double.NaN;
        /// <summary>
        /// 图表Y轴最小值
        /// </summary>
        public double YMin { get; set; } = double.NaN;
        /// <summary>
        /// X轴坐标文字显示格式
        /// </summary>
        public Func<double, string> XFormatter { get; set; }
        /// <summary>
        /// Y轴坐标文字显示格式
        /// </summary>
        public Func<double, string> YFormatter { get; set; }
        /// <summary>
        /// 图表X轴最大值
        /// </summary>
        public double XMax { get; set; } = double.NaN;
        /// <summary>
        /// 图表X轴最小值
        /// </summary>
        public double XMin { get; set; } = double.NaN;
        /// <summary>
        /// 显示在chart上的数据
        /// </summary>
        public ChartValues<double> Values { get; } = new ChartValues<double>();
        #endregion

        #region 界面统计值
        /// <summary>
        /// 平均值
        /// </summary>
        public double Average { get; set; }
        /// <summary>
        /// 最大值
        /// </summary>
        public double Max { get; set; }
        /// <summary>
        /// 最小值
        /// </summary>
        public double Min { get; set; }
        /// <summary>
        /// 2sigma
        /// </summary>
        public double Sigma2 { get; set; }
        #endregion

        #region 状态
        /// <summary>
        /// 测试时间
        /// </summary>
        public DateTime Time { get; set; }
        /// <summary>
        /// 间隔
        /// </summary>
        public TimeSpan Interval { get; set; }
        /// <summary>
        /// 间隔,单位ms
        /// </summary>
        public double Intervalms { get; set; }
        /// <summary>
        /// 混合数
        /// </summary>
        public int Mix { get; set; }
        /// <summary>
        /// 暂停
        /// </summary>
        public bool IsPaused { get; set; }
        /// <summary>
        /// AD值模式
        /// </summary>
        public bool IsADMode { get; private set; }
        #endregion
        /// <summary>
        /// 真实接收的数据，与 Values 不一样， Values只是mDatas的一部分
        /// </summary>
        List<double> mDatas = new List<double>();

        /// <summary>
        /// 数据保存路径
        /// </summary>
        public string SavePath { get; set; }
        /// <summary>
        /// 自动Y轴中值时，Y轴中值的Value
        /// </summary>
        public double AutoTarget { get; set; }
        /// <summary>
        /// 多少个 mDatas 显示为一个 Values， 限定屏幕最大显示2000个数据
        /// </summary>
        int values_scale = 1;
        /// <summary>
        /// 限定屏幕最大显示2000个数据
        /// </summary>
        const int values_cap = 1000;

        IFixService fixService;
        FixGraphItemParam graphparam;
        DynArea dynArea;
        public FixGraphVm()
        {
            #region 与数据无关界面参数
            XFormatter = (x) =>
            {
                int index = (int)x;
                double s = Interval.TotalSeconds * index * values_scale;
                return $"{s:F1}s";
            };

            YFormatter = (y) => y.ToString("F1");

            #endregion
        }
        public void Init(IFixService fixService, DynArea dynArea, FixGraphItemParam bulkGraphItemFix)
        {
            this.dynArea = dynArea;
            this.fixService = fixService;
            graphparam = bulkGraphItemFix;

            Update4Graphparam();


            graphparam.PropertyChanged += Graphparam_PropertyChanged;
            this.fixService.PropertyChanged += FixService_PropertyChanged;
            this.PropertyChanged += FixGraphViewModel_PropertyChanged;
            this.dynArea.PropertyChanged += DynArea_PropertyChanged;

            IsPaused = this.dynArea.ControllerState != CTRL_STATE.FIX;
        }

        private void DynArea_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(DynArea.ControllerState)) {
                IsPaused = this.dynArea.ControllerState != CTRL_STATE.FIX;
            }
        }
        
        private void FixService_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(fixService.UpdateTime))
            {
                if (IsPaused)
                    return;

                //变化了
                //向服务获取数据
                fixService.GetFixDatas(fixService_GetFixDatas, this);
            }
        }

        private void FixGraphViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(AutoTarget))
            {
                updateAutoTarget();
            }
        }
        void updateAutoTarget()
        {
            if (!graphparam.IsAutoY && graphparam.IsAutoTarget)
            {
                if ((!double.IsNaN(YMax)) && (!double.IsNaN(YMin)))
                {
                    double mid = (YMax + YMin) / 2;
                    double range = (YMax - YMin) / 2;
                    double ymax2 = mid + range / 2;
                    double ymin2 = mid - range / 2;
                    if ((AutoTarget < ymax2) && (AutoTarget > ymin2))
                    {
                        if (Math.Abs(range - graphparam.YRange) < 0.05)
                        {
                            //还在范围内，不需要改变 Y轴范围
                            return;
                        }
                    }
                }
                YMax = AutoTarget + graphparam.YRange;
                YMin = AutoTarget - graphparam.YRange;
            }
        }
        private void Graphparam_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(graphparam.Interval))
            {
                UpdateInterval();
            }
            else if (e.PropertyName == nameof(graphparam.Mix))
            {
                UpdateMix();
            }
            else if (e.PropertyName == nameof(graphparam.Len))
            {
                UpdateX();
            }
            else if ((e.PropertyName == nameof(graphparam.IsAutoY)) ||
                (e.PropertyName == nameof(graphparam.IsAutoTarget)) ||
                (e.PropertyName == nameof(graphparam.YRange)) ||
                (e.PropertyName == nameof(graphparam.YTarget)))
            {
                UpdateY();
            }
            else if (e.PropertyName == nameof(graphparam.IsADMode))
            {
                UpdateIsADMode();
            }
        }
        void Update4Graphparam()
        {
            UpdateInterval();
            UpdateMix();

            UpdateX();
            UpdateY();

            UpdateIsADMode();
        }
        void UpdateX()
        {
            if (graphparam.Len > values_cap)
                values_scale = graphparam.Len / values_cap;
            else
                values_scale = 1;

            XMax = (int)(graphparam.Len / values_scale);//最后算出来 的数量肯定大于 values_cap,没关系，反正数据量少了
            XMin = 0;
        }

        void UpdateY()
        {
            if (graphparam.IsAutoY)
            {
                YMax = double.NaN;
                YMin = double.NaN;
            }
            else if (graphparam.IsAutoTarget)
            {
                updateAutoTarget();
                //YMax = AutoTarget + graphparam.YRange;
                //YMin = AutoTarget - graphparam.YRange;
            }
            else
            {
                YMax = graphparam.YTarget + graphparam.YRange;
                YMin = graphparam.YTarget - graphparam.YRange;
            }

        }
        void UpdateInterval()
        {
            TimeSpan interval;
            interval = TimeSpan.FromTicks((long)(graphparam.Interval * TimeSpan.TicksPerMillisecond));
            Interval = interval;
            Intervalms = ((double)(Interval.Ticks)) / TimeSpan.TicksPerMillisecond;
        }
        void UpdateMix()
        {
            Mix = graphparam.Mix;
        }

        void UpdateIsADMode()
        {
            IsADMode = graphparam.IsADMode;
            //清空全部数据
            Clear();

            UpdateStat();
        }

        /// <summary>
        /// 全部 mDatas 重新放入Values
        /// </summary>
        /// <param name="datas"></param>
        void RefreshValues(List<double> datas)
        {
            Values.Clear();


            //显示在屏幕的数据，只能有2000个
            if (values_scale <= 1)
            {
                foreach (var d in datas)
                    Values.Add(d);
            }
            else
            {
                data_show.Clear();
                foreach (var d in datas)
                {
                    data_show.Add(d);
                    if (data_show.Count >= values_scale)
                    {
                        Values.Add(data_show.Average());
                        data_show.Clear();
                    }
                }
            }
        }

        /// <summary>
        /// 增量数据，添加到 Values
        /// </summary>
        /// <param name="datas"></param>
        void RefreshValuesMore(List<double> datas)
        {
            //显示在屏幕的数据，只能有2000个

            if (values_scale <= 1)
            {

                foreach (var d in datas)
                    Values.Add(d);

                while (Values.Count > graphparam.Len)
                    Values.RemoveAt(0);
            }
            else
            {

                foreach (var d in datas)
                {
                    data_show.Add(d);
                    if (data_show.Count >= values_scale)
                    {
                        Values.Add(data_show.Average());
                        data_show.Clear();
                    }
                }

                //删除数据
                while (Values.Count > graphparam.Len / values_scale)
                    Values.RemoveAt(0);
            }
        }
        public void Clear()
        {

            mDatas.Clear();
            data_interval.Clear();
            data_mix.Clear();
            data_show.Clear();
            data_interval_totaltime = TimeSpan.Zero;
        }
        #region 用于间隔
        /// <summary>
        /// 显示数据用，mDatas->mPoints 的转换
        /// </summary>
        List<double> data_show = new List<double>();
        /// <summary>
        /// interval 时间内数据整合
        /// </summary>
        List<double> data_interval = new List<double>();
        /// <summary>
        /// data_interval 的总时间
        /// </summary>
        TimeSpan data_interval_totaltime = TimeSpan.Zero;
        /// <summary>
        /// 数据混合
        /// </summary>
        List<double> data_mix = new List<double>();
        #endregion

        void fixService_GetFixDatas(object asyncContext, object retData)
        {
            var reponse = retData as GetFixDatasReponse;
            //可以为null，需要判断
            if (reponse == null)
                return;

            //输入1.28ms 的数据， 输出 混合为mix，间隔为 interval 的数据
            List<double> data_out = new List<double>();

            for (int i = 0; i < reponse.thicks.Length; i++)
            {
                double v = (IsADMode) ? reponse.ADs[i] : reponse.thicks[i];
                data_interval.Add(v);
                data_interval_totaltime += reponse.ts;

                if (data_interval_totaltime >= Interval)
                {
                    double avg = data_interval.Average();
                    data_interval.Clear();
                    do
                    {
                        data_interval_totaltime -= Interval;
                        data_mix.Add(avg);
                    } while (data_interval_totaltime >= Interval);//当 reponse.ts = 1.28， Interval=1时，需要添加更多的数据

                    int len = data_mix.Count - graphparam.Mix;
                    if (len > 0)
                        data_mix.RemoveRange(0, len);
                    data_out.Add(data_mix.Average());
                }
            }

            if (data_out.Count() > 0)
            {
                Time = reponse.time;
                AddPoint(data_out);
            }
        }

        void UpdateStat()
        {
            if (mDatas.Count() == 0)
            {
                Max = double.NaN;
                Min = double.NaN;
                Average = double.NaN;
                Sigma2 = double.NaN;
                AutoTarget = double.NaN;
                return;
            }

            Max = mDatas.Max();
            Min = mDatas.Min();
            Average = mDatas.Average();
            Sigma2 = mDatas.Sigma() * 2;

            if (mDatas.Count > 100)
            {
                //最后100个数据的平均值
                AutoTarget = mDatas.Skip(mDatas.Count - 100).Average();
            }
            else
            {
                AutoTarget = Average;
            }
        }
        /// <summary>
        /// 1.28ms 经过重新 划分间隔， 混合后的数据，添加到 mDatas
        /// </summary>
        /// <param name="datas"></param>
        void AddPoint(List<double> datas)
        {
            mDatas.AddRange(datas);

            //删除数据
            if (mDatas.Count > graphparam.Len)
                mDatas.RemoveRange(0, mDatas.Count - graphparam.Len);

            //更新状态
            UpdateStat();

            //增量数据，添加到 Values
            RefreshValuesMore(datas);
        }

        public void PreSave()
        {
            string directory = System.IO.Path.GetDirectoryName(SavePath);
            if (string.IsNullOrEmpty(directory))
            {
                directory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            }
            string filename = $"{DateTime.Now:yyyyMMdd_HHmmss}.csv";
            SavePath = System.IO.Path.Combine(directory, filename);
        }

        AsyncCBHandler asyncDelegate;
        object asyncContext;
        /// <summary>
        /// 成功或失败，会调用 AsyncDelegate
        /// </summary>
        /// <param name="asyncDelegate"></param>
        /// <param name="AsyncState"></param>
        public void Save(AsyncCBHandler asyncDelegate, object asyncContext)
        {
            //需要异步！！！！
            if (!IsPaused)
            {
                this.asyncDelegate(this.asyncContext, false);
                return;
            }
            this.asyncDelegate = asyncDelegate;
            this.asyncContext = asyncContext;
            Task.Factory.StartNew(Output);
        }
        void Output()
        {
            string path = SavePath;
            try
            {
                using (StreamWriter sw = new StreamWriter(path, false, Encoding.GetEncoding("GB2312")))
                {
                    int ms = graphparam.Interval;
                    if (IsADMode)
                    {
                        sw.WriteLine("时间(ms),AD");
                        for (int i = 0; i < mDatas.Count(); i++)
                        {
                            double d = mDatas[i];
                            sw.WriteLine($"{(i * ms)},{d:F0}");
                        }
                    }
                    else
                    {
                        sw.WriteLine("时间(ms),数据");
                        for (int i = 0; i < mDatas.Count(); i++)
                        {
                            double d = mDatas[i];
                            sw.WriteLine($"{(i * ms)},{d:F2}");
                        }
                    }
                    sw.Flush();
                    sw.Close();
                }
                asyncDelegate(asyncContext, true);
            }
            catch (Exception e)
            {
                asyncDelegate(asyncContext, false);
            }
        }

        public void GetDatas(out double intervalms, out List<double> datas)
        {
            intervalms = Intervalms;
            datas = mDatas;
        }

        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
