using System;
using System.Collections.Generic;
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 Unity;
using FLY.Thick.Base.Common;
using FLY.Thick.Base.IService;
using CommunityToolkit.Mvvm.Input;
using LiveCharts;
using LiveCharts.Wpf;
using MahApps.Metro.Controls;
using MahApps.Metro.Controls.Dialogs;
using OfficeOpenXml;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;

namespace FLY.Thick.Base.UI
{
    /// <summary>
    /// UcGridGraphVm.xaml 的交互逻辑
    /// </summary>
    public partial class PgGrid : Page
    {
        PgGridVm viewModel;
        public PgGrid()
        {
            InitializeComponent();

            if (System.ComponentModel.LicenseManager.UsageMode != System.ComponentModel.LicenseUsageMode.Runtime)
                return;

            this.Loaded += UcGridGraph_Loaded;
            this.Unloaded += UcGridGraph_Unloaded;
        }

        private void UcGridGraph_Unloaded(object sender, RoutedEventArgs e)
        {
            viewModel.DisposeBinding();
        }

        private void UcGridGraph_Loaded(object sender, RoutedEventArgs e)
        {
            viewModel.SetBinding();
        }

        [InjectionMethod]
        public void Init(IUnityContainer container)
        {
            viewModel = new PgGridVm();
            container.BuildUp(viewModel, new Unity.Resolution.ParameterOverride("chart", chart));

            this.DataContext = viewModel;

            container.BuildUp(mircoGage);
        }
    }

    public class PgGridVm : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        #region IPgGridVm Property

        /// <summary>
        /// AD值模式
        /// </summary>
        public bool IsADMode { get; set; }

        /// <summary>
        /// 最多6条曲线
        /// </summary>
        public ObservableCollection<SeriesInfo> SeriesInfos { get; } = new ObservableCollection<SeriesInfo>();

        /// <summary>
        /// 可以添加新曲线
        /// </summary>
        public bool CanAddSeries { get; set; }
        /// <summary>
        /// 可以删除最后一条曲线
        /// </summary>
        public bool CanRemoveSeries { get; set; }
        #region 曲线

        /// <summary>
        /// 图表放大辅助器
        /// </summary>
        public CartesianChartZoomInHelper ZoomInHelper { get; private set; }

        public Func<double, string> XFormatter { get; private set; }
        public Func<double, string> YFormatter { get; private set; }


        public SeriesCollection Series { get; } = new SeriesCollection();
        #endregion

        #endregion

        #region IPgGridVm Command

        public RelayCommand GetFDataCmd { get; private set; }

        public RelayCommand GetBDataCmd { get; private set; }

        public RelayCommand AddSeriesCmd { get; private set; }
        public RelayCommand RemoveSeriesCmd { get; private set; }

        public RelayCommand SaveCmd { get; private set; }

        public RelayCommand ChangeADModeCmd { get; private set; }

        public RelayCommand<SeriesInfo> SelectCmd { get; private set; }
        #endregion

        public int PosOfGrid { get; set; } = 20;
        public double Mmpp { get; set; } = 0.0943;
        public int PosLength { get; set; } = 20000;

        /// <summary>
        /// grid数据量
        /// </summary>
        public int GridLen => PosLength / PosOfGrid;

        IFlyADService flyAdService;
        IInitParamService initParam;
        MetroWindow metroWindow;
        CartesianChart chart;

        Dictionary<object, List<Misc.BindingOperations.PropertyChangedEventContexts>> bindingConexts = new Dictionary<object, List<Misc.BindingOperations.PropertyChangedEventContexts>>();

        /// <summary>
        /// 数据已经绑定了
        /// </summary>
        bool isBinding;

        public PgGridVm()
        {
            GetFDataCmd = new RelayCommand(() =>
            {
                flyAdService.GetGrid(Misc.DIRECTION.FORWARD, GetGridCB, null);
            });

            GetBDataCmd = new RelayCommand(() =>
            {
                flyAdService.GetGrid(Misc.DIRECTION.BACKWARD, GetGridCB, null);
            });

            SaveCmd = new RelayCommand(Save);

            AddSeriesCmd = new RelayCommand(AddSeries);
            RemoveSeriesCmd = new RelayCommand(RemoveSeries);
            ChangeADModeCmd = new RelayCommand(ChangeADMode);

            SelectCmd = new RelayCommand<SeriesInfo>((info) =>
            {
                if (info.IsSelected)
                    return;
                foreach (var i in SeriesInfos)
                    i.IsSelected = (i == info);
            });


            ZoomInHelper = new CartesianChartZoomInHelper();
            ZoomInHelper.XRangeSlider.SliderMax = 1000;
            ZoomInHelper.XRangeSlider.SliderMin = 0;
            ZoomInHelper.XRangeSlider.MinRange = 10;

            ZoomInHelper.YRangeSlider.SliderMax = 66000;
            ZoomInHelper.YRangeSlider.SliderMin = -1000;
            ZoomInHelper.YRangeSlider.MinRange = 100;

            XFormatter = (x) =>
            {
                int pos = (int)getPosFromGridIndex((int)x);
                int mm = (int)(pos * Mmpp);
                return $"{pos}\n{mm}mm";
            };
            YFormatter = (y) =>
            {
                if (IsADMode)
                {
                    return $"{y}";
                }
                else
                {
                    return $"{y:F1}";
                }
            };

            AddSeries();




        }



        [InjectionMethod]
        public void Init(
            IInitParamService initParam,
            IFlyADService flyADService,
            MetroWindow metroWindow,
            CartesianChart chart
            )
        {
            this.initParam = initParam;
            this.flyAdService = flyADService;
            this.metroWindow = metroWindow;
            this.chart = chart;

            SetBinding();
        }
        /// <summary>
        /// 参数绑定
        /// </summary>
        public void SetBinding()
        {
            if (isBinding)
                return;
            isBinding = true;

            //下面全部event保存在bindingConexts
            Misc.BindingOperations.StartMarkdownEvents(bindingConexts);

            Misc.BindingOperations.SetBinding(this.initParam, nameof(initParam.Encoder1_mmpp), this, nameof(Mmpp));
            Misc.BindingOperations.SetBinding(this.initParam, nameof(initParam.PosOfGrid), this, nameof(PosOfGrid));
            Misc.BindingOperations.SetBinding(this.initParam, nameof(initParam.PosLength), this, nameof(PosLength));


            //图表放大器 X轴 最大值
            Misc.BindingOperations.SetBinding(this, nameof(GridLen), ZoomInHelper.XRangeSlider, nameof(XYRangeSlider.SliderMax));

            //备份event完毕, 必须关闭记录
            Misc.BindingOperations.StopMarkdownEvents();


            ZoomInHelper.Init(chart);

            this.PropertyChanged += PgGridVm_PropertyChanged;
        }

        /// <summary>
        /// 解除绑定
        /// </summary>
        public void DisposeBinding()
        {
            if (!isBinding)//已经解除绑定了
                return;
            isBinding = false;

            Misc.BindingOperations.DisposeEventTargetObject(bindingConexts);

            ZoomInHelper.DisposeBinding();

            this.PropertyChanged -= PgGridVm_PropertyChanged;

        }
        private void ChangeADMode()
        {
            IsADMode = !IsADMode;
        }


        private void PgGridVm_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(IsADMode))
            {
                UpdateADMode();
                UpdateRange();
                ZoomInHelper.FullscreenCmd.Execute(null);
            }
        }


        void GetGridCB(object asyncContext, object retData)
        {
            GridInfo gridinfo = retData as GridInfo;
            var series = SeriesInfos.Last(s => s.IsSelected);

            series.Title = DateTime.Now.ToString("HH:mm:ss");
            series.IsBackw = gridinfo.direction == Misc.DIRECTION.BACKWARD;
            series.ADs.Clear();
            series.Thicks.Clear();
            var ads = gridinfo.data.Select(ad =>
            {
                if (Misc.MyBase.ISVALIDATA(ad))
                {
                    return ad;
                }
                else
                {
                    return double.NaN;
                }
            });
            series.ADs.AddRange(ads);
            series.Thicks.AddRange(gridinfo.thick);
            UpdateRange();

            if (SeriesInfos.Count() < 6)
                CanAddSeries = true;

            if (SeriesInfos.Count() > 1)
                CanRemoveSeries = true;
        }

        Brush GetNextColor()
        {
            List<Brush> brushs = new List<Brush>();
            int index = SeriesInfos.Count();
            while (true)
            {
                var brush = FLY.ControlLibrary.Themes.Styles.GetForeground(index);
                if (SeriesInfos.Any(si => si.Fill == brush))
                {
                    if (brushs.Contains(brush))
                        return brush;//已经没有不一样的了

                    brushs.Add(brush);
                    index++;

                    continue;
                }
                else
                {
                    return brush;
                }
            }
        }
        protected void AddSeries()
        {
            int index = SeriesInfos.Count() - 1;
            if (index >= 0)
            {
                foreach (var seriesInfo in SeriesInfos)
                    seriesInfo.IsSelected = false;
            }
            index++;

            SeriesInfo info = new SeriesInfo()
            {
                Title = $"{index}",
                Fill = GetNextColor(),//FLY.ControlLibrary.Themes.Styles.GetForeground(index),
                IsSelected = true
            };
            LineSeries lineSeries = new LineSeries
            {
                StrokeThickness = 2,
                Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent),
                PointGeometry = null
            };
            lineSeries.SetBinding(LineSeries.StrokeProperty, nameof(SeriesInfo.Fill));
            lineSeries.DataContext = info;
            Series.Add(lineSeries);
            info.Series = lineSeries;
            SeriesInfos.Add(info);

            UpdateADMode();
            CanAddSeries = false;
            CanRemoveSeries = false;
        }
        protected void RemoveSeries()
        {
            if (SeriesInfos.Count() == 1)
                return;//只有一条，不能删除

            var info = SeriesInfos.Last(s => s.IsSelected);

            //解除绑定
            Series.Remove(info.Series);
            SeriesInfos.Remove(info);

            //选择最后一条
            info = SeriesInfos.Last();
            info.IsSelected = true;

            if (SeriesInfos.Count() == 1)
            {
                //只有一条，不能删除
                CanRemoveSeries = false;
            }
        }

        void UpdateADMode()
        {
            if (IsADMode)
            {
                foreach (var info in SeriesInfos)
                    info.Series.SetBinding(LineSeries.ValuesProperty, nameof(SeriesInfo.ADs));
            }
            else
            {
                foreach (var info in SeriesInfos)
                    info.Series.SetBinding(LineSeries.ValuesProperty, nameof(SeriesInfo.Thicks));
            }
        }


        void UpdateRange()
        {
            List<double> vlist = new List<double>();
            foreach (var info in SeriesInfos)
            {
                IEnumerable<double> values = IsADMode ? info.ADs : info.Thicks;

                if (values.Count() == 0)
                    continue;

                var values2 = values.Where(v => !double.IsNaN(v));
                if (values2.Count() == 0)
                    continue;

                vlist.AddRange(values2);
            }
            if (vlist.Count() == 0)
            {
                //没有任何数据
                return;
            }
            else
            {
                if (IsADMode)
                {
                    double max = vlist.Max();
                    double min = vlist.Min();
                    double range = max - min;
                    if (range < 1000)
                        range = 1000;

                    ZoomInHelper.YRangeSlider.SliderMax = Math.Round(max + range / 10);
                    ZoomInHelper.YRangeSlider.SliderMin = Math.Round(min - range / 10);
                    ZoomInHelper.YRangeSlider.MinRange = 100;
                }
                else
                {
                    double max = vlist.Max();
                    double min = vlist.Min();
                    double range = max - min;
                    if (range < 10)
                        range = 10;

                    ZoomInHelper.YRangeSlider.SliderMax = Math.Round(max + range / 10);
                    ZoomInHelper.YRangeSlider.SliderMin = Math.Round(min - range / 10);
                    ZoomInHelper.YRangeSlider.MinRange = 2;
                }
            }
        }

        async void Save()
        {
            string strDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);

            string filename = $"Grid数据_{DateTime.Now:yyyyMMddHHmmss}.xlsx";

            string filePath = System.IO.Path.Combine(strDesktopPath, filename);

            if (SeriesInfos.All(s => s.ADs.Count() == 0))
            {
                MessageBox.Show("没有任何数据", "输出失败", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            var ret = await Task.Factory.StartNew(() =>
            {
                try
                {
                    using (ExcelPackage p = new ExcelPackage(new FileInfo(filePath)))
                    {
                        SaveToXlsxThicks(p);
                        SaveToXlsxAD(p);
                        p.Save();
                    }
                    return true;
                }
                catch
                {
                    return false;
                }
            });
            if(ret==true)
                MessageBox.Show($"导出到{filePath}", "输出成功", MessageBoxButton.OK, MessageBoxImage.Information);
            else
                MessageBox.Show($"未知错误", "输出失败", MessageBoxButton.OK, MessageBoxImage.Error);
        }

        void SaveToXlsxAD(ExcelPackage p)
        {
            var seriesInfos = SeriesInfos;
            int posOfGrid = PosOfGrid;
            double mmpp = Mmpp;
            var ws = p.Workbook.Worksheets.Add("AD数据");
            ws.Cells[1, 1].Value = "脉冲";
            ws.Cells[1, 2].Value = "mm";

            int max_pos_count = 0;
            for (int i = 0; i < seriesInfos.Count(); i++)
            {
                var info = seriesInfos[i];
                var values = info.ADs;
                int col = 3 + i;
                ws.Cells[1, col].Value = $"{info.Header}";
                for (int j = 0; j < values.Count(); j++)
                {
                    if (!double.IsNaN(values[j]))
                        ws.Cells[2 + j, col].Value = (int)values[j];
                }
                if (values.Count() <= max_pos_count)
                    continue;

                // 脉冲 & mm
                for (int j = max_pos_count; j < values.Count(); j++)
                {
                    int pos = getPosFromGridIndex(j);
                    double mm = pos * mmpp;

                    ws.Cells[2 + j, 1].Value = pos;
                    ws.Cells[2 + j, 2].Value = mm;
                }
                max_pos_count = values.Count();
            }

            //设置格式
            //格式
            ws.Column(1).Style.Numberformat.Format = "0";
            ws.Column(2).Style.Numberformat.Format = "0";
            for (int i = 0; i < seriesInfos.Count(); i++)
            {
                int col = 3 + i;
                ws.Column(col).Style.Numberformat.Format = "0";
            }
        }

        void SaveToXlsxThicks(ExcelPackage p)
        {
            var seriesInfos = SeriesInfos;
            int posOfGrid = PosOfGrid;
            double mmpp = Mmpp;
            var ws = p.Workbook.Worksheets.Add("Thick数据");
            ws.Cells[1, 1].Value = "脉冲";
            ws.Cells[1, 2].Value = "mm";

            int max_pos_count = 0;
            for (int i = 0; i < seriesInfos.Count(); i++)
            {
                var info = seriesInfos[i];
                var values = info.Thicks;
                int col = 3 + i;
                ws.Cells[1, col].Value = $"{info.Header}";
                for (int j = 0; j < values.Count(); j++)
                {
                    if (!double.IsNaN(values[j]))
                        ws.Cells[2 + j, col].Value = values[j];
                }
                if (values.Count() <= max_pos_count)
                    continue;

                // 脉冲 & mm
                for (int j = max_pos_count; j < values.Count(); j++)
                {
                    int pos = getPosFromGridIndex(j);
                    double mm = pos * mmpp;
                    ws.Cells[2 + j, 1].Value = pos;
                    ws.Cells[2 + j, 2].Value = mm;
                }
                max_pos_count = values.Count();
            }

            //设置格式
            //格式
            ws.Column(1).Style.Numberformat.Format = "0";
            ws.Column(2).Style.Numberformat.Format = "0";
            for (int i = 0; i < seriesInfos.Count(); i++)
            {
                int col = 3 + i;
                ws.Column(col).Style.Numberformat.Format = "0.00";
            }
        }




        int getPosFromGridIndex(int gridIndex)
        {
            return gridIndex * PosOfGrid;
        }
        int getGridIndexFromPos(int pos)
        {
            return pos / PosOfGrid;
        }
    }

    public class SeriesInfo : INotifyPropertyChanged
    {
        /// <summary>
        /// 显示的标题，肯定是 0，1，2，3，4，5.。。。
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// 是反向数据
        /// </summary>
        public bool? IsBackw { get; set; }

        public string Header
        {
            get
            {
                if (IsBackw == null)
                    return Title;
                else
                {
                    if (IsBackw == false)
                        return $"{Title} 正向";
                    else
                        return $"{Title} 反向";
                }
            }
        }
        /// <summary>
        /// 颜色
        /// </summary>
        public Brush Fill { get; set; }

        /// <summary>
        /// 当前被选中
        /// </summary>
        public bool IsSelected { get; set; }
        /// <summary>
        /// 对应的曲线
        /// </summary>
        public LineSeries Series { get; set; }

        /// <summary>
        /// 测量区平均值
        /// </summary>
        public double Avg { get; set; } = double.NaN;
        /// <summary>
        /// 当非AD值模式时，显示的厚度值
        /// </summary>
        public ChartValues<double> Thicks { get; } = new ChartValues<double>();
        /// <summary>
        /// 当为AD值模式时，显示的AD值
        /// </summary>
        public ChartValues<double> ADs { get; } = new ChartValues<double>();

        public void Clear()
        {
            Thicks.Clear();
            ADs.Clear();
            Avg = double.NaN;
            IsBackw = null;
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class PgGridVmUt : PgGridVm
    {
        public PgGridVmUt()
        {

            PosOfGrid = 20;
            Mmpp = 0.0943;
            IsADMode = false;


            AddSeries();

            Random r = new Random();
            for (int i = 0; i < SeriesInfos.Count(); i++)
            {
                var info = SeriesInfos[i];
                int period = r.Next(200, 600);
                info.ADs.Clear();
                info.Thicks.Clear();
                info.ADs.AddRange(GetADs(period));
                info.Thicks.AddRange(GetValues(info.ADs));
                info.IsBackw = i % 2 == 0;
            }
        }

        IEnumerable<double> GetADs(int period)
        {
            List<double> ads = new List<double>();

            Random r = new Random();
            for (int i = 0; i < 1000; i++)
            {
                if (i < 100 || i > 800)
                {
                    ads.Add(double.NaN);
                }
                else
                {
                    int ad = (int)(Math.Sin(Math.PI * 2 * i / period) * 2000) + r.Next(200) - 100 + 10000;
                    ads.Add(ad);
                }
            }
            return ads;
        }
        IEnumerable<double> GetValues(IEnumerable<double> ADs)
        {
            return ADs.Select(ad =>
            {
                if (double.IsNaN(ad))
                    return double.NaN;
                else
                    return ad / 100.0;
            });
        }
    }
}
