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.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>
    /// ColumnRangeScanGraph.xaml 的交互逻辑
    /// </summary>
    public partial class ColumnRangeScanGraph : UserControl, IDisposable
    {
        ColumnRangeScanGraphViewModel viewModel;
        public ColumnRangeScanGraph()
        {
            InitializeComponent();
        }
        public void Init(ScanDataItem scanDataItem)
        {
            viewModel = new ColumnRangeScanGraphViewModel();
            viewModel.Init(scanDataItem);
            this.DataContext = viewModel;
        }

        private void Button_pre_click(object sender, RoutedEventArgs e)
        {
            viewModel.Pre();
        }

        private void Button_next_click(object sender, RoutedEventArgs e)
        {
            viewModel.Next();
        }

        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);
            }
        }

        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);
            }

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

    public class ColumnRangeScanGraphViewModel : INotifyPropertyChanged, IDisposable
    {
        #region 图表控制
        public double YMax { get; set; } = double.NaN;
        public double YMin { get; set; } = double.NaN;
        public double Target { get; set; } = double.NaN;
        public double ToleranceYMax { get; set; } = double.NaN;
        public double ToleranceYMin { get; set; } = double.NaN;
        public double CtrllineYMax { get; set; } = double.NaN;
        public double CtrllineYMin { get; set; } = double.NaN;
        public double XMax { get; set; } = double.NaN;
        public double XMin { get; set; } = double.NaN;

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

        public SeriesCollection Series { get; set; }
        #endregion



        #region 界面统计值
        public double Velocity { get; set; }
        public double Position { get; set; }
        public double Average { get; set; } = double.NaN;

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

        /// <summary>
        /// 混合数
        /// </summary>
        public int Mix { get; set; } = 6;
        /// <summary>
        /// 测量时间
        /// </summary>
        public DateTime Time { get; set; }
        /// <summary>
        /// 数据库中的ID标示
        /// </summary>
        public int Index { get; set; }
        public int Count { get; set; }
        public double FilmBegin { get; set; } = double.NaN;
        public double FilmEnd { get; set; } = double.NaN;

        public bool IsPreEnable { get; private set; }
        public bool IsNextEnable { get; private set; }
        #endregion

        List<Brush> AreaColors;

        ChartValues<double> Values { get; } = new ChartValues<double>();
        ChartValues<BoltValue> BoltValues { get; } = new ChartValues<BoltValue>();
        object Mapper { get; set; }

        object BoltMapper { get; set; }


        ScanDataItem scanDataItem;

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

            AreaColors = new List<Brush>();
            for (int i = 0; i < 5; i++)
                AreaColors.Add(App.Current.FindResource($"AreaColors{i}") as Brush);

            XFormatter = (x) =>
            {
                double cm = x;
                string str = $"{cm:F0}cm";
                return str;
            };

            YFormatter = (y) =>
            {
                string name = null;
                if (y == Target)
                {
                    name = "目标值";
                }
                else if (y == ToleranceYMax)
                {
                    name = "规格线";
                }
                else if (y == ToleranceYMin)
                {
                    name = "规格线";
                }
                else if (y == CtrllineYMax)
                {
                    name = "控制线";
                }
                else if (y == CtrllineYMin)
                {
                    name = "控制线";
                }

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

            Mapper = Mappers.Xy<double>()
                .X((value, index) => index + 0.5)
                .Y(value => value);

            BoltMapper = Mappers.Weighted<BoltValue>()
                .X(b => (b.From + b.To) / 2)
                .Weight(b => Math.Abs(b.From - b.To))
                .Y(b => b.Value)
                .Fill((b) =>
                {
                    if (b.Value > ToleranceYMax)
                    {
                        return AreaColors[0];
                    }
                    else if (b.Value > CtrllineYMax)
                    {
                        return AreaColors[1];
                    }
                    else if (b.Value >= CtrllineYMin)
                    {
                        return AreaColors[2];
                    }
                    else if (b.Value >= ToleranceYMin)
                    {
                        return AreaColors[3];
                    }
                    else
                    {
                        return AreaColors[4];
                    }
                });
            #endregion

            Series = new SeriesCollection
            {
                new LineSeries
                {
                    Title = "Line",
                    Values = Values,
                    StrokeThickness = 3,
                    Stroke = new SolidColorBrush(System.Windows.Media.Colors.Gray),
                    Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent),
                    PointGeometry = null,
                    Configuration = Mapper
                },

                new ColumnRangeSeries
                {
                    Title = "ColumnRange",
                    Values = BoltValues,
                    StrokeThickness = 1,
                    Opacity=0.6,
                    Stroke = new SolidColorBrush(System.Windows.Media.Colors.Black),
                    DataLabels = true,
                    FontSize=15,
                    Configuration = BoltMapper
                }
            };
            ((Series)Series[1]).SetBinding(ColumnRangeSeries.YAxisCrossingProperty, new Binding("Target") { Source = this });

            this.PropertyChanged += ColumnRangeScanGraphViewModel_PropertyChanged;
        }

        private void ColumnRangeScanGraphViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Index")
            {
                UpdatePreNextEnable();
            }

        }
        void UpdateY()
        {
            Target = scanDataItem.Target;
            ToleranceYMax = scanDataItem.USL;
            ToleranceYMin = scanDataItem.LSL;
            CtrllineYMax = Target + scanDataItem.Ctrlline;
            CtrllineYMin = Target - scanDataItem.Ctrlline;
            YMax = scanDataItem.YMax;
            YMin = scanDataItem.YMin;
        }
        void UpdateX()
        {
            var process = DBViewerModel.Instance.tgProcess;
            if ((process.nSRBegin != null) && (process.nSREnd != null))
            {
                XMin = (int)process.nSRBegin;
                XMax = (int)process.nSREnd;
            }
            else
            {
                XMin = 0;
                XMax = DBViewerModel.Instance.NBolts;
            }
        }

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

            UpdateX();

            Values.Clear();
            BoltValues.Clear();

            Index = 0;

            Count = scanDataItem.MixValues.Count();
            UpdatePreNextEnable();

            Update4IndexChanged();


            scanDataItem.PropertyChanged += scanDataItem_PropertyChanged;
        }
        public void Dispose()
        {
            scanDataItem.PropertyChanged -= scanDataItem_PropertyChanged;
        }



        private void scanDataItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ((e.PropertyName == "Target") || (e.PropertyName == "Tolerance") || (e.PropertyName == "Ctrlline") || (e.PropertyName == "YRangePercent"))
            {
                UpdateY();
            }
            else if (e.PropertyName == "MixCuttingValues")
            {
                Update4IndexChanged();
            }
        }

        void Update4IndexChanged()
        {
            if (Count == 0)
                return;
            if (Index < 0)
                return;
            if (Index >= Count)
                return;

            Values.Clear();

            Values.AddRange(scanDataItem.MixValues[Index]);

            BoltValues.Clear();
            BoltValues.AddRange(scanDataItem.MixCuttingValues[Index]);

            var list = from b in BoltValues where !double.IsNaN(b.Value) select b.Value;
            if (list.Count() == 0)
            {
                Average = double.NaN;
                Max = double.NaN;
                Min = double.NaN;
            }
            else
            {
                Average = list.Average();
                Max = list.Max();
                Min = list.Min();
            }
            var scanData = scanDataItem.Values[Index];
            if (scanData == null)
            {
                throw new Exception($"ColumnRangeScanGraph.Update4IdChanged Index = {Index}, Count = {Count}, 但DBViewerModel.Instance.GetScanData(0, Index) = null");
            }
            Velocity = (double)scanData.nVelocity;
            Position = (double)scanData.nProcessLen;
            Time = (DateTime)scanData.tTime;
            FilmBegin = (double)(scanData.nFilmBegin) / 10.0;
            FilmEnd = (double)(scanData.nFilmEnd) / 10.0;
        }

        void UpdatePreNextEnable()
        {
            if (Count == 0)
            {
                IsPreEnable = false;
                IsNextEnable = false;
            }
            else
            {
                if (Index <= 0)
                    IsPreEnable = false;
                else
                    IsPreEnable = true;

                if (Index >= Count - 1)
                    IsNextEnable = false;
                else
                    IsNextEnable = true;
            }
        }
        public void Next()
        {
            if (Index < Count - 1)
            {
                Index++;
                Update4IndexChanged();
            }
        }
        public void Pre()
        {
            if (Index > 0)
            {
                Index--;
                Update4IndexChanged();
            }
        }

        public bool SaveToXlsx(string filepath)
        {
            if (BoltValues.Count() == 0 || 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 = "时间";
                ws.Cells[1, 2].Value = "纵向位置m";
                ws.Cells[1, 3].Value = "涂布速度m/min";
                ws.Cells[1, 4].Value = "目标值g/m²";
                ws.Cells[1, 5].Value = "规格线g/m²";
                ws.Cells[1, 6].Value = "控制线g/m²";
                ws.Cells[1, 7].Value = "开始位置mm";
                ws.Cells[1, 8].Value = "结束位置mm";
                ws.Cells[1, 9].Value = "平均值g/m²";
                int col = 10;
                for (int i = 0; i < DBViewerModel.Instance.CuttingMap.Count(); i++)
                {
                    ws.Cells[1, col].Value = $"分条{i}";
                    col++;
                }
                for (int i = 0; i < Values.Count(); i++)
                {
                    ws.Cells[1, col].Value = $"{i}cm";
                    col++;
                }
                //格式
                ws.Column(1).Style.Numberformat.Format = "yyyy-MM-dd HH:mm:ss";
                ws.Column(2).Style.Numberformat.Format = "0.00";
                for (int i = 3; i < col; i++)
                    ws.Column(i).Style.Numberformat.Format = "0.0";
                ws.Column(7).Style.Numberformat.Format = "0";
                ws.Column(8).Style.Numberformat.Format = "0";

                //内容

                var scanData = scanDataItem.Values[Index];
                if (scanData == null)
                {
                    throw new Exception($"ColumnRangeScanGraph.Update4IdChanged Index = {Index}, Count = {Count}, 但DBViewerModel.Instance.GetScanData(0, Index) = null");
                }

                ws.Cells[2, 1].Value = ((DateTime)scanData.tTime).ToOADate();
                ws.Cells[2, 2].Value = (double)scanData.nProcessLen;
                ws.Cells[2, 3].Value = (double)scanData.nVelocity;
                ws.Cells[2, 4].Value = scanDataItem.Target;
                ws.Cells[2, 5].Value = scanDataItem.Tolerance;
                ws.Cells[2, 6].Value = scanDataItem.Ctrlline;
                ws.Cells[2, 7].Value = (double)scanData.nFilmBegin;
                ws.Cells[2, 8].Value = (double)scanData.nFilmEnd;
                ws.Cells[2, 9].Value = Average;
                col = 10;
                for (int i = 0; i < DBViewerModel.Instance.CuttingMap.Count(); i++)
                {
                    if (i < BoltValues.Count())
                    {
                        if (!double.IsNaN(BoltValues[i].Value))
                            ws.Cells[2, col].Value = BoltValues[i].Value;
                    }
                    col++;
                }
                for (int i = 0; i < Values.Count(); i++)
                {
                    if (!double.IsNaN(Values[i]))
                        ws.Cells[2, col].Value = Values[i];
                    col++;
                }


                //Save and close the package.
                p.Save();
            }

            return true;
        }



        public event PropertyChangedEventHandler PropertyChanged;
    }


    public class ColumnRangeScanGraphViewModel_UnitTest : INotifyPropertyChanged
    {
        #region 图表控制
        public double YMax { get; set; }
        public double YMin { get; set; }
        public double Target { get; set; }
        public double ToleranceYMax { get; set; }
        public double ToleranceYMin { get; set; }
        public double CtrllineYMax { get; set; }
        public double CtrllineYMin { get; set; }
        public double XMax { get; set; }
        public double XMin { get; set; }

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

        public SeriesCollection Series { get; set; }
        #endregion

        #region 颜色
        List<Brush> AreaColors;
        #endregion


        #region 界面统计值
        public double Velocity { get; set; }
        public double Position { get; set; }
        public double Average { get; set; }

        /// <summary>
        /// 最大值
        /// </summary>
        public double Max { get; set; }
        /// <summary>
        /// 最小值
        /// </summary>
        public double Min { get; set; }

        /// <summary>
        /// 混合数
        /// </summary>
        public int Mix { get; set; }
        /// <summary>
        /// 测量时间
        /// </summary>
        public DateTime Time { get; set; }
        /// <summary>
        /// 数据库中的ID标示
        /// </summary>
        public int Index { get; set; }
        public int Count { get; set; }
        public bool IsPreEnable { get; private set; }
        public bool IsNextEnable { get; private set; }
        #endregion



        ChartValues<double> Values { get; } = new ChartValues<double>();
        ChartValues<BoltValue> BoltValues { get; } = new ChartValues<BoltValue>();
        object Mapper { get; set; }

        object BoltMapper { get; set; }

        public ColumnRangeScanGraphViewModel_UnitTest()
        {
            #region 与数据无关界面参数
            AreaColors = new List<Brush>();
            for (int i = 0; i < 5; i++)
                AreaColors.Add(App.Current.FindResource($"AreaColors{i}") as Brush);

            XFormatter = (x) =>
            {
                double cm = x;
                string str = $"{cm:F0}cm";
                return str;
            };

            YFormatter = (y) =>
            {
                string name = null;
                if (y == Target)
                {
                    name = "目标值";
                }
                else if (y == ToleranceYMax)
                {
                    name = "规格线";
                }
                else if (y == ToleranceYMin)
                {
                    name = "规格线";
                }
                else if (y == CtrllineYMax)
                {
                    name = "控制线";
                }
                else if (y == CtrllineYMin)
                {
                    name = "控制线";
                }

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

            Mapper = Mappers.Xy<double>()
                .X((value, index) => index + 0.5)
                .Y(value => value);

            BoltMapper = Mappers.Weighted<BoltValue>()
                .X(b => (b.From + b.To) / 2)
                .Weight(b => Math.Abs(b.From - b.To))
                .Y(b => b.Value)
                .Fill((b) =>
                {
                    if (b.Value > ToleranceYMax)
                    {

                        return AreaColors[0];
                    }
                    else if (b.Value > CtrllineYMax)
                    {
                        return AreaColors[1];
                    }
                    else if (b.Value >= CtrllineYMin)
                    {
                        return AreaColors[2];
                    }
                    else if (b.Value >= ToleranceYMin)
                    {
                        return AreaColors[3];
                    }
                    else
                    {
                        return AreaColors[4];
                    }
                });
            #endregion
            #region 数据
            double target = 150;
            double tolerance = target * 0.02;
            double ctrlline = tolerance * 0.6;
            double yrangepercent = 1.5;
            int nbolts = 80;
            Bolt scanrange = new Bolt(10, 62);
            double[] datas = new double[nbolts];
            Random random = new Random();
            for (int i = 0; i < nbolts; i++)
            {
                if (i < scanrange.Begin)
                    datas[i] = double.NaN;
                else if (i > scanrange.End)
                    datas[i] = double.NaN;
                else
                    datas[i] = (Math.Sin(i * Math.PI / nbolts) * 3) * tolerance + target + (random.NextDouble() - 0.5) * 1 - 6;
            }
            List<Bolt> boltmap = new List<Bolt>
            {
                new Bolt(110, 160),
                new Bolt(160, 210),
                new Bolt(210, 260),
                new Bolt(260, 310),
                new Bolt(410, 460),
                new Bolt(460, 510),
                new Bolt(510, 560),
                new Bolt(560, 610)
            };
            #endregion
            #region 界面数据
            Target = target;
            ToleranceYMax = Target + tolerance;
            ToleranceYMin = Target - tolerance;
            CtrllineYMax = Target + ctrlline;
            CtrllineYMin = Target - ctrlline;
            YMax = Target + tolerance * yrangepercent;
            YMin = Target - tolerance * yrangepercent;
            XMin = 0;// scanrange.Begin;
            XMax = nbolts - 1;// scanrange.End;
            Mix = 6;
            Time = DateTime.Now;
            Index = 3;
            Count = 10;
            IsPreEnable = true;
            IsNextEnable = true;
            Velocity = 14.8;
            Position = 752.1;
            Values.Clear();
            Values.AddRange(datas);

            List<BoltValue> boltValues = new List<BoltValue>();
            foreach (Bolt bolt in boltmap)
            {
                List<double> a = new List<double>();
                int b = (int)Math.Floor(bolt.Begin / 10);
                int e = (int)Math.Ceiling(bolt.End / 10);
                for (int i = b; i <= e; i++)
                {
                    if (i >= 0 && i < datas.Count() && !double.IsNaN(datas[i]))
                    {
                        a.Add(datas[i]);
                    }
                }
                boltValues.Add(new BoltValue(bolt.Begin / 10, bolt.End / 10, a.Average()));
            }

            BoltValues.Clear();
            BoltValues.AddRange(boltValues);

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

            Series = new SeriesCollection
            {
                new LineSeries
                {
                    Title = "Line",
                    Values = Values,
                    StrokeThickness = 3,
                    Stroke = new SolidColorBrush(System.Windows.Media.Colors.Gray),
                    Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent),
                    PointGeometry = null,
                    Configuration = Mapper
                },

                new ColumnRangeSeries
                {
                    Title = "ColumnRange",
                    Values = BoltValues,
                    StrokeThickness = 1,
                    Opacity=0.6,
                    Stroke = new SolidColorBrush(System.Windows.Media.Colors.Black),
                    DataLabels = true,
                    FontSize = 15,
                    YAxisCrossing = Target,
                    Configuration = BoltMapper
                }
            };

        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}
