using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms.DataVisualization.Charting;
using System.Drawing;
using System.Data;

namespace QMAPP.WinForm.Common.SPCMonitor
{
    /// <summary>
    /// 描述:SPC图表帮助类
    /// 作者:单雨春
    /// 日期:2015年5月28日11:03:02
    /// </summary>
    public class ChartHelper
    {
        /// <summary>
        /// 画趋势图
        /// </summary>
        /// <param name="chart"></param>
        /// <param name="chartValue"></param>
        /// <returns></returns>
        public static Chart DrawTendencyChart(Chart chart, DataTable dt, string quota)
        {
            //获得坐标的最大值及最小值
            ChartValueModel chartValue = GetChartValueForTendency(dt, quota);

            //设置图表的坐标
            chart.ChartAreas.FirstOrDefault().AxisY.Interval = chartValue.Step;
            chart.ChartAreas.FirstOrDefault().AxisY.Maximum = chartValue.MaxOfCoord;
            chart.ChartAreas.FirstOrDefault().AxisY.Minimum = chartValue.MinOfCoord;

            //画上限、下限之间的格线
            for (double tempValue = chartValue.Min; tempValue < chartValue.Max; tempValue += chartValue.Step)
            {
                StripLine dash = new StripLine();
                dash.BackColor = System.Drawing.Color.Silver;
                dash.BorderDashStyle = ChartDashStyle.Dash;
                dash.IntervalOffset = Math.Round(tempValue,2);
                dash.StripWidth = 0.0001;
                chart.ChartAreas.FirstOrDefault().AxisY.StripLines.Add(dash);
            }

            //下限
            StripLine minLine = new StripLine();
            minLine.BackColor = Color.FromArgb(0, 90, 0);
            minLine.BorderDashStyle = ChartDashStyle.Dash;
            minLine.IntervalOffset = Convert.ToDouble(chartValue.Min);
            minLine.StripWidth = 0.0001;
            minLine.Text = chartValue.Min.ToString();
            minLine.TextAlignment = StringAlignment.Far;
            chart.ChartAreas.FirstOrDefault().AxisY.StripLines.Add(minLine);

            //上限
            StripLine maxLine = new StripLine();
            maxLine.BackColor = Color.FromArgb(0, 90, 0);
            maxLine.BorderDashStyle = ChartDashStyle.Dash;
            maxLine.IntervalOffset = Convert.ToDouble(chartValue.Max);
            maxLine.StripWidth = 0.0001;
            maxLine.Text = chartValue.Max.ToString();
            maxLine.TextAlignment = StringAlignment.Far;
            chart.ChartAreas.FirstOrDefault().AxisY.StripLines.Add(maxLine);

            //中值
            StripLine midLine = new StripLine();
            midLine.BackColor = Color.FromArgb(0, 90, 0);
            midLine.BorderDashStyle = ChartDashStyle.Dash;
            midLine.IntervalOffset = Convert.ToDouble(chartValue.Middle);
            midLine.StripWidth = 0.0001;
            midLine.Text = chartValue.Middle.ToString();
            midLine.TextAlignment = StringAlignment.Far;
            chart.ChartAreas.FirstOrDefault().AxisY.StripLines.Add(midLine);

            //去掉X轴的标签
            chart.ChartAreas.FirstOrDefault().AxisX.LabelStyle.Enabled = false;
            return chart;
        }

        // <summary>
        /// 画分布
        /// </summary>
        /// <param name="chart"></param>
        /// <param name="chartValue"></param>
        /// <returns></returns>
        public static Chart DrawDistributionChart(Chart chart, DataTable dt,Dictionary<double, int> dict, string quota)
        {
            //获得坐标的最大值及最小值
            ChartValueModel chartValue = GetChartValueForDistribution(dt, quota);

            //设置图表的坐标
            chart.ChartAreas.FirstOrDefault().AxisX.Interval = chartValue.Step;
            chart.ChartAreas.FirstOrDefault().AxisX.Maximum = chartValue.MaxOfCoord;
            chart.ChartAreas.FirstOrDefault().AxisX.Minimum = chartValue.MinOfCoord;

            //均值
            double avg = dt.AsEnumerable().Average(p => Convert.ToDouble(p[quota]));
            //均方差
            double dev = Math.Sqrt(dt.AsEnumerable().Average(p => Math.Pow(Convert.ToDouble(p[quota]) - avg, 2)));

            //标题
            Title title = new Title("3σ = " + 3 * Math.Round(dev, 6) + "\n" + " ¯x = " + Math.Round(avg, 6), Docking.Right);
            title.TextOrientation = TextOrientation.Horizontal;
            title.Alignment = ContentAlignment.TopRight;
            title.ForeColor = System.Drawing.Color.RoyalBlue;
            title.IsDockedInsideChartArea = true;
            title.DockedToChartArea = "ChartA";
            chart.Titles.Add(title);

            //高斯图形
            Series seriesGaussian = GetGaussianSeries(dict.Values.Max(), avg, dev);
            chart.Series.Add(seriesGaussian);

            //下限
            StripLine minLine = new StripLine();
            minLine.BackColor = System.Drawing.Color.Green;
            minLine.BorderDashStyle = ChartDashStyle.Dash;
            minLine.IntervalOffset = avg - 3 * dev;
            minLine.StripWidth = 0.0001;
            minLine.Text = "3σ";
            minLine.TextAlignment = StringAlignment.Far;
            minLine.TextOrientation = TextOrientation.Horizontal;
            chart.ChartAreas.FirstOrDefault().AxisX.StripLines.Add(minLine);

            //上限
            StripLine maxLine = new StripLine();
            maxLine.BackColor = System.Drawing.Color.Green;
            maxLine.BorderDashStyle = ChartDashStyle.Dash;
            maxLine.IntervalOffset = avg + 3 * dev;
            maxLine.StripWidth = 0.0001;
            maxLine.Text = "3σ";
            maxLine.TextAlignment = StringAlignment.Far;
            maxLine.TextOrientation = TextOrientation.Horizontal;
            chart.ChartAreas.FirstOrDefault().AxisX.StripLines.Add(maxLine);

            //中值
            StripLine midLine = new StripLine();
            midLine.BackColor = System.Drawing.Color.Orange;
            midLine.BorderDashStyle = ChartDashStyle.Dash;
            midLine.IntervalOffset = avg;
            midLine.StripWidth = 0.0001;
            midLine.Text = "¯x";
            midLine.TextOrientation = TextOrientation.Stacked;
            midLine.TextAlignment = StringAlignment.Far;
            chart.ChartAreas.FirstOrDefault().AxisX.StripLines.Add(midLine);            

            //去掉Y轴的标签
            chart.ChartAreas.FirstOrDefault().AxisY.LabelStyle.Enabled = false;
            return chart;
        }

        /// <summary>
        /// 趋势图-设置坐标及间隔
        /// </summary>
        /// <param name="dr"></param>
        /// <returns></returns>
        public static ChartValueModel GetChartValueForTendency(DataTable dt, string quota)
        {
            ChartValueModel chartValue = new ChartValueModel();
            DataRow dr = dt.AsEnumerable().FirstOrDefault();


            //取得最大值和最小值
            chartValue.Min = Convert.ToDouble(dr["MIN"]);
            chartValue.Max = Convert.ToDouble(dr["MAX"]);
            chartValue.Middle = Convert.ToDouble(chartValue.Min + chartValue.Max) / 2;

            //计算间隔
            chartValue.Step = (chartValue.Max - chartValue.Min) / 10.0;

            //计算最大坐标和最小坐标
            chartValue.MaxOfCoord = chartValue.Max + chartValue.Step * 5;
            chartValue.MinOfCoord = chartValue.Min - chartValue.Step * 5;
            
            return chartValue;
        }

        /// <summary>
        /// 分布图-设置坐标及间隔
        /// </summary>
        /// <param name="dr"></param>
        /// <returns></returns>
        public static ChartValueModel GetChartValueForDistribution(DataTable dt, string quota)
        {
            ChartValueModel chartValue = new ChartValueModel();
            DataRow dr = dt.AsEnumerable().FirstOrDefault();
            //取得最大值和最小值
            chartValue.Min = dt.AsEnumerable().Min(p => Convert.ToDouble(p[quota]));
            chartValue.Max = dt.AsEnumerable().Max(p => Convert.ToDouble(p[quota]));

            //计算间隔
            chartValue.Step = double.Parse(dr["STEP"].ToString());

            //计算最大坐标和最小坐标
            chartValue.MaxOfCoord = chartValue.Max + chartValue.Step * 3;
            chartValue.MinOfCoord = chartValue.Min - chartValue.Step * 3;

            return chartValue;
        }

        /// <summary>
        /// 获取高斯函数图形
        /// </summary>
        /// <param name="yMax">Y轴最大</param>
        /// <param name="avg">均值</param>
        /// <param name="dev">均方差</param>
        /// <returns></returns>
        private static Series GetGaussianSeries(int yMax, double avg, double dev)
        {
            #region 高斯算法
            List<GaussianModel> gass = new List<GaussianModel>();
            double min = avg - 6 * dev;
            double max = avg + 6 * dev;
            double step = (max - min) / 100;
            if (step == 0)
                step = Int32.MaxValue;
            for (double m = min; m <= max; m += step)
            {
                GaussianModel model = new GaussianModel();
                double y = (1 / (Math.Sqrt(2 * Math.PI) * dev)) * Math.Pow(Math.E, 0 - (Math.Pow(m - avg, 2) / (2 * Math.Pow(dev, 2))));
                model.X = m;
                model.Y = y;
                gass.Add(model);
            }
            #endregion

            #region 画高斯图形
            Series seriesGass = new Series();
            seriesGass.ChartType = SeriesChartType.Spline;
            double pram = 0;
            double temp = 0;
            pram = yMax * 1.1;
            //double gassMax = gass[50].Y;
            double gassMax = gass.Max<GaussianModel>(o => o.Y);
            temp = pram / gassMax;
            foreach (GaussianModel item in gass)
            {
                seriesGass.Points.AddXY(item.X, temp * item.Y);
            }
            #endregion

            return seriesGass;
        }

        /// <summary>
        /// 取Bar图的所有坐标
        /// </summary>
        /// <param name="dr"></param>
        /// <returns></returns>
        public static Dictionary<double, int> GetBarValue(DataTable dt, string quota)
        {
            Type type = dt.Columns[quota].DataType;
            Dictionary<double, int> dict = new Dictionary<double, int>();
            double min = dt.AsEnumerable().Min(p => Convert.ToDouble(p[quota]));
            double max = dt.AsEnumerable().Max(p => Convert.ToDouble(p[quota]));
            double step = Convert.ToDouble(dt.AsEnumerable().FirstOrDefault()["STEP"]);

            for (double tempValue = min; tempValue <= max; tempValue += step)
            {
                var rowCount = dt.AsEnumerable().Where(p => Convert.ToDouble(p[quota]) > tempValue - step/2 && Convert.ToDouble(p[quota]) <= tempValue + step/2).Count();
                dict.Add(tempValue, rowCount);
            }
            return dict;
        }
    }
}