using DevExpress.Charts.Native;
using DevExpress.Utils.Extensions;
using DevExpress.XtraCharts;
using DevExpress.XtraEditors;
using DevExpress.XtraLayout;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionsDev
{
    public static class ChartExtension
    {
        private enum EnumTimeType
        {
            Year,
            Month,
            Day,
            Hour,
            Minute,
            Second,
            Millisecond
        }
        private class ChartOptions
        {
            public int XLableCount { get; set; } = 5;
            public EnumTimeType TimeType { get; set; } = EnumTimeType.Second;

            public bool ShowMarker { get; set; }

            public int MarkerSize { get; set; } = 4;

        }

        /// <summary>
        /// 快速构建一个Spline类型的ChartControl Series,X轴绑定时间类型的数据(目前还不支持多个series)
        /// </summary>
        /// <param name="ctrl"></param>
        /// <param name="chartTitle">Chart顶部的标题</param>
        /// <param name="unitY">Y轴单位,默认无单位</param>
        /// <param name="yPrecision">Y轴数字保留的精度,默认3(只对double float类型的数据有效)</param>
        /// <param name="xFormat">时间格式字符</param>
        /// <param name="xLableCount">X轴时间显示的标签个数</param>
        /// <param name="showMarker">是否在Spline中显示点(如果绑定的数据点数少于3个,则始终会显示)</param>
        /// <param name="series">该参数会使ChartControl创建多个Series,不含元素时也会创建一个</param>
        /// <returns></returns>
        public static ChartControl UseSplineWithTimeData(this ChartControl ctrl, string chartTitle = "ChartTitle", string unitY = "", int yPrecision = 3, string xFormat = "yyyy-MM-dd HH:mm:ss", int xLableCount = 5, bool showMarker = false,string[] series=null)
        {
            ctrl.Titles.Clear();
            ctrl.Titles.Add(new ChartTitle() { Text = chartTitle });
            ctrl.Series.Clear();
            if (series == null || !series.Any())
            {
                series = new string[1];
            }
            var options = new ChartOptions()
            {
                XLableCount = xLableCount,
                ShowMarker = showMarker,
            };
            foreach (var item in series)
            {
                Series s = new Series();
                s.CrosshairLabelPattern = "{A:" + xFormat + "}\r\n {V:f" + yPrecision + "}" + unitY;
                s.Name = item;
                s.ArgumentScaleType = ScaleType.DateTime;
                SplineSeriesView splineSeriesView1 = new SplineSeriesView();
                s.View = splineSeriesView1;
                s.Tag = options;
                ctrl.Series.Add(s);
            }


            var dt = new DateTime(2000, 2, 2, 1, 1, 1, 1);
            var dtStr = dt.ToString(xFormat);
            var dtNew = DateTime.ParseExact(dtStr, xFormat, null);

            XYDiagram diagram = (XYDiagram)ctrl.Diagram;//坐标系设定
            diagram.AxisX.Tickmarks.MinorVisible = false;
            diagram.AxisY.Tickmarks.MinorVisible = false;
            diagram.AxisX.Label.TextPattern = "{A:" + xFormat + "}";
            diagram.AxisY.Label.TextPattern = "{V}" + unitY;

           
            if (dtNew.Millisecond == 1)
            {
                diagram.AxisX.DateTimeScaleOptions.MeasureUnit = DateTimeMeasureUnit.Millisecond;
                diagram.AxisX.DateTimeScaleOptions.GridAlignment = DateTimeGridAlignment.Millisecond;
                options.TimeType = EnumTimeType.Millisecond;
            }
            else if (dtNew.Second == 1)
            {
                diagram.AxisX.DateTimeScaleOptions.MeasureUnit = DateTimeMeasureUnit.Second;
                diagram.AxisX.DateTimeScaleOptions.GridAlignment = DateTimeGridAlignment.Second;
                options.TimeType = EnumTimeType.Second;
            }
            else if (dtNew.Minute == 1)
            {
                diagram.AxisX.DateTimeScaleOptions.MeasureUnit = DateTimeMeasureUnit.Minute;
                diagram.AxisX.DateTimeScaleOptions.GridAlignment = DateTimeGridAlignment.Minute;
                options.TimeType = EnumTimeType.Minute;
            }
            else if (dtNew.Hour == 1)
            {
                diagram.AxisX.DateTimeScaleOptions.MeasureUnit = DateTimeMeasureUnit.Hour;
                diagram.AxisX.DateTimeScaleOptions.GridAlignment = DateTimeGridAlignment.Hour;
                options.TimeType = EnumTimeType.Hour;
            }
            else if (dtNew.Day == 2)
            {
                diagram.AxisX.DateTimeScaleOptions.MeasureUnit = DateTimeMeasureUnit.Day;
                diagram.AxisX.DateTimeScaleOptions.GridAlignment = DateTimeGridAlignment.Day;
                options.TimeType = EnumTimeType.Day;
            }
            else if (dtNew.Month == 2)
            {
                diagram.AxisX.DateTimeScaleOptions.MeasureUnit = DateTimeMeasureUnit.Month;
                diagram.AxisX.DateTimeScaleOptions.GridAlignment = DateTimeGridAlignment.Month;
                options.TimeType = EnumTimeType.Month;
            }
            else
            {
                diagram.AxisX.DateTimeScaleOptions.MeasureUnit = DateTimeMeasureUnit.Year;
                diagram.AxisX.DateTimeScaleOptions.GridAlignment = DateTimeGridAlignment.Year;
                options.TimeType = EnumTimeType.Year;
            }
            return ctrl;
        }
        /// <summary>
        /// 设置数据源
        /// </summary>
        /// <typeparam name="T">泛型只支持基本数值类型</typeparam>
        /// <param name="ctrl"></param>
        /// <param name="data"></param>
        /// <param name="series">要设置的series,默认会设置第一个series</param>
        /// <returns></returns>
        public static ChartControl SetData<T>(this ChartControl ctrl, IEnumerable<(DateTime x, T y)> data,string series=null)
        {
            var s = ctrl.Series.Where(p=>p.Name==series).FirstOrDefault() as Series;
            if (s == null)
                s = ctrl.Series.FirstOrDefault() as Series;
            ChartOptions options = s.Tag as ChartOptions;
            (s.View as SplineSeriesView).LineMarkerOptions.Size = options.MarkerSize;
            if (data.Count() < 3)
                (s.View as SplineSeriesView).MarkerVisibility = DevExpress.Utils.DefaultBoolean.True;
            else
            {
                if (options.ShowMarker)
                    (s.View as SplineSeriesView).MarkerVisibility = DevExpress.Utils.DefaultBoolean.True;
                else
                    (s.View as SplineSeriesView).MarkerVisibility = DevExpress.Utils.DefaultBoolean.False;
            }
            var yMin = data.Select(p => p.y).Min();
            var yMax = data.Select(p => p.y).Max();
            var xMin = data.Select(p => p.x).Min();
            var xMax = data.Select(p => p.x).Max();
            XYDiagram diagram = (XYDiagram)ctrl.Diagram;//坐标系设定
            diagram.AxisY.WholeRange.SetMinMaxValues(yMin, yMax);
            double gridSpacing = 0;
            if (options.TimeType == EnumTimeType.Millisecond)
            {
                gridSpacing = (xMax - xMin).TotalMilliseconds / (options.XLableCount - 1);
            }
            else if (options.TimeType == EnumTimeType.Second)
            {
                gridSpacing = (xMax - xMin).TotalSeconds / (options.XLableCount - 1);
            }
            else if (options.TimeType == EnumTimeType.Minute)
            {
                gridSpacing = (xMax - xMin).TotalMinutes / (options.XLableCount - 1);
            }
            else if (options.TimeType == EnumTimeType.Hour)
            {
                gridSpacing = (xMax - xMin).TotalHours / (options.XLableCount - 1);
            }
            else if (options.TimeType == EnumTimeType.Day)
            {
                gridSpacing = (xMax - xMin).TotalDays / (options.XLableCount - 1);
            }
            else if (options.TimeType == EnumTimeType.Month)
            {
                gridSpacing = (xMax - xMin).TotalDays / 30d / (options.XLableCount - 1);
            }
            else if (options.TimeType == EnumTimeType.Year)
            {
                gridSpacing = (xMax - xMin).TotalDays / 365d / (options.XLableCount - 1);
            }
            if (gridSpacing > 0)
                diagram.AxisX.DateTimeScaleOptions.GridSpacing = gridSpacing;
            s.Points.AddRange(data.Select(p => new SeriesPoint(p.x, p.y)).ToArray());
            return ctrl;
        }

    }
}
