using FLY.OBJComponents.Client;
using FLY.OBJComponents.IService;
using FLY.Weight2.Client;
using FLY.Weight2.IService;
using FLY.Weight2.Server.Model;
using LiveCharts;
using LiveCharts.Configurations;
using LiveCharts.Wpf;
using MultiLayout;
using MultiLayout.UiModule;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Navigation;
using System.Windows.Threading;
using Unity;

namespace FLY.Weight2.UI.Client.UiModule
{
    /// <summary>
    /// FlowGraph.xaml 的交互逻辑
    /// </summary>
    public partial class FlowGraph : UserControl
    {
        IUnityContainer container;
        IWeightSystemService weightSystemService;
        FlowGraphVm viewModel;
        public FlowGraph()
        {
            InitializeComponent();
        }

        [InjectionMethod]
        public void Init(
             IUnityContainer container,
              IWeightSystemService weightSystemService,
              IBulkDbFlowService bulkDb)
        {
            this.container = container;
            this.weightSystemService = weightSystemService;
            viewModel = new FlowGraphVm();
            viewModel.Init(weightSystemService, bulkDb);

            DataContext = viewModel;
        }

        private void btnInfoClick(object sender, RoutedEventArgs e)
        {
            WdGraphSet w = new WdGraphSet();
            w.Init(viewModel);
            w.Owner = FLY.ControlLibrary.COMMON.GetWindow(this);
            w.ShowDialog();
        }

        private void btnTableClick(object sender, RoutedEventArgs e)
        {
            PgFlowTable p = new PgFlowTable();
            container.BuildUp(p);

            MultiLayout.FlyLayoutManager.NavigationService.Navigate(p);
        }

        private void btnSelectClick(object sender, RoutedEventArgs e)
        {
            viewModel.SelectABC(((Button)sender).Tag as FlowGraphVm_Item);
        }

        private void btnSelectAllClick(object sender, RoutedEventArgs e)
        {
            viewModel.SelectAll();
        }
    }
    public class FlowGraphVm : INotifyPropertyChanged
    {
        const int MARKNO_UPDATE_Y = 0;

        /// <summary>
        /// 画面只显示200个数据, 就算设置画面设置显示1000个数,也会整合为200个数据
        /// </summary>
        const int Len = 200;
        /// <summary>
        /// 自动按保持最新值,总时间
        /// </summary>
        const int AutoKeepTotalSec = 60;

        public event PropertyChangedEventHandler PropertyChanged;

        #region 图表控制
        /// <summary>
        /// 曲线值转换器
        /// </summary>
        public CartesianMapper<Lc_Flow> Mapper { get; set; }
        /// <summary>
        /// 曲线颜色
        /// </summary>
        public Brush Stroke { get; set; }

        /// <summary>
        /// X轴格式
        /// </summary>
        public Func<double, string> XFormatter { get; set; }

        /// <summary>
        /// y轴格式
        /// </summary>
        public Func<double, string> YFormatter { get; set; }

        /// <summary>
        /// Y轴中值
        /// </summary>
        public double YTarget { get; set; }

        /// <summary>
        /// 最大值
        /// </summary>
        public double YMax { get; set; } = double.NaN;
        /// <summary>
        /// 最小值
        /// </summary>
        public double YMin { get; set; } = double.NaN;

        public double XMax { get; set; } = double.NaN;

        #endregion

        #region property


        public ChartValues<Lc_Flow> Values { get; private set; } = new ChartValues<Lc_Flow>();


        /// <summary>
        /// 开始时间
        /// </summary>
        public DateTime BeginTime { get; set; }

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

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

        public ObservableCollection<FlowGraphVm_Item> Items { get; } = new ObservableCollection<FlowGraphVm_Item>();
        /// <summary>
        /// 总流量
        /// </summary>
        public double TotalFlow { get; set; }
        #endregion


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

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

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


        /// <summary>
        /// 当前现在不是最新,而且没有移动动作,20秒自动按 Newest()
        /// </summary>
        DispatcherTimer timer;


        IWeightSystemService weightSystemService;
        FlowGraphParams graphparam;
        IBulkDbFlowService bulkDb;



        public FlowGraphVm()
        {
            #region 与数据无关界面参数
            XFormatter = (x) =>
            {
                int index = (int)x;
                if (index < Values.Count() && index >= 0)
                    return Values[index].Time.ToString("HH:mm");
                else
                    return "??:??";
            };

            YFormatter = (y) =>
            {
                return $"{y,6:F1}";
            };
            #endregion

        }


        public void Init(
            IWeightSystemService weightSystemService,
            IBulkDbFlowService bulkDb
            )
        {
            graphparam = FlowGraphParams.Current;
            this.weightSystemService = weightSystemService;
            this.bulkDb = bulkDb;

            Values.Clear();
            Id = -1;
            IsNewest = true;

            ToNewest();

            //根据 weightSystemClient.Item创建Series


            BindingSeries();
            SelectAll();


            UpdateY();
            UpdateX();
            if (this.weightSystemService is WeightSystemServiceClient)
            {
                (this.weightSystemService as WeightSystemServiceClient).ResetItemsEvent += BindingSeries;
            }
            bulkDb.PropertyChanged += BulkDb_PropertyChanged;
            this.PropertyChanged += FlowGraphVm_PropertyChanged;
            graphparam.PropertyChanged += Graphparam_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 BulkDb_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(bulkDb.LastId))
            {
                Update4LastIdChanged();
            }
            else if (e.PropertyName == nameof(FObjBase.FObjServiceClient.IsConnected))
            {
                if ((bulkDb as FObjBase.FObjServiceClient).IsConnected)
                {
                    Update4LastIdChanged();
                }
                else
                {
                    IsLoading = false;
                }
            }
        }

        private void Graphparam_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ((e.PropertyName == nameof(graphparam.IsYRangeAuto)) ||
            (e.PropertyName == nameof(graphparam.IsYRangePercent)) ||
            (e.PropertyName == nameof(graphparam.YRangePercent)) ||
            (e.PropertyName == nameof(graphparam.YRange)))
            {
                UpdateY();
            }
            else if (e.PropertyName == nameof(graphparam.Interval))
            {
                ToNewest();
            }
        }

        private void FlowGraphVm_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(YTarget))
                UpdateY();
        }




        void BindingSeries()
        {

            if (Items.Count != weightSystemService.Items.Count)
            {
                Items.Clear();
                for (int i = 0; i < weightSystemService.Items.Count; i++)
                {
                    FlowGraphVm_Item item = new FlowGraphVm_Item
                    {
                        Title = weightSystemService.Items[i].Number,
                        Fill = weightSystemService.Items[i].Color,
                        Visibility = Visibility.Hidden,
                        Index = i
                    };
                    Items.Add(item);
                }
                UpdateYTarget();
            }

        }

        void UpdateY()
        {
            if (graphparam.IsYRangeAuto)
            {
                YMax = double.NaN;
                YMin = double.NaN;
            }
            else if (graphparam.IsYRangePercent)
            {
                double range = YTarget * graphparam.YRangePercent;
                if (range <= 0.1)
                {
                    YMax = double.NaN;
                    YMin = double.NaN;
                }
                else
                {
                    YMax = YTarget + range;
                    YMin = YTarget - range;
                }
            }
            else
            {
                double range = graphparam.YRange;
                if (range <= 0.1)
                {
                    YMax = double.NaN;
                    YMin = double.NaN;
                }
                else
                {
                    YMax = YTarget + range;
                    YMin = YTarget - range;
                }
            }
        }
        void UpdateX()
        {
            XMax = Len - 1;
        }
        void UpdateYTarget()
        {
            FObjBase.PollModule.Current.Poll_JustOnce(() =>
            {
                if (Values.Count() == 0)
                    return;

                Lc_Flow flows = Values.Last();

                for (int i = 0; i < Items.Count() && i < flows.Items.Count(); i++)
                {
                    Items[i].Value = flows.Items[i].Flow;
                }
                TotalFlow = flows.Total;

                double target = TotalFlow;

                try
                {
                    FlowGraphVm_Item it = Items.First((i) => i.Visibility == Visibility.Visible);
                    if (it != null)
                        target = it.Value;
                }
                catch
                {

                }

                YTarget = Math.Round(target);
            }, this, MARKNO_UPDATE_Y);
        }
        void Update4LastIdChanged()
        {
            if (!IsConnected)
                return;
            if (IsLoading)
                return;

            if (!IsNewest)
                return;

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

                if (cnt > Len)
                    cnt = Len;
            }

            if (cnt == 0)
                return;

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

            //向Values 后面添加数据
            if (reponse.Values != null && reponse.Values.Count > 0)
            {
                //从尾向前排的,需要反转!!!!
                reponse.Values.Reverse();
                if (Values.Count() > 0)
                {
                    if (reponse.Values.First().Time <= Values.Last().Time)
                    {
                        //不能接上
                        Values.Clear();
                    }
                }
                Values.AddRange(reponse.Values);
                //限制Values长度
                int remove_cnt = Values.Count() - Len;
                for (int i = 0; i < remove_cnt; i++)
                    Values.RemoveAt(0);


                Id = reponse.Values.Last().ID;

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

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

                UpdateYTarget();
            }
            else
            {
                long newestId = graphparam.Interval * (bulkDb.LastId / 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,
                    Interval = graphparam.Interval,
                    Count = Len
                },
                GetTrendReponse, this);
        }
        bool IsConnected
        {
            get
            {
                return (bulkDb as FObjBase.FObjServiceClient).IsConnected;
            }
        }
        public void Seatch(DateTime lastTime)
        {
            if (!IsConnected)
                return;
            if (IsLoading)
                return;
            AutoKeepNewestTimeRemaining = AutoKeepTotalSec;
            IsNewest = false;
            IsLoading = true;
            //TODO,计算,是否有重叠的数据
            bulkDb.GetTrend(
                new Pack_GetTrendRequest()
                {
                    Interval = graphparam.Interval,
                    Count = Len,
                    IsSearchByTime = true,
                    Time = lastTime
                },
                GetTrendReponse, this);
        }
        public void PreView()
        {
            Search(Id - Len * graphparam.Interval);
        }

        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,
                    Interval = graphparam.Interval,
                    Count = Len
                },
                GetTrendReponse, this);
        }


        class GetMapperOfFlyData_Flow
        {
            int Index = 0;
            public GetMapperOfFlyData_Flow(int index)
            {
                Index = index;
            }
            public CartesianMapper<Lc_Flow> GetCartesianMapper()
            {
                return Mappers.Xy<Lc_Flow>()
                    .X((value, index) =>
                    {
                        return index;
                    })
                    .Y(value =>
                    {
                        if (Index < value.Items.Count())
                            return value.Items[Index].Flow;
                        else
                            return double.NaN;
                    });
            }
            public static CartesianMapper<Lc_Flow> ConfigurationAll = Mappers.Xy<Lc_Flow>()
                .X((value, index) =>
                {
                    return index;
                })
                .Y(value =>
                {
                    return value.Total;
                });
        }

        public void SelectAll()
        {
            foreach (FlowGraphVm_Item i in Items)
            {
                i.Visibility = Visibility.Hidden;
            }
            Stroke = new SolidColorBrush(Colors.Black);
            Mapper = GetMapperOfFlyData_Flow.ConfigurationAll;

            UpdateYTarget();
        }
        public void SelectABC(FlowGraphVm_Item item)
        {
            foreach (FlowGraphVm_Item i in Items)
            {
                if (i != item)
                {
                    i.Visibility = Visibility.Hidden;
                }
                else
                {
                    i.Visibility = Visibility.Visible;
                }
            }
            Stroke = item.Fill;
            Mapper = new GetMapperOfFlyData_Flow(item.Index).GetCartesianMapper();

            UpdateYTarget();
        }
    }

    public class FlowGraphVmUt : INotifyPropertyChanged
    {

        #region 图表控制
        /// <summary>
        /// 曲线值转换器
        /// </summary>
        public CartesianMapper<Lc_Flow> Mapper { get; set; }
        /// <summary>
        /// 曲线颜色
        /// </summary>
        public Brush Stroke { get; set; }

        /// <summary>
        /// X轴格式
        /// </summary>
        public Func<double, string> XFormatter { get; set; }

        /// <summary>
        /// y轴格式
        /// </summary>
        public Func<double, string> YFormatter { get; set; }

        /// <summary>
        /// Y轴中值
        /// </summary>
        public double YTarget { get; set; }

        /// <summary>
        /// 最大值
        /// </summary>
        public double YMax { get; set; } = double.NaN;
        /// <summary>
        /// 最小值
        /// </summary>
        public double YMin { get; set; } = double.NaN;

        public double XMax { get; set; } = double.NaN;

        #endregion

        #region property


        public ChartValues<Lc_Flow> Values { get; private set; } = new ChartValues<Lc_Flow>();


        /// <summary>
        /// 开始时间
        /// </summary>
        public DateTime BeginTime { get; set; }

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

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

        public List<FlowGraphVm_Item> Items { get; } = new List<FlowGraphVm_Item>();
        /// <summary>
        /// 总流量
        /// </summary>
        public double TotalFlow { get; set; }
        #endregion


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

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

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


        public FlowGraphVmUt()
        {


            #region 与数据无关界面参数
            XFormatter = (x) =>
            {
                int index = (int)x;
                if (index < Values.Count() && index >= 0)
                    return Values[index].Time.ToString("HH:mm");
                else
                    return "??:??";
            };

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

            Mapper = Mappers.Xy<Lc_Flow>()
                .X((value, index) =>
                {
                    return index;
                })
                .Y(value =>
                {
                    return value.Total;
                });
            #endregion


            Items = new List<FlowGraphVm_Item>();
            Items.Add(new FlowGraphVm_Item() { Title = "A", Value = 103.1, Visibility = Visibility.Hidden });
            Items.Add(new FlowGraphVm_Item() { Title = "B", Value = 10, Visibility = Visibility.Visible });
            Items.Add(new FlowGraphVm_Item() { Title = "C", Value = 23, Visibility = Visibility.Hidden });
            Items.Add(new FlowGraphVm_Item() { Title = "D", Value = 46.7, Visibility = Visibility.Hidden });
            Items.Add(new FlowGraphVm_Item() { Title = "E", Value = 80, Visibility = Visibility.Hidden });
            for (int i = 0; i < Items.Count(); i++)
            {
                FlowGraphVm_Item s = Items[i];
                s.Fill = WeighterColorDB.GetSelf(i);
            }

            TotalFlow = Items.Sum(i => i.Value);
            double yRangePercent = 0.3;
            XMax = 200;

            YTarget = TotalFlow;

            YMax = YTarget + YTarget * yRangePercent;
            YMax = YTarget - YTarget * yRangePercent;
            DateTime startTime = DateTime.Now;
            Random random = new Random();
            for (int j = 0; j < 200; j++)
            {
                Lc_Flow lc_Flow = new Lc_Flow();
                lc_Flow.ID = j;
                lc_Flow.Time = startTime + TimeSpan.FromSeconds(j * 10);
                lc_Flow.Items = new Lc_FlowItem[Items.Count()];
                for (int i = 0; i < Items.Count(); i++)
                {
                    lc_Flow.Items[i] = new Lc_FlowItem()
                    {
                        Flow = Items[i].Value * (1 + (random.NextDouble() - 0.5) * 0.10),
                    };
                }

                lc_Flow.Total = lc_Flow.Items.Sum(item => item.Flow);
                Values.Add(lc_Flow);
            }

            BeginTime = Values.First().Time;
            EndTime = Values.Last().Time;
            Id = Values.Last().ID;
            IsNewest = true;
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
    public class FlowGraphVm_Item : INotifyPropertyChanged
    {

        public string Title { get; set; }
        /// <summary>
        /// 颜色
        /// </summary>
        public Brush Fill { get; set; }
        /// <summary>
        /// 显示
        /// </summary>
        public Visibility Visibility { get; set; }
        /// <summary>
        /// 值
        /// </summary>
        public double Value { get; set; }

        public int Index { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;
    }
    public class FlowGraphParams : INotifyPropertyChanged
    {
        static FlowGraphParams current = null;
        public static FlowGraphParams Current
        {
            get
            {
                if (current == null)
                {
                    current = new FlowGraphParams();
                    current.Load();
                }
                return current;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 数据间隔
        /// </summary>
        public int Interval { get; set; } = 6;

        /// <summary>
        /// Y轴 自动设置 MaxValue, MinValue, 
        /// </summary>
        public bool IsYRangeAuto { get; set; } = true;

        /// <summary>
        /// /Y轴以% 范围显示
        /// </summary>
        public bool IsYRangePercent { get; set; } = true;

        /// <summary>
        /// Y轴显示范围 %
        /// MaxValue = YTarget*(1+YRangePercent)
        /// MinValue = YTarget*(1-YRangePercent)
        /// </summary>
        public double YRangePercent { get; set; } = 0.15;


        /// <summary>
        /// Y轴显示范围, 中间值一定是 YTarget
        /// MaxValue = YTarget+YRange
        /// MinValue = YTarget-YRange
        /// </summary>
        public int YRange { get; set; } = 20;

        string FilePath;
        public FlowGraphParams()
        {
            string path = System.IO.Path.Combine(FlyLayout.BasePath, "flowgraph.json");
            FilePath = path;
        }
        /// <summary>
        /// 保存
        /// </summary>
        /// <returns></returns>
        public bool Save()
        {
            try
            {
                string json = Newtonsoft.Json.JsonConvert.SerializeObject(this);

                File.WriteAllText(FilePath, json);
            }
            catch
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 读取
        /// </summary>
        /// <returns></returns>
        bool Load()
        {
            try
            {
                string json = File.ReadAllText(FilePath);
                Newtonsoft.Json.JsonConvert.PopulateObject(json, this);
                return true;
            }
            catch
            {
                return false;
            }
        }
    }

    public class UiModule2_FlowGraph : MultiLayout.UiModule.IUiModule2
    {
        /// <summary>
        /// 控件标题
        /// 它的值取决于culture
        /// </summary>
        public string Title=> "单组称重.流量图";

        public ComponentType Type => ComponentType.Graph;
        public bool IsUnique => false;

        /// <summary>
        /// 控件
        /// 创建时,需要给它唯一ID,让加载自己的数据
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public FrameworkElement GetComponent(int id, IUnityContainer container)
        {
            FlowGraph graph = new FlowGraph();
            container.BuildUp(graph);
            return graph;
        }

        /// <summary>
        /// 控件缩略图,用于编辑界面时,大致看看
        /// 创建时,需要给它唯一ID,让加载自己的数据
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public FrameworkElement GetThumbnail()
        {
            return new System.Windows.Controls.Grid();
        }


        /// <summary>
        /// 给出全部控件ID, 控件自行删除没有的参数
        /// </summary>
        /// <param name="IDs"></param>
        public void MatchParam(int[] IDs)
        {
        }
    }
}