using FLY.Integrated.Common;
using FLY.Integrated.IService;
using FLY.Integrated.Server.Model;
using FLY.OBJComponents.Client;
using FLY.OBJComponents.IService;
using GalaSoft.MvvmLight.Command;
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.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Unity;

namespace FLY.Integrated.UI.Client.UiModule
{
    /// <summary>
    /// IBCCtrlGraph.xaml 的交互逻辑
    /// </summary>
    public partial class IbcCtrlGraph : UserControl
    {
        IbcCtrlGraphVm viewModel;
        public IbcCtrlGraph()
        {
            InitializeComponent();
            InitialzeChart();
        }
        void InitialzeChart() 
        {
            chart.AxisY[0].SetBinding(Axis.MaxValueProperty, new Binding("D_MaxValue"));
            chart.AxisY[0].SetBinding(Axis.MinValueProperty, new Binding("D_MinValue"));
            chart.AxisY[0].SetBinding(Axis.LabelFormatterProperty, new Binding("YFormatter"));


            chart.AxisY[1].SetBinding(Axis.MaxValueProperty, new Binding("FilmWidth_MaxValue"));
            chart.AxisY[1].SetBinding(Axis.MinValueProperty, new Binding("FilmWidth_MinValue"));
            chart.AxisY[1].SetBinding(Axis.LabelFormatterProperty, new Binding("YFormatter"));

            chart.AxisX[0].SetBinding(Axis.LabelFormatterProperty, new Binding("DateTimeFormatter"));
        }

        [InjectionMethod]
        public void Init(IUnityContainer container,
            IIntegratedSystemService integratedSystemService
            )
        {
            viewModel = new IbcCtrlGraphVm();
            container.BuildUp(viewModel);
            this.DataContext = viewModel;
        }
    }
    public class IbcCtrlGraphVm : INotifyPropertyChanged
    {
        const int MARKNO_TOVALUES = 1;


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

        /// <summary>
        ///自动按保持最新值,剩余时间
        /// </summary>
        public int AutoKeepNewestTimeRemaining { get; set; }

        /// <summary>
        /// 曲线, 是有 bufferWindow.Record 生成的
        /// Values 最大数量只有 ValuesCap
        /// 平均从 bufferWindow.Record提取
        /// </summary>
        public ChartValues<Db_Width> Values { get; set; }

        /// <summary>
        /// 折径曲线 XY 定义
        /// </summary>
        public CartesianMapper<Db_Width> Mapper_FilmWidth { get; set; }

        /// <summary>
        /// 进风-出风曲线 XY 定义
        /// </summary>
        public CartesianMapper<Db_Width> Mapper_D { get; set; }

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

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

        /// <summary>
        /// 折径目标值
        /// </summary>
        public double FilmWidthAxisYTarget { get; set; }

        /// <summary>
        /// 折径 最大值
        /// </summary>
        public double FilmWidth_MaxValue { get; set; }
        /// <summary>
        /// 折径 最小值
        /// </summary>
        public double FilmWidth_MinValue { get; set; }

        /// <summary>
        /// 进风-出风 最大值
        /// </summary>
        public double D_MaxValue { get; set; }
        /// <summary>
        /// 进风-出风 最小值
        /// </summary>
        public double D_MinValue { get; set; }

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

        /// <summary>
        /// 结束数据
        /// </summary>
        public DateTime EndTime { get; set; }
        #endregion

        #region Command
        public RelayCommand NewestCmd { get; }
        public RelayCommand NextCmd { get; }
        public RelayCommand PreCmd { get; }
        public RelayCommand InfoSetCmd { get; }
        #endregion


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

        IIntegratedSystemService integratedSystemService;
        IBuffer<Db_Width> ctrlList;
        public BufferWindow<Db_Width> DbWidthWindow { get; private set; }
        
        IbcCtrlGraphParams graphParams;

        public IbcCtrlGraphVm()
        {
            NewestCmd = new RelayCommand(() => { DbWidthWindow.MoveNewest(); });
            NextCmd = new RelayCommand(() => { DbWidthWindow.MoveNextPage(); });
            PreCmd = new RelayCommand(() => { DbWidthWindow.MovePrePage(); });
            InfoSetCmd = new RelayCommand(InfoSet);

            Mapper_FilmWidth = Mappers.Xy<Db_Width>()
                .X(value => value.Time.Ticks)
                .Y(value => value.FilmWidth);

            Mapper_D = Mappers.Xy<Db_Width>()
                .X(value => value.Time.Ticks)
                .Y(value => value.InletAirFreq - value.OutletAirFreq);

            DateTimeFormatter = value => new DateTime((long)value).ToString("mm:ss");
            YFormatter = value => value.ToString("F0");

            Values = new ChartValues<Db_Width>();
        }


        public void Init(
            IIntegratedSystemService integratedSystemClient,
            IBuffer<Db_Width> ctrlList
            )
        {
            
            this.integratedSystemService = integratedSystemClient;
            this.graphParams = IbcCtrlGraphParams.Current;
            this.ctrlList = ctrlList;


            DbWidthWindow = new BufferWindow<Db_Width>(ctrlList, graphParams.WindowSize);

            Misc.BindingOperations.SetBinding(graphParams, "WindowSize", DbWidthWindow, "Size");

            DbWidthWindow.Record.CollectionChanged += (object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) =>
            {
                FObjBase.PollModule.Current.Poll_JustOnce(
                    () =>
                    {
                        ToValues();
                    }, this, MARKNO_TOVALUES);
            };

            //TODO 注册更新
            Misc.BindingOperations.SetBinding(integratedSystemClient.Ibc, "FilmWidthSet", this, "FilmWidthAxisYTarget");
            
            Update_filmwidth_y();
            Update_d_y();

            this.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == "FilmWidthAxisYTarget")
                    Update_filmwidth_y();
            };

            graphParams.PropertyChanged += (s, e) => {
                if (e.PropertyName == "IsAxisYRangeAuto")
                {
                    Update_filmwidth_y();
                    Update_d_y();
                }
                else if (e.PropertyName == "FilmWidthAxisYRange")
                {
                    Update_filmwidth_y();
                }
                else if (
                    (e.PropertyName == "DAxisYTarget") ||
                    (e.PropertyName == "DAxisYRange"))
                {
                    Update_d_y();
                }
            };

            //当不是最新数据时,10秒内无操作,自动按 [最新]
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += Timer_Tick;
            DbWidthWindow.PropertyChanged += BufferWindow_PropertyChanged;
        }

        void ToValues()
        {
            if (DbWidthWindow.Record.Count <= ValuesCap)
            {
                Values.Clear();
                Values.AddRange(DbWidthWindow.Record);
            }
            else
            {
                Values.Clear();
                List<Db_Width> record = new List<Db_Width>();
                for (int i = 0; i < ValuesCap; i++)
                {
                    int idx = i * DbWidthWindow.Record.Count / ValuesCap;
                    record.Add(DbWidthWindow.Record[idx]);
                }
                Values.AddRange(record);
            }
            if (Values.Count() > 0)
            {
                BeginTime = Values.First().Time;
                EndTime = Values.Last().Time;
            }
            else
            {
                BeginTime = DateTime.MinValue;
                EndTime = DateTime.MinValue;
            }
        }


        private void Timer_Tick(object sender, EventArgs e)
        {
            AutoKeepNewestTimeRemaining--;
            if (AutoKeepNewestTimeRemaining <= 0)
            {
                AutoKeepNewestTimeRemaining = AutoKeepNewestTimeCounter;
                DbWidthWindow.MoveNewest();
            }
        }
        private void BufferWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsKeepNewest")
            {
                if (!DbWidthWindow.IsKeepNewest)
                {
                    //当前不是最新,开始倒计时
                    AutoKeepNewestTimeRemaining = AutoKeepNewestTimeCounter;
                    timer.Start();
                }
                else
                {
                    timer.Stop();
                }
            }
        }

        void Update_filmwidth_y()
        {
            if (graphParams.IsAxisYRangeAuto)
            {
                FilmWidth_MaxValue = double.NaN;
                FilmWidth_MinValue = double.NaN;
            }
            else
            {
                FilmWidth_MaxValue = FilmWidthAxisYTarget + graphParams.FilmWidthAxisYRange;
                FilmWidth_MinValue = FilmWidthAxisYTarget - graphParams.FilmWidthAxisYRange;
            }
        }
        void Update_d_y()
        {
            if (graphParams.IsAxisYRangeAuto)
            {
                D_MaxValue = double.NaN;
                D_MinValue = double.NaN;
            }
            else
            { 
                D_MaxValue = graphParams.DAxisYTarget + graphParams.DAxisYRange;
                D_MinValue = graphParams.DAxisYTarget - graphParams.DAxisYRange;
            }
        }
        void Debug()
        {
            Values = new ChartValues<Db_Width>();

            double target_filmwidth = 1000;
            double filmwidth = 800;
            double inletfreq = 30;
            double outletfreq = 30;
            double pid_p = 0.01;
            double p_f = 8;

            DateTime date = DateTime.Now;

            Random r = new Random();
            double noise = 2;


            double last_filmwidth = filmwidth;
            for (int i = 0; i < 1000; i++)
            {
                double e = target_filmwidth - filmwidth;
                last_filmwidth = filmwidth;

                double e_freq = e * pid_p;

                inletfreq = e_freq + outletfreq;


                e_freq = inletfreq - outletfreq;

                filmwidth += e_freq * p_f;

                filmwidth += (r.NextDouble() * noise - noise / 2);

                Values.Add(new Db_Width()
                {
                    Time = date.AddSeconds(1 * i),
                    FilmWidth = (float)filmwidth,
                    InletAirFreq = (float)inletfreq,
                    OutletAirFreq = (float)outletfreq
                });
            }
            FilmWidthAxisYTarget = target_filmwidth;


        }

        private void InfoSet()
        {
            WdGraphSet w = new WdGraphSet();
            w.Init(graphParams);
            w.Owner = Application.Current.MainWindow;
            if (w.ShowDialog() == true) 
            {
                graphParams.Save();
            }
        }

    }
    public class IbcCtrlGraphParams : INotifyPropertyChanged
    {
        static IbcCtrlGraphParams current = null;
        public static IbcCtrlGraphParams Current
        {
            get
            {
                if (current == null)
                {
                    current = new IbcCtrlGraphParams();
                    current.Load();
                }
                return current;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 窗口显示数据总量
        /// </summary>
        public int WindowSize { get; set; } = 1000;

        /// <summary>
        /// 折径,进风-出风, Y轴 自动设置 MaxValue, MinValue, 
        /// </summary>
        public bool IsAxisYRangeAuto { get; set; }
        /// <summary>
        /// 折径,Y轴显示范围, 中间值一定是 FilmWidthSet, 单位mm
        /// MaxValue = FilmWidthSet+FilmWidthAxisYRange
        /// MinValue = FilmWidthSet-FilmWidthAxisYRange
        /// </summary>
        public int FilmWidthAxisYRange { get; set; } = 20;


        /// <summary>
        /// 进风-出风 Y轴显示范围 单位Hz
        /// </summary>
        public int DAxisYRange { get; set; } = 10;

        /// <summary>
        /// 进风-出风 Y轴 中间显示值 单位Hz
        /// </summary>
        public int DAxisYTarget { get; set; } = 0;

        string FilePath;
        public IbcCtrlGraphParams()
        {
            string path = System.IO.Path.Combine(FlyLayout.BasePath, "ibcCtrlGraph.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_IbcCtrlGraph : IUiModule2
    {
        /// <summary>
        /// 控件标题
        /// 它的值取决于culture
        /// </summary>
        public string Title => "综合.IBC控制流程图";
        public ComponentType Type => ComponentType.Graph;
        public bool IsUnique => true;

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