using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using Aitex.Core.Utilities;
using MECF.Framework.Common.DataCenter;
using MECF.Framework.UI.Client.CenterViews.Core;
using MECF.Framework.UI.Client.CenterViews.Core.Charting;
using MECF.Framework.UI.Client.CenterViews.Core.EventArgs;
using SciChart.Charting.Visuals.Axes;
using Sicentury.Core.EventArgs;
using Sicentury.Core.Tree;

namespace MECF.Framework.UI.Client.CenterViews.Operations.RealTime
{
    public sealed class RealtimeViewModel : BusyIndicateableUiViewModelBase
    {
        #region Variables

        /// <summary>
        /// 允许监控的最大曲线数量
        /// </summary>
        private const int MAX_PARAMETERS_ALLOWED = 200;

        private readonly RealtimeProvider _provider = new RealtimeProvider();

        private bool _enableAutoZoom = true;
        private int _pointCount;
        private readonly PeriodicJob _thread;

        /// <summary>
        /// Monitor中添加数据点时直接向DataSeries对象添加，已实现跨线程操作。
        /// </summary>
        private readonly List<SicFastLineDataSeries> _dataSeriesList;

        /// <summary>
        /// 更新报告导出信息。
        /// </summary>
        private readonly IProgress<ProgressUpdatingEventArgs> _exportingProgressReporter;

        #endregion

        #region Constructors

        public RealtimeViewModel()
        {
            DisplayName = "Realtime";

            SelectedData = new ChartingLineSeriesCollection(DisplayName);

            ParameterNodes = new TreeNode(DisplayName)
            {
                MaxTerminalSelectionAllowed = MAX_PARAMETERS_ALLOWED
            };

            ParameterNodes.ChildNodes.AddRange(_provider.GetParameters());
            ParameterNodes.TerminalNodeSelectionChanged += OnNodeSelectionChanged;

            IntervalSaved = true;
            TrendInterval = 500;
            TimeSpanSaved = true;
            TrendTimeSpan = 60 * 5;

            _thread = new PeriodicJob(TrendInterval, MonitorData, "RealTime", true);
            _pointCount = Math.Max(TrendTimeSpan * 1000 / TrendInterval, 10);

            _dataSeriesList = new List<SicFastLineDataSeries>();

            _exportingProgressReporter = new Progress<ProgressUpdatingEventArgs>(e =>
            {
                BusyIndicatorContent = e.Message;
            });
        }

        #endregion

        #region Properties

        public TreeNode ParameterNodes { get; }

        public ChartingLineSeriesCollection SelectedData { get; set; }


        public AutoRange ChartAutoRange => EnableAutoZoom ? AutoRange.Always : AutoRange.Never;

        public bool EnableAutoZoom
        {
            get => _enableAutoZoom;

            set
            {
                _enableAutoZoom = value;
                NotifyOfPropertyChange(nameof(EnableAutoZoom));
                NotifyOfPropertyChange(nameof(ChartAutoRange));
            }
        }

        [IgnorePropertyChange]
        public int TrendInterval { get; set; }

        public bool IntervalSaved { get; set; }

        [IgnorePropertyChange]
        public int TrendTimeSpan { get; set; }

        public bool TimeSpanSaved { get; set; }

        #endregion

        #region Methods

        protected bool MonitorData()
        {
            try
            {
                Dictionary<string, object> data = null;
                if (SelectedData.Count > 0)
                {
                    data = QueryDataClient.Instance.Service.PollData(Array.ConvertAll(SelectedData.ToArray(), x => (x as SicFastLineSeries).DataName));
                }

                AppendData(data);

                for (var j = 0; j < ParameterNodes.ChildNodes.Count; j++)
                {
                    var par = ParameterNodes.ChildNodes[j];
                    par.IsVisibilityParentNode = Visibility.Hidden;
                }
            }
            catch (Exception ex)
            {
                LOG.Error(ex.Message);
            }

            return true;
        }

        public void AppendData(Dictionary<string, object> data)
        {
            if (data == null)
                return;

            var dt = DateTime.Now;

            foreach (var ds in _dataSeriesList)
            {
                if (ds == null || !data.ContainsKey(ds.SeriesName))
                    continue;

                if (data[ds.SeriesName] is bool)
                {
                    ds.Append(dt, ((bool)data[ds.SeriesName] ? 1 : 0) * ds.Factor + ds.Offset, new ParameterNodePoint(dt, ((bool)data[ds.SeriesName] ? 1 : 0)));
                    continue;
                }

                if (!double.TryParse(data[ds.SeriesName].ToString(), out var value))
                    continue;

                ds.Append(dt, value * ds.Factor + ds.Offset, new ParameterNodePoint(dt, value));
            }
        }

        private void UpdateFifoCapacity(int capacity)
        {
            foreach (var renderableSeries in SelectedData)
            {
                if (renderableSeries is SicFastLineSeries line)
                {
                    var ds = line.GetDataSeries();
                    var copyMetaData = ds.Metadata.Where(x => x != null && ((ParameterNodePoint)x).Time > DateTime.Now.AddSeconds(-TrendTimeSpan)).ToList();

                    ds.FifoCapacity = capacity;
                    foreach (ParameterNodePoint item in copyMetaData)
                    {
                        ds.Append(item.Time, item.Value);
                    }
                }
            }
        }

        public void SetInterval()
        {
            _thread.ChangeInterval(TrendInterval);
            _pointCount = Math.Max(10, TrendTimeSpan * 1000 / TrendInterval);
            IntervalSaved = true;
            NotifyOfPropertyChange(nameof(IntervalSaved));

            UpdateFifoCapacity(_pointCount);
        }

        public void SetTimeSpan()
        {
            _pointCount = Math.Max(10, TrendTimeSpan * 1000 / TrendInterval);
            TimeSpanSaved = true;
            NotifyOfPropertyChange(nameof(TimeSpanSaved));

            UpdateFifoCapacity(_pointCount);
        }

        #endregion

        #region Events

        public void OnNodeSelectionChanged(object sender, TreeNodeSelectionChangedEventArgs e)
        {
            if (e.NewValue == true && SelectedData.Count >= MAX_PARAMETERS_ALLOWED)
            {
                e.Cancel = true;
                return;
            }

            var line = SelectedData.Cast<SicFastLineSeries>().FirstOrDefault(x => x.DataName == e.Source.FullName);
            switch (e.NewValue)
            {
                case false:
                    {
                        if (line != null)
                        {
                            _dataSeriesList.Remove(line.GetDataSeries());
                            SelectedData.Remove(line);
                        }

                        break;
                    }

                case true:
                    {
                        if (line == null)
                        {
                            var series = new SicFastLineSeries(e.Source.FullName)
                            {
                                BackendParameterNode = e.Source
                            };
                            series.GetDataSeries().FifoCapacity = _pointCount;

                            SelectedData.Add(series);
                            _dataSeriesList.Add(series.GetDataSeries());
                            SelectedData.ResetColors();
                        }

                        break;
                    }

                default:
                    break;
            }
        }

        public void Deleted(object sender, RenderableSeriesDeletingEventArgs e)
        {
            var list = e.Source.Cast<SicFastLineSeries>().ToList();

            //var total = SelectedData.Count;
            for (var i = list.Count - 1; i >= 0; i--)
            {
                // 清除左侧Tree中的选中状态
                list[i].BackendParameterNode.IsSelected = false;
                _dataSeriesList.Remove(list[i].GetDataSeries());
            }

            ((RealtimeView)View).tvParameterNodes.ClearPresetGroupSelectionOnly();
        }

        public void Exporting(object sender, EventArgs e)
        {
            BusyIndicatorContent = "Exporting ...";
            IsBusy = true;
        }

        public void Exported(object sender, EventArgs e)
        {
            IsBusy = false;
        }

        public void ProgressUpdating(object sender, ProgressUpdatingEventArgs e)
        {
            _exportingProgressReporter.Report(e);
        }

        public override void Cancel()
        {
            if (View is RealtimeView view)
            {
                view.dataGrid.CancelOperation();
            }
        }

        #endregion
    }
}
