using LiveCharts;
using LiveCharts.Configurations;
using LiveCharts.Wpf;
using MahApps.Metro.Controls.Dialogs;
using Microsoft.Win32;
using OfficeOpenXml;
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 WSDDBBrowser.Core;

namespace WSDDBBrowser.Chart
{
    /// <summary>
    /// HistogramGraph.xaml 的交互逻辑
    /// </summary>
    public partial class HistogramGraph : UserControl, IDisposable
    {
        HistogramGraphViewModel viewModel;
        public HistogramGraph()
        {
            InitializeComponent();

        }
        public void Init(ScanDataItem scanDataItem)
        {
            viewModel = new HistogramGraphViewModel();
            viewModel.Init(scanDataItem);
            this.DataContext = viewModel;
        }

        private void button_select_Click(object sender, RoutedEventArgs e)
        {
            SeriesInfo info = (sender as Button).Tag as SeriesInfo;
            if (info.IsVisible)
            {
                info.IsVisible = false;
            }
            else
            {
                info.IsVisible = true;
            }
        }

        private async void ButtonJpgClick(object sender, RoutedEventArgs e)
        {
            string strDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            string filename = "直方图_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".jpg";
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "jpg files (*.jpg)|*.jpg";
            sfd.InitialDirectory = strDesktopPath;
            sfd.FileName = filename;
            if (sfd.ShowDialog() == true)
            {
                ChartToImage.SaveToJpeg(this, sfd.FileName);

                await App.MetroWindow.ShowMessageAsync("输出成功",
                    sfd.FileName);
            }
        }

        private async void ButtonXlsxClick(object sender, RoutedEventArgs e)
        {
            string strDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            string filename = "直方图_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx";
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "xlsx files (*.xlsx)|*.xlsx";
            sfd.InitialDirectory = strDesktopPath;
            sfd.FileName = filename;
            if (sfd.ShowDialog() == true)
            {
                viewModel.SaveToXlsx(sfd.FileName);
                await App.MetroWindow.ShowMessageAsync("输出成功",
                    sfd.FileName);
            }
        }

        public void Dispose()
        {
            viewModel.Dispose();
        }
    }


    public class HistogramGraphViewModel : INotifyPropertyChanged, IDisposable
    {
        #region 图表控制
        public double Target { get; set; }
        public double ToleranceMax { get; set; }
        public double ToleranceMin { get; set; }
        public double CtrllineMax { get; set; }
        public double CtrllineMin { get; set; }

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

        public double YMin { get; set; } = double.NaN;
        public double YMax { get; set; } = double.NaN;

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

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

        public ObservableCollection<SeriesInfo> SeriesInfos { get; } = new ObservableCollection<SeriesInfo>();
        #endregion

        #region 界面统计值
        /// <summary>
        /// 平均值
        /// </summary>
        public double Average { get; set; }
        /// <summary>
        /// CPK
        /// </summary>
        public double CPK { get; set; }
        /// <summary>
        /// 3sigma
        /// </summary>
        public double Sigma3 { get; set; }
        /// <summary>
        /// 在规格线内的%
        /// </summary>
        public double TolerancePercent { get; set; }
        /// <summary>
        /// 在控制线内的%
        /// </summary>
        public double CtrllinePercent { get; set; }

        #endregion
        ChartValues<XY> Values = new ChartValues<XY>();
        ChartValues<XY> NormValues = new ChartValues<XY>();
        object Mapper { get; set; }
        public double Step { get; private set; } = 0.2;
        public int TotalCount { get; private set; } = 0;
        ScanDataItem scanDataItem;

        public HistogramGraphViewModel()
        {
            #region 与数据无关界面参数

            XFormatter = (x) =>
            {
                string name = null;
                if (x == Target)
                {
                    name = "目标值";
                }
                else if (x == ToleranceMax)
                {
                    name = "规格线";
                }
                else if (x == ToleranceMin)
                {
                    name = "规格线";
                }
                else if (x == CtrllineMax)
                {
                    name = "控制线";
                }
                else if (x == CtrllineMin)
                {
                    name = "控制线";
                }

                if (!string.IsNullOrEmpty(name))
                    return $"{name}\n{x:F1}";
                else
                    return x.ToString("F1");
            };

            Mapper = Mappers.Xy<XY>()
                .X((value, index) => value.X)
                .Y(value => value.Y);

            #endregion

            Column2Series columnSeries = new Column2Series
            {
                Title = "直方图",
                Values = Values,
                PointGeometry = null,
                Stroke = new SolidColorBrush(Colors.Black),
                StrokeThickness = 1,
                ColumnWidth = Step,
                DataLabels = true,
                LabelPoint = (cp) => $"{cp.Y:P1}",
                Configuration = Mapper,
            };
            Series.Add(columnSeries);

            columnSeries.SetBinding(Column2Series.ColumnWidthProperty, new Binding("Step") { Source = this });

            LineSeries lineSeries = new LineSeries
            {
                Title = "正态曲线",
                Values = NormValues,
                PointGeometry = null,
                Stroke = new SolidColorBrush(Colors.Black),
                StrokeThickness = 2,
                StrokeDashArray = new DoubleCollection(new double[] { 1, 1 }),
                Fill = new SolidColorBrush(Colors.Transparent),
                Configuration = Mapper,
            };
            Series.Add(lineSeries);
        }


        public void Init(ScanDataItem scanDataItem)
        {
            this.scanDataItem = scanDataItem;

            UpdateX();

            InitBoltSelect();
            UpdateValues();

            scanDataItem.PropertyChanged += ScanDataItem_PropertyChanged;
            DBViewerModel.Instance.PropertyChanged += Instance_PropertyChanged;

        }

        private void ScanDataItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ((e.PropertyName == "Target") || (e.PropertyName == "Tolerance") || (e.PropertyName == "Ctrlline") || (e.PropertyName == "YRangePercent"))
            {
                UpdateX();
            }
            else if (e.PropertyName == "MixCuttingValues")
            {
                InitBoltSelect();
                UpdateValues();
            }
        }

        private void Instance_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Step")
            {
                UpdateValues();
            }
        }

        void InitBoltSelect()
        {
            SeriesInfos.Clear();
            for (int i = 0; i < DBViewerModel.Instance.CuttingMap.Count(); i++)
            {
                SeriesInfo info = new SeriesInfo()
                {
                    BoltIndex = i,
                    Title = $"{i}",
                    Fill = Themes.Styles.GetForeground(i),
                    IsVisible = true
                };

                SeriesInfos.Add(info);

                info.PropertyChanged += (s, e) =>
                {
                    if (e.PropertyName == "IsVisible")
                    {
                        UpdateValues();
                    }
                };
            };
        }
        void UpdateX()
        {
            Target = scanDataItem.Target;
            ToleranceMax = scanDataItem.USL;
            ToleranceMin = scanDataItem.LSL;
            CtrllineMax = Target + scanDataItem.Ctrlline;
            CtrllineMin = Target - scanDataItem.Ctrlline;
            XMax = scanDataItem.YMax;
            XMin = scanDataItem.YMin;

        }
        void UpdateValues()
        {
            Values.Clear();
            Step = DBViewerModel.Instance.Step;

            List<double> values1 = new List<double>();
            //不统计平均值曲线
            foreach (var info in SeriesInfos)
            {
                if (info.IsVisible && info.BoltIndex != -1)
                    values1.AddRange(scanDataItem.GetCuttingValue(info.BoltIndex));
            }
            // 把NaN 删除
            var values = values1.Where(d => !double.IsNaN(d));

            if (values.Count() == 0)
            {
                //没有数据
                TotalCount = 0;
                Average = double.NaN;
                Sigma3 = double.NaN;
                CPK = double.NaN;

                YMin = double.NaN;
                YMax = double.NaN;
                TolerancePercent = double.NaN;
                CtrllinePercent = double.NaN;
                return;
            }
            else
            {
                TotalCount = values.Count();

                List<XY> list = new List<XY>();
                foreach (double d in values)
                {
                    double x = ((int)(d / Step)) * Step;
                    XY xy = list.Find(_xy => _xy.X == x);
                    if (xy != null)
                    {
                        xy.Y++;
                    }
                    else
                    {
                        xy = new XY(x, 1);
                        list.Add(xy);
                    }
                }
                list.Sort();

                Values.AddRange(list.Select(xy => new XY(xy.X, xy.Y / TotalCount)));

                Average = values.Average();
                double sigma = values.Sigma(d => d);
                Sigma3 = sigma * 3;

                int tolerance_cnt = values.Count(d => d <= ToleranceMax && d >= ToleranceMin);
                int ctrline_cnt = values.Count(d => d <= CtrllineMax && d >= CtrllineMin);

                TolerancePercent = 1.0 * tolerance_cnt / TotalCount;
                CtrllinePercent = 1.0 * ctrline_cnt / TotalCount;

                CPK = values.CPK(ToleranceMax, ToleranceMin);


                //double xmin = Average - Sigma3;
                //double xmax = Average + Sigma3;
                //XMax = xmax;
                //XMin = xmin;

                YMin = -Values.Max(xy => xy.Y) * 0.1;
                YMax = Values.Max(xy => xy.Y) * 1.1;

                //xmin = ((int)xmin / DBViewerModel.Instance.Step) * DBViewerModel.Instance.Step;
                //xmax = ((int)xmax / DBViewerModel.Instance.Step) * DBViewerModel.Instance.Step;

                //List<XY> normvalues = new List<XY>();
                //for (double x = xmin; x <= xmax; x += DBViewerModel.Instance.Step)
                //{
                //    normvalues.Add(new XY(x, MathExt.NormDist(x, Average, sigma)));
                //}

                //double sum = normvalues.Sum(xy => xy.Y);
                //把小于0.5的删除
                //normvalues.RemoveAll(xy => (total * xy.Y / sum) <= 0.5);

                //NormValues.Clear();
                //NormValues.AddRange(normvalues.Select(xy => new XY(xy.X, total * xy.Y / sum)));
            }
        }


        public bool SaveToXlsx(string filepath)
        {
            if (Values.Count() == 0)
                return false;
            if (File.Exists(filepath))
            {
                File.Delete(filepath);
            }

            using (ExcelPackage p = new ExcelPackage(new FileInfo(filepath)))
            {
                //Get the Worksheet created in the previous codesample. 
                var ws = p.Workbook.Worksheets.Add("Sheet1");//创建worksheet
                //var ws = p.Workbook.Worksheets["MySheet"];

                //Set the cell value using row and column.
                //添加标题
                ws.Cells[1, 1].Value = "面密度g/m²";
                ws.Cells[1, 2].Value = "数量";

                //内容
                int row = 2;
                for (int i = 0; i < Values.Count(); i++)
                {
                    ws.Cells[row, 1].Value = Values[i].X;
                    ws.Cells[row, 2].Value = Values[i].Y;
                    row++;
                }
                //Save and close the package.
                p.Save();
            }

            return true;
        }

        public void Dispose()
        {
            DBViewerModel.Instance.PropertyChanged -= Instance_PropertyChanged;
            scanDataItem.PropertyChanged -= ScanDataItem_PropertyChanged;

        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
    public class HistogramGraphViewModel_UnitTest : INotifyPropertyChanged
    {
        #region 图表控制
        public double Target { get; set; }
        public double ToleranceMax { get; set; }
        public double ToleranceMin { get; set; }
        public double CtrllineMax { get; set; }
        public double CtrllineMin { get; set; }

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

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

        public ObservableCollection<SeriesInfo> SeriesInfos { get; } = new ObservableCollection<SeriesInfo>();
        #endregion

        #region 界面统计值
        /// <summary>
        /// 平均值
        /// </summary>
        public double Average { get; set; }
        /// <summary>
        /// CPK
        /// </summary>
        public double CPK { get; set; }
        /// <summary>
        /// 3sigma
        /// </summary>
        public double Sigma3 { get; set; }
        /// <summary>
        /// 在规格线内的%
        /// </summary>
        public double TolerancePercent { get; set; }
        /// <summary>
        /// 在控制线内的%
        /// </summary>
        public double CtrllinePercent { get; set; }

        #endregion
        ChartValues<XY> Values = new ChartValues<XY>();

        object Mapper { get; set; }


        public HistogramGraphViewModel_UnitTest()
        {
            #region 与数据无关界面参数

            XFormatter = (x) =>
            {
                string name = null;
                if (x == Target)
                {
                    name = "目标值";
                }
                else if (x == ToleranceMax)
                {
                    name = "规格线";
                }
                else if (x == ToleranceMin)
                {
                    name = "规格线";
                }
                else if (x == CtrllineMax)
                {
                    name = "控制线";
                }
                else if (x == CtrllineMin)
                {
                    name = "控制线";
                }

                if (!string.IsNullOrEmpty(name))
                    return $"{name}\n{x:F1}";
                else
                    return x.ToString("F1");
            };

            Mapper = Mappers.Xy<XY>()
                .X((value, index) => value.X)
                .Y(value => value.Y);

            #endregion
            #region 数据
            double target = 150;
            double tolerance = target * 0.02;
            double ctrlline = tolerance * 0.6;
            double step = 0.5;

            double min = target - tolerance * 1.5;
            double max = target + tolerance * 1.5;

            List<XY> values = new List<XY>();
            for (double x = min; x <= max; x++)
            {
                double y = MathExt.NormDist(x, target + 0.2, ctrlline * 1.4);
                y *= 2000;
                values.Add(new XY(x, y));
            }

            #endregion
            #region 界面数据
            Target = target;
            ToleranceMax = Target + tolerance;
            ToleranceMin = Target - tolerance;
            CtrllineMax = Target + ctrlline;
            CtrllineMin = Target - ctrlline;

            Values.AddRange(values);
            Sigma3 = ctrlline * 1.4 * 3;
            Average = target + 0.2;
            TolerancePercent = 0.801;
            CtrllinePercent = 0.901;

            //Average = BoltValues.Average((b) => b.Value);
            //Max = BoltValues.Max(b => b.Value);
            //Min = BoltValues.Min(b => b.Value);
            #endregion

            Column2Series columnSeries = new Column2Series
            {
                Title = "直方图",
                Values = Values,
                PointGeometry = null,
                Stroke = new SolidColorBrush(Colors.Black),
                StrokeThickness = 1,
                Configuration = Mapper,
            };
            Series.Add(columnSeries);

            SeriesInfos.Add(new SeriesInfo()
            {
                BoltIndex = -1,
                Title = "总体",
                Fill = new SolidColorBrush(System.Windows.Media.Colors.Black),
                IsVisible = true,
            });

            for (int i = 0; i < 6; i++)
            {
                SeriesInfos.Add(new SeriesInfo()
                {
                    BoltIndex = i,
                    Title = $"{i}",
                    Fill = Themes.Styles.GetForeground(i),
                    IsVisible = false
                });
            };
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
    public static class MathExt
    {
        public static double NormDist(double x, double u, double sigma)
        {
            double n1 = 1 / (Math.Sqrt(Math.PI * 2) * sigma);

            double n2 = Math.Exp(-Math.Pow(x - u, 2) / (2 * Math.Pow(sigma, 2)));

            return n1 * n2;
        }
    }
}
