using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Forms.DataVisualization.Charting;

namespace FLY.ControlLibrary
{

    /// <summary>
    /// GraphRange.xaml 的交互逻辑
    /// IsAutoTarget, 自动目标值
    /// IsPercent,百分比显示,alarm = curr_target * Alarm/Target,   
    /// </summary>
    public partial class GraphRange : UserControl, IGraphRange, INotifyPropertyChanged
    {
        #region 私有成员变量
        int autotarget = 30;
        protected int AutoTarget
        {
            get
            {
                return autotarget;
            }
            set
            {
                if (autotarget != value) 
                {
                    autotarget = value;
                    NotifyPropertyChanged("AutoTarget");
                }
            }
        }

        #endregion
        #region  接口
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(GraphRange), new PropertyMetadata(
                "Title",
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("Title");
                })));
        public string Title
        {
            get
            {
                return GetValue(TitleProperty) as string;
            }
            set
            {
                    SetValue(TitleProperty, value);
            }
        }

        public static readonly DependencyProperty Title2Property =
            DependencyProperty.Register("Title2", typeof(string), typeof(GraphRange), new PropertyMetadata(
                "",
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("Title2");
                })));
        public string Title2
        {
            get
            {
                return GetValue(Title2Property) as string;
            }
            set
            {
                SetValue(Title2Property, value);
            }
        }

        /// <summary>
        /// 计算结果
        /// </summary>
        public static readonly DependencyProperty CalStateProperty =
            DependencyProperty.Register("CalState", typeof(string), typeof(GraphRange), new PropertyMetadata(
                "Max:null Min:null Avg:null 2σ:null",
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {

                })));
        public string CalState
        {
            get
            {
                return (string)GetValue(CalStateProperty);
            }
            set
            {
                SetValue(CalStateProperty, value);
            }
        }

        /// <summary>
        /// 空的数据,定义某个值为空,默认为 99999998
        /// </summary>
        public static readonly DependencyProperty EmptyValueProperty =
            DependencyProperty.Register("EmptyValue", typeof(int), typeof(GraphRange), new PropertyMetadata(
                99999998,
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("EmptyValue");
                })));
        public int EmptyValue
        {
            get
            {
                return (int)GetValue(EmptyValueProperty);
            }
            set
            {
                SetValue(EmptyValueProperty, value);
            }
        }

        /// <summary>
        /// 放大倍数, 目标值= Target/放大倍数
        /// </summary>
        public static readonly DependencyProperty MultiProperty =
            DependencyProperty.Register("Multi", typeof(int), typeof(GraphRange), new PropertyMetadata(
                100,
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("Multi");
                })));

        public int Multi
        {
            get
            {
                return (int)GetValue(MultiProperty);
            }
            set
            {
                SetValue(MultiProperty, value);
            }
        }

        /// <summary>
        /// %显示
        /// </summary>
        public static readonly DependencyProperty IsPercentProperty =
            DependencyProperty.Register("IsPercent", typeof(bool), typeof(GraphRange), new PropertyMetadata(
                false,
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("IsPercent");
                })));
        public bool IsPercent
        {
            get
            {
                return (bool)GetValue(IsPercentProperty);
            }
            set
            {
                SetValue(IsPercentProperty, value);
            }
        }

        /// <summary>
        /// 自动目标值
        /// </summary>
        public static readonly DependencyProperty IsAutoTargetProperty =
            DependencyProperty.Register("IsAutoTarget", typeof(bool), typeof(GraphRange), new PropertyMetadata(
                false,
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("IsAutoTarget");
                })));
        public bool IsAutoTarget
        {
            get
            {
                return (bool)GetValue(IsAutoTargetProperty);
            }
            set
            {
                SetValue(IsAutoTargetProperty, value);
            }
        }


        public static readonly DependencyProperty TypeProperty =
            DependencyProperty.Register("Type", typeof(GraphRangeType), typeof(GraphRange), new PropertyMetadata(
                GraphRangeType.Sigma2,
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("Type");
                })));
        public GraphRangeType Type
        {
            get
            {
                return (GraphRangeType)GetValue(TypeProperty);
            }
            set
            {
                SetValue(TypeProperty, value);
            }
        }


        public static readonly DependencyProperty XAxisTypeProperty =
            DependencyProperty.Register("XAxisType", typeof(GraphRangeXAxisType), typeof(GraphRange), new PropertyMetadata(
        GraphRangeXAxisType.Time,
        new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as GraphRange).NotifyPropertyChanged("XAxisType");
        })));
        public GraphRangeXAxisType XAxisType
        {
            get
            {
                return (GraphRangeXAxisType)GetValue(XAxisTypeProperty);
            }
            set
            {
                SetValue(XAxisTypeProperty, value);
            }
        }


        /// <summary>
        /// 目标值
        /// </summary>
        public static readonly DependencyProperty TargetProperty =
            DependencyProperty.Register("Target", typeof(int), typeof(GraphRange), new PropertyMetadata(
                3000,
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {

                    (d as GraphRange).NotifyPropertyChanged("Target");
                })));
        public int Target
        {
            get
            {
                return (int)GetValue(TargetProperty);
            }
            set
            {
                SetValue(TargetProperty, value);
            }
        }

        /// <summary>
        /// 报警值
        /// </summary>
        public static readonly DependencyProperty AlarmProperty =
            DependencyProperty.Register("Alarm", typeof(int), typeof(GraphRange), new PropertyMetadata(
                1000,
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("Alarm");
                })));
        public int Alarm
        {
            get
            {
                return (int)GetValue(AlarmProperty);
            }
            set
            {
                SetValue(AlarmProperty, value);
            }
        }

        /// <summary>
        /// 控制线
        /// </summary>
        public static readonly DependencyProperty HasCtrlLineProperty =
            DependencyProperty.Register("HasCtrlLine", typeof(bool), typeof(GraphRange), new PropertyMetadata(
                false,
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("HasCtrlLine");
                })));
        public bool HasCtrlLine
        {
            get
            {
                return (bool)GetValue(HasCtrlLineProperty);
            }
            set
            {
                SetValue(HasCtrlLineProperty, value);
            }

        }

        /// <summary>
        /// 控制线
        /// </summary>
        public static readonly DependencyProperty CtrlLineProperty =
            DependencyProperty.Register("CtrlLine", typeof(int), typeof(GraphRange), new PropertyMetadata(
                1000,
                new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    (d as GraphRange).NotifyPropertyChanged("CtrlLine");
                })));
        public int CtrlLine
        {
            get
            {
                return (int)GetValue(CtrlLineProperty);
            }
            set
            {
                SetValue(CtrlLineProperty, value);
            }

        }

        /// <summary>
        /// Y轴是 Alarm 的N倍 默认是 3倍 
        /// </summary>
        public static readonly DependencyProperty YRangePercentProperty =
            DependencyProperty.Register("YRangePercent", typeof(double), typeof(GraphRange), new PropertyMetadata(
            3.0,
            new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {

                (d as GraphRange).NotifyPropertyChanged("YRangePercent");
            })));
        public double YRangePercent
        {
            get
            {
                return (double)GetValue(YRangePercentProperty);
            }
            set
            {
                SetValue(YRangePercentProperty, value);
            }
        }




        public static readonly DependencyProperty DataCntProperty =
            DependencyProperty.Register("DataCnt", typeof(int), typeof(GraphRange), new PropertyMetadata(
            80,
            new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                (d as GraphRange).NotifyPropertyChanged("DataCnt");
            })));
        public int DataCnt
        {
            get
            {
                return (int)GetValue(DataCntProperty);
            }
            set
            {
                SetValue(DataCntProperty, value);
            }
        }


        /// <summary>
        /// 数据源
        /// </summary>
        private ObservableCollection<GraphRangeData> _datasource;
        public ObservableCollection<GraphRangeData> DataSource
        {
            get
            {
                return _datasource;
            }
            set
            {
                if (_datasource != null)
                    _datasource.CollectionChanged -= _dataSource_CollectionChanged;

                _datasource = value;

                _datasource.CollectionChanged += _dataSource_CollectionChanged;
                mRefresh.UpdateData = true;
            }
        }
        System.Collections.Specialized.NotifyCollectionChangedEventHandler _dataSource_CollectionChanged;

        public event EventHandler ShowSettingDialogEvent;
        #endregion

        #region 刷新率控制
        class Refresh
        {
            /// <summary>
            /// 控制刷新率
            /// </summary>
            System.Windows.Threading.DispatcherTimer timer;
            bool updatedata_flag = false;
            bool updatey_flag = false;
            bool updatex_flag = false;
            bool updatecalx_flag = false;


            /// <summary>
            /// 最快0.1s 刷新一次
            /// </summary>
            TimeSpan REFRESH_RATE = TimeSpan.FromSeconds(0.1);

            public Refresh(GraphRange graph)
            {
                timer = new System.Windows.Threading.DispatcherTimer();
                timer.Tick += new EventHandler(delegate(object sender, EventArgs e)
                {
                    if (UpdateData)
                    {
                        UpdateData = false;
                        graph.UpdateAutoTarget();
                        graph.DataBindAll();
                    }
                    if (UpdateY)
                    {
                        UpdateY = false;
                        graph.UpdateAxisY();
                    }
                    if (UpdateX)
                    {
                        UpdateX = false;
                        graph.UpdateXRange();
                        graph.UpdateXType();
                    }
                    timer.Stop();
                });

                timer.Interval = REFRESH_RATE;
                timer.Start();
            }

            public bool UpdateData
            {
                get { return updatedata_flag; }
                set
                {
                    updatedata_flag = value;
                    if (value)
                    {
                        if (!timer.IsEnabled)
                            timer.Start();
                    }
                }
            }
            public bool UpdateY
            {
                get { return updatey_flag; }
                set
                {
                    updatey_flag = value;
                    if (value)
                    {
                        if (!timer.IsEnabled)
                            timer.Start();
                    }
                }
            }
            public bool UpdateX
            {
                get { return updatex_flag; }
                set
                {
                    updatex_flag = value;
                    if (value)
                    {
                        if (!timer.IsEnabled)
                            timer.Start();
                    }
                }
            }
            public bool UpdateCalX
            {
                get { return updatecalx_flag; }
                set
                {
                    updatecalx_flag = value;
                    if (value)
                    {
                        if (!timer.IsEnabled)
                            timer.Start();
                    }
                }
            }

        }
        Refresh mRefresh;
        #endregion

        public GraphRange()
        {
            InitializeComponent();
            InitializeComponent2();

            _dataSource_CollectionChanged = new System.Collections.Specialized.NotifyCollectionChangedEventHandler(dataSource_CollectionChanged);

            this.PropertyChanged += new PropertyChangedEventHandler(GraphRange_PropertyChanged);
            mRefresh = new Refresh(this);
            mRefresh.UpdateX = true;
            mRefresh.UpdateY = true;

            CheckDataSourceEmpty();

            run_title.Text = Title;
            run_title2.Text = Title2;

        }

        void GraphRange_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ((e.PropertyName == "Target") ||
                (e.PropertyName == "Alarm") ||
                (e.PropertyName == "YRangePercent") ||
                (e.PropertyName == "AutoTarget") ||
                (e.PropertyName == "IsAutoTarget") ||
                (e.PropertyName == "IsPercent") ||
                (e.PropertyName == "Type"))
            {
                mRefresh.UpdateY = true;
                mRefresh.UpdateData = true;
            }
            else if ((e.PropertyName == "DataCnt") ||
                (e.PropertyName == "XAxisType"))
            {
                mRefresh.UpdateX = true;
            }
            else if (e.PropertyName == "Title") {
                run_title.Text = Title;
            }
            else if (e.PropertyName == "Title2")
            {
                run_title2.Text = Title2;
            }
        }

        void InitializeComponent2()
        {
            System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
            System.Windows.Forms.DataVisualization.Charting.Series series_sigma = new System.Windows.Forms.DataVisualization.Charting.Series();
            System.Windows.Forms.DataVisualization.Charting.Series series_avg = new System.Windows.Forms.DataVisualization.Charting.Series();

            chart1.Name = "chart1";
            chart1.BackColor = System.Drawing.Color.Transparent;

            // 
            // chart1
            //---------------------------------------------------------------------------------------
            chartArea1.Name = "chartArea1";
            chartArea1.AxisX.LabelStyle.Font = new System.Drawing.Font("Trebuchet MS", 8.25F, System.Drawing.FontStyle.Bold);
            chartArea1.AxisX.LineColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
            chartArea1.AxisX.MajorGrid.LineColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
            chartArea1.AxisX.IsMarksNextToAxis = false;
            chartArea1.AxisX.IsLabelAutoFit = true;
            chartArea1.AxisX.LabelAutoFitStyle = LabelAutoFitStyles.DecreaseFont;
            chartArea1.AxisX.IntervalAutoMode = System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode.VariableCount;

            chartArea1.AxisY.LabelStyle.Font = new System.Drawing.Font("Trebuchet MS", 8.25F, System.Drawing.FontStyle.Bold);
            chartArea1.AxisY.LineColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
            chartArea1.AxisY.MajorGrid.LineColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));

            chartArea1.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
            chartArea1.BorderDashStyle = System.Windows.Forms.DataVisualization.Charting.ChartDashStyle.Solid;

            
            
            //chartArea1.Position.Height = 100;// 42F;
            //chartArea1.Position.Width = 100;// 88F;
            //chartArea1.Position.X = 0;// 3F;
            //chartArea1.Position.Y = 0;// 10F;

            chart1.ChartAreas.Add(chartArea1);

            //---------------------------------------------------------------------------------------
            series_sigma.Name = "series_sigma";
            series_sigma.ChartArea = chartArea1.Name;

            series_sigma.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.RangeColumn;
            series_sigma.Color = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(65)))), ((int)(((byte)(140)))), ((int)(((byte)(240)))));


            series_sigma.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(26)))), ((int)(((byte)(59)))), ((int)(((byte)(105)))));
            series_sigma.BorderWidth = 2;

            series_sigma.ShadowColor = System.Drawing.Color.Black;
            series_sigma.ShadowOffset = 2;
            series_sigma.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
            series_sigma.YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Double;
            chart1.Series.Add(series_sigma);

            //---------------------------------------------------------------------------------------
            series_avg.Name = "series_avg";
            series_avg.ChartArea = chartArea1.Name;

            series_avg.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Spline;
            series_avg.Color = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(65)))), ((int)(((byte)(140)))), ((int)(((byte)(240)))));
            series_avg.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(26)))), ((int)(((byte)(59)))), ((int)(((byte)(105)))));
            series_avg.BorderWidth = 3;
            series_avg.ShadowColor = System.Drawing.Color.Black;
            series_avg.ShadowOffset = 2;
            series_avg.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32;
            series_avg.YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Double;
            chart1.Series.Add(series_avg);

            //chart1.PrePaint += new EventHandler<ChartPaintEventArgs>(Chart1_PrePaint);

            this.chart1.PrePaint += new System.EventHandler<System.Windows.Forms.DataVisualization.Charting.ChartPaintEventArgs>(Chart1_PrePaint);
        }

        public void UpdateCtrlLine()
        {
            //chart1.Invalidate();
        }

        protected virtual  void Chart1_PrePaint(object sender, System.Windows.Forms.DataVisualization.Charting.ChartPaintEventArgs e)
        {
            //if (e.ChartElement is ChartArea)
            //{
            //    ChartArea area = (ChartArea)e.ChartElement;
            //    if (area.Name == "chartArea1")
            //    {
            //        if (!HasCtrlLine)
            //            return;
            //        double c1 = ((double)(Target+CtrlLine)) / Multi;
            //        double c2 = ((double)(Target-CtrlLine)) / Multi;

            //        if (c1 > area.AxisY.Maximum || c2 < area.AxisY.Minimum)
            //            return;

            //        // Take Graphics object from chart
            //        System.Drawing.Graphics graph = e.ChartGraphics.Graphics;



            //        // Convert X and Y values to screen position
            //        c1 = e.ChartGraphics.GetPositionFromAxis(area.Name, AxisName.Y, c1);
            //        c2 = e.ChartGraphics.GetPositionFromAxis(area.Name, AxisName.Y, c2);


            //        double XMin = e.ChartGraphics.GetPositionFromAxis(area.Name, AxisName.X, area.AxisX.Minimum);
            //        double XMax = e.ChartGraphics.GetPositionFromAxis(area.Name, AxisName.X, area.AxisX.Maximum);
                    
                    
                    
            //        // Draw Maximum trangle
            //        System.Drawing.Pen darkPen = new System.Drawing.Pen(System.Drawing.Color.DarkRed, 2);

            //        System.Drawing.PointF[] points = new System.Drawing.PointF[2];
            //        points[0].X = (float)XMin;
            //        points[1].X = (float)XMax;
            //        points[0].Y = points[1].Y = (float)c1;

            //        // Convert relative coordinates to absolute coordinates.
            //        points[0] = e.ChartGraphics.GetAbsolutePoint(points[0]);
            //        points[1] = e.ChartGraphics.GetAbsolutePoint(points[1]);

            //        graph.DrawLine(darkPen, points[0], points[1]);

            //        points[0].X = (float)XMin;
            //        points[1].X = (float)XMax;
            //        points[0].Y = points[1].Y = (float)c2;

            //        // Convert relative coordinates to absolute coordinates.
            //        points[0] = e.ChartGraphics.GetAbsolutePoint(points[0]);
            //        points[1] = e.ChartGraphics.GetAbsolutePoint(points[1]);

            //        graph.DrawLine(darkPen, points[0], points[1]);
            //    }
            //}
        }

        void dataSource_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            mRefresh.UpdateData = true;
        }

        #region 绑定画图
        void DataBindAll() 
        {
            CheckDataSourceEmpty();
            UpdateAutoTarget(); 
            switch (Type)
            {
                case GraphRangeType.All:
                    DataBindAll_all();
                    break;
                case GraphRangeType.Avg:
                    DataBindAll_avg();
                    break;
                case GraphRangeType.Sigma2:
                    DataBindAll_2sigma();
                    break;
            }
            UpdateXType();
            
        }

        #endregion
        void UpdateDataCalRange() 
        {
            switch (Type)
            {
                case GraphRangeType.All:
                    UpdateDataCalRange_avg();
                    break;
                case GraphRangeType.Avg:
                    UpdateDataCalRange_avg();
                    break;
                case GraphRangeType.Sigma2:
                    UpdateDataCalRange_2sigma();
                    break;
            }           
        }
        /// <summary>
        /// DataSource 里面的数据都是 EmptyValue 组成, 当修改数据时,应该clear ,再添加
        /// </summary>
        bool DataSourceIsEmpty = false;
        /// <summary>
        /// 当一个数据都没有时,需要添加空数据
        /// </summary>
        protected void CheckDataSourceEmpty()
        {
            if ((DataSource == null) || (DataSource.Count() == 0))
            {
                chart1.Series["series_avg"].Points.Clear();
                chart1.Series["series_sigma"].Points.Clear();
                chart1.Series["series_sigma"].Points.AddXY(0, 10,-10);
                chart1.Series["series_sigma"].Points[0].IsEmpty = true;
                DataSourceIsEmpty = true;
            }
            else
            {
                DataSourceIsEmpty = false;
            }
        }

        public void Button_Click(object sender, RoutedEventArgs e)
        {
            if (ShowSettingDialogEvent != null)
                ShowSettingDialogEvent(this, new EventArgs());
        }


        /// <summary>
        /// 与 FirstBoltNo,FirstIndex,LastIndex 相关
        /// </summary>
        void UpdateXRange()
        {
            // Set manual minimum and maximum values.
            chart1.ChartAreas["chartArea1"].AxisX.Minimum = 0;
            chart1.ChartAreas["chartArea1"].AxisX.Maximum = DataCnt;
        }
        void UpdateXType()
        {
            UpdateDataCalRange();

            if (DataSource == null)
                return;

            switch (XAxisType) 
            {
                case GraphRangeXAxisType.Index:
                    {
                        for (int i = 0; i < DataSource.Count(); i++)
                        {
                            if (i < chart1.Series["series_sigma"].Points.Count)
                            {
                                DataPoint p = chart1.Series["series_sigma"].Points[i];
                                p.AxisLabel = null;
                            }
                            if (i < chart1.Series["series_avg"].Points.Count)
                            {
                                DataPoint p = chart1.Series["series_avg"].Points[i];
                                p.AxisLabel = null;
                            }

                        }
                    }break;
                case GraphRangeXAxisType.Time:
                    {
                        for (int i = 0; i < DataSource.Count(); i++)
                        {
                            if (i < chart1.Series["series_sigma"].Points.Count)
                            {
                                DataPoint p = chart1.Series["series_sigma"].Points[i];
                                p.AxisLabel = DataSource[i].Time.ToString("HH:mm");
                            }
                            if (i < chart1.Series["series_avg"].Points.Count)
                            {
                                DataPoint p = chart1.Series["series_avg"].Points[i];
                                p.AxisLabel = DataSource[i].Time.ToString("HH:mm");
                            }
                        }
                    }break;
            }
        }

        void UpdateAutoTarget() 
        {
            AutoTarget = Target;
            if (DataSource == null)
                return;
            for (int i = 0; i < DataSource.Count(); i++)
            {
                if (DataSource[DataSource.Count - 1 - i].Avg != EmptyValue)
                {
                    AutoTarget = DataSource[DataSource.Count - 1 - i].Avg;
                    break;
                }
            }
        }
        void UpdateAxisY()
        {
            switch(Type)
            {
                case GraphRangeType.Sigma2:
                    UpdateAxisY_2sigma();
                    break;
                case GraphRangeType.All:
                    UpdateAxisY_all();
                    break;
                case GraphRangeType.Avg:
                    UpdateAxisY_avg();
                    break;
            }
        }
        protected void SetCustomLabel(Axis axisY, double p, string s, GridTickTypes gt, System.Drawing.Color c)
        {
            double multi = Multi;

            double r = axisY.Maximum - axisY.Minimum;
            double f, t;
            f = p + r / 2;
            t = p - r / 2;

            CustomLabel customLabel = axisY.CustomLabels.Add(f, t, s);
            customLabel.GridTicks = gt;
            customLabel.ForeColor = c;

        }

        #region 2sigma

        protected virtual void SetCustomLabel_2sigma(Axis axisY, double p, GridTickTypes gt, System.Drawing.Color c, bool ispercent, string message)
        {
            double multi = Multi;

            double r = axisY.Maximum - axisY.Minimum;
            double f, t;
            f = p + r / 2;
            t = p - r / 2;

            CustomLabel customLabel;
            string str;
            if (!ispercent)
            {
                str = p.ToString("N1");
            }
            else
            {
                str = p.ToString("N1") + "%";
            }
            if (!string.IsNullOrEmpty(message))
                str = message + " " + str;

            customLabel = axisY.CustomLabels.Add(f, t, str);

            customLabel.GridTicks = gt;
            customLabel.ForeColor = c;
        }


        protected virtual void UpdateAxisY_2sigma()
        {
            double multi = Multi;
            double alarm;
            double yrange;

            if (IsPercent)
            {
                alarm = 100.0 * Alarm / Target;
                yrange = alarm * YRangePercent;
            }
            else
            {
                alarm = Alarm / multi;
                yrange = alarm * YRangePercent;
            }
            if ((alarm < 0.1) || (yrange<0.3))
            {
                alarm = 0.1;
                yrange = 0.3;
            }
            Axis axisY = chart1.ChartAreas["chartArea1"].AxisY;

            //TODO
            axisY.Crossing = 0;

            // Set manual minimum and maximum values.
            axisY.Minimum = -yrange;
            axisY.Maximum = yrange;

            axisY.CustomLabels.Clear();

            //上下限
            SetCustomLabel_2sigma(axisY, alarm, GridTickTypes.All, System.Drawing.Color.DarkGoldenrod, IsPercent, null);
            SetCustomLabel_2sigma(axisY, -alarm, GridTickTypes.All, System.Drawing.Color.DarkGoldenrod, IsPercent, null);

            //2倍上下限
            SetCustomLabel_2sigma(axisY, alarm * 2, GridTickTypes.All, System.Drawing.Color.Red, IsPercent, null);
            SetCustomLabel_2sigma(axisY, -alarm * 2, GridTickTypes.All, System.Drawing.Color.Red, IsPercent, null);
            
            
            //SetCustomLabel_2sigma(axisY, axisY.Maximum, GridTickTypes.All, System.Drawing.Color.Black, IsPercent, null);
            //SetCustomLabel_2sigma(axisY, axisY.Minimum, GridTickTypes.All, System.Drawing.Color.Black, IsPercent, null);

            //显示范围数值
            if ((yrange != alarm) && (yrange != 2 * alarm))
            {
                SetCustomLabel_2sigma(axisY, axisY.Minimum, GridTickTypes.TickMark, System.Drawing.Color.Black, IsPercent, null);
                SetCustomLabel_2sigma(axisY, axisY.Maximum, GridTickTypes.TickMark, System.Drawing.Color.Black, IsPercent, null);
            }

            //显示范围太小,多加纵坐标
            if (yrange <= alarm)
            {
                SetCustomLabel_2sigma(axisY, (axisY.Maximum + axisY.Minimum) / 2 - (axisY.Maximum - axisY.Minimum) / 4, GridTickTypes.All, System.Drawing.Color.Black, IsPercent, null);
                SetCustomLabel_2sigma(axisY, (axisY.Maximum + axisY.Minimum) / 2 + (axisY.Maximum - axisY.Minimum) / 4, GridTickTypes.All, System.Drawing.Color.Black, IsPercent, null);
            }

        }
        protected virtual void UpdateOnePoint_2sigma(int index)
        {
            double multi = Multi;
            int emptyvalue = EmptyValue;
            DataPoint point;

            double sigma2 = 2 * DataSource[index].Sigma / multi;
            double avg = DataSource[index].Avg / multi;

            if (IsPercent)
            {
                if (avg <= 0)
                {
                    //异常
                    sigma2 = 0;
                }
                else
                {
                    sigma2 = sigma2 / avg * 100;
                }
            }
            if (index >= chart1.Series["series_sigma"].Points.Count)
            {
                chart1.Series["series_sigma"].Points.AddXY(index, sigma2, -sigma2);
                point = chart1.Series["series_sigma"].Points[index];
            }
            else
            {
                point = chart1.Series["series_sigma"].Points[index];
                point.YValues[0] = sigma2;
                point.YValues[1] = -sigma2;
                point.XValue = index;
            }

            //设定空数据
            if (DataSource[index].Avg == emptyvalue)
            {
                point.IsEmpty = true;
            }
            else
            {
                point.IsEmpty = false;

                double threshold1;
                double threshold2;

                if (IsPercent)
                    threshold1 = 100.0 * Alarm / Target;
                else
                    threshold1 = Alarm / multi;
                threshold2 = threshold1 * 2;
                System.Drawing.Color c;
                if (sigma2 > threshold2)
                    c = System.Drawing.Color.Red;
                else if (sigma2 > threshold1)
                    c = System.Drawing.Color.Orange;
                else
                    c = System.Drawing.Color.Green;

                point.BorderColor = c;
                point.Color = c;
            }
        }
        void DataBindAll_2sigma()
        {
            chart1.Series["series_avg"].Points.Clear();
            chart1.Series["series_sigma"].Points.Clear();
            if (DataSource != null)
            {
                for (int i = 0; i < DataSource.Count(); i++)
                {
                    UpdateOnePoint_2sigma(i);
                }
            }
            CheckDataSourceEmpty();
        }



        /// <summary>
        /// 与 FirstBoltNo,DataFirst,DataLast 相关
        /// </summary>
        void UpdateDataCalRange_2sigma()
        {
            System.Windows.Forms.DataVisualization.Charting.Axis axisX = chart1.ChartAreas["chartArea1"].AxisX;

            axisX.CustomLabels.Clear();
        }
        #endregion

        #region all
        void UpdateAxisY_all()
        {
            chart1.Series["series_avg"].ChartType = SeriesChartType.Spline;
            UpdateAxisY_avg();
        }
        protected virtual void UpdateOnePoint_all(int index)
        {
            double multi = Multi;
            int emptyvalue = EmptyValue;
            DataPoint point_sigma;
            DataPoint point_avg;

            double sigma2 = 2 * DataSource[index].Sigma / multi;
            double avg = DataSource[index].Avg / multi;


            if (index >= chart1.Series["series_sigma"].Points.Count)
            {
                chart1.Series["series_sigma"].Points.AddXY(index, avg + sigma2, avg - sigma2);
                point_sigma = chart1.Series["series_sigma"].Points[index];

                chart1.Series["series_avg"].Points.AddXY(index, avg);
                point_avg = chart1.Series["series_avg"].Points[index];
            }
            else
            {
                point_sigma = chart1.Series["series_sigma"].Points[index];
                point_sigma.YValues[0] = avg + sigma2;
                point_sigma.YValues[1] = avg - sigma2;
                point_sigma.XValue = index;

                point_avg = chart1.Series["series_avg"].Points[index];
                point_avg.YValues[0] = avg;
                point_avg.XValue = index;
            }

            //设定空数据
            if (DataSource[index].Avg == emptyvalue)
            {
                point_sigma.IsEmpty = true;
                point_avg.IsEmpty = true;
            }
            else
            {
                point_sigma.IsEmpty = false;

                double alarm;
                double target;
                double yrange;
                bool ispercent;

                GetActualTargetAlarm(out target, out alarm, out yrange, out ispercent);

                double threshold1 = alarm;
                double threshold2 = alarm * 2;


                System.Drawing.Color c;
                if (((avg + sigma2) > (target + threshold2)) || ((avg + sigma2) < (target - threshold2)))
                {
                    c = System.Drawing.Color.Red;
                }
                else if (((avg - sigma2) > (target + threshold2)) || ((avg - sigma2) < (target - threshold2)))
                {
                    c = System.Drawing.Color.Red;
                }
                else if (((avg + sigma2) > (target + threshold1)) || ((avg + sigma2) < (target - threshold1)))
                {
                    c = System.Drawing.Color.Orange;
                }
                else if (((avg - sigma2) > (target + threshold1)) || ((avg - sigma2) < (target - threshold1)))
                {
                    c = System.Drawing.Color.Orange;
                }
                else
                {
                    c = System.Drawing.Color.Green;
                }

                point_sigma.BorderColor = c;
                point_sigma.Color = c;

                point_avg.Color = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(65)))), ((int)(((byte)(140)))), ((int)(((byte)(240)))));
                point_avg.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(26)))), ((int)(((byte)(59)))), ((int)(((byte)(105)))));


            }
        }
        void DataBindAll_all()
        {
            chart1.Series["series_avg"].Points.Clear();
            chart1.Series["series_sigma"].Points.Clear();
            if (DataSource != null)
            {
                for (int i = 0; i < DataSource.Count(); i++)
                {
                    UpdateOnePoint_all(i);
                }
            }
            CheckDataSourceEmpty();
            UpdateCalState_all();
        }

        /// <summary>
        /// 与 Multi,EmptyValue,DataFirst,DataLast 相关
        /// </summary>
        void UpdateCalState_all()
        {
            int emptyvalue = EmptyValue;
            double multi = Multi;

            if (DataSource == null)
            {
                goto _err;
            }

            string title;
            var dats_max = from data in DataSource where data.Avg != emptyvalue select data.Avg+data.Sigma*2;
            var dats_min = from data in DataSource where data.Avg != emptyvalue select data.Avg - data.Sigma * 2;
            List<int> dats = new List<int>();
            dats.AddRange(dats_max);
            dats.AddRange(dats_min);
            if (dats.Count() == 0)
            {
                goto _err;
            }
            else
            {
                //AutoTarget = (int)dats.Average();

                double avg = dats.Average() / multi;
                double max = dats.Max() / multi;
                double min = dats.Min() / multi;

                double sum = 0;
                foreach (int dat in dats)
                {
                    sum += Math.Pow(avg - (double)dat / multi, 2);
                }
                sum /= (dats.Count() - 1);
                double sigma = Math.Sqrt(sum);


                if (IsPercent)
                {
                    title = "Max:+" + ((max / avg * 100) - 100).ToString("N1") + "% " +
                        "Min:-" + (100 - (min / avg * 100)).ToString("N1") + "% " +
                        "Avg:" + avg.ToString("N1") + " " +
                        "2σ:" + (sigma / avg * 2 * 100).ToString("N1") + "%";
                }
                else
                {
                    title = "Max:" + max.ToString("N1") + " " +
                        "Min:" + min.ToString("N1") + " " +
                        "Avg:" + avg.ToString("N1") + " " +
                        "2σ:" + (sigma * 2).ToString("N1");
                }

            }
            goto _end;
        _err:
            //title = Properties.Resources.StrMax + ":" + "NULL " +
            //        Properties.Resources.StrMin+":"+"NULL "+
            //        Properties.Resources.StrAvg+":"+"NULL "+
            //        "3Sigma:"+"NULL ";
            title = "Max:null Min:null Avg:null 2σ:null";

        _end:



            CalState = title;
            UpdateDataCalRange_avg();
        }

        #endregion

        #region avg

        protected void SetCustomLabel(Axis axisY, double p, GridTickTypes gt, System.Drawing.Color c, bool ispercent, string message)
        {
            double multi = Multi;

            double r = axisY.Maximum - axisY.Minimum;
            double f, t;
            f = p + r / 2;
            t = p - r / 2;
            double target = (axisY.Maximum + axisY.Minimum) / 2;

            CustomLabel customLabel;
            string str;
            if (!ispercent)
            {
                str = p.ToString("N1");

            }
            else
            {
                if (target == p)
                {
                    str = p.ToString("N1");
                }
                else
                {
                    double percent;

                    percent = ((p - target) / target) * 100;
                    str = percent.ToString("N1") + "%";
                }
            }
            if (!string.IsNullOrEmpty(message))
                str = message + " " + str;

            customLabel = axisY.CustomLabels.Add(f, t, str);

            customLabel.GridTicks = gt;
            customLabel.ForeColor = c;

        }


        /// <summary>
        /// 获取实际应用的目标值,公差
        /// </summary>
        /// <param name="target"></param>
        /// <param name="alarm"></param>
        /// <param name="ispercent"></param>
        void GetActualTargetAlarm(out double target, out double alarm, out double yrange, out bool ispercent)
        {
            double multi = Multi;
            if (IsAutoTarget)
                target = AutoTarget / multi;//AutoTarget 计算得到的平均值
            else
                target = Target / multi;

            ispercent = IsPercent;
            if ((ispercent) && (Target <= 0) || (Alarm < 0) || (target <= 0))
                ispercent = false;

            if (ispercent)
            {
                alarm = target * Alarm / Target;
                yrange = alarm * YRangePercent;
            }
            else
            {
                alarm = Alarm / multi;
                yrange = alarm * YRangePercent;
            }

            if ((alarm < 0.1) || (yrange < 0.3))
            {
                alarm = 0.1;

                yrange = 0.3;
            }
        }
        protected virtual void UpdateAxisY_avg()
        {
            double alarm;
            double target;
            double yrange;
            bool ispercent;

            GetActualTargetAlarm(out target, out alarm, out yrange, out ispercent);

            Axis axisY = chart1.ChartAreas["chartArea1"].AxisY;

            axisY.Crossing = target;
            // Set manual minimum and maximum values.
            axisY.Minimum = target - yrange;// 3 * alarm;
            axisY.Maximum = target + yrange;// 3 * alarm;

            axisY.CustomLabels.Clear();
            //目标值
            SetCustomLabel(axisY, target, GridTickTypes.All, System.Drawing.Color.Green, false, null);

            //上下限
            SetCustomLabel(axisY, target + alarm, GridTickTypes.All, System.Drawing.Color.DarkGoldenrod, ispercent, null);
            SetCustomLabel(axisY, target - alarm, GridTickTypes.All, System.Drawing.Color.DarkGoldenrod, ispercent, null);
            
            //2倍上下限
            SetCustomLabel(axisY, target + alarm * 2, GridTickTypes.All, System.Drawing.Color.Red, ispercent, null);
            SetCustomLabel(axisY, target - alarm * 2, GridTickTypes.All, System.Drawing.Color.Red, ispercent, null);

            //显示范围数值
            if ((yrange != alarm) && (yrange != 2 * alarm))
            {
                SetCustomLabel(axisY, axisY.Minimum, GridTickTypes.TickMark, System.Drawing.Color.Black, ispercent, null);
                SetCustomLabel(axisY, axisY.Maximum, GridTickTypes.TickMark, System.Drawing.Color.Black, ispercent, null);
            }

            //显示范围太小,多加纵坐标
            if (yrange <= alarm)
            {
                SetCustomLabel(axisY, target - yrange / 2, GridTickTypes.All, System.Drawing.Color.Black, ispercent, null);
                SetCustomLabel(axisY, target + yrange / 2, GridTickTypes.All, System.Drawing.Color.Black, ispercent, null);
            }
        }
        protected virtual void UpdateOnePoint_avg(int index)
        {
            double multi = Multi;
            int emptyvalue = EmptyValue;
            DataPoint point_avg;

            double avg = DataSource[index].Avg / multi;




            if (index >= chart1.Series["series_avg"].Points.Count)
            {
                chart1.Series["series_avg"].Points.AddXY(index, avg);
                point_avg = chart1.Series["series_avg"].Points[index];
            }
            else
            {
                point_avg = chart1.Series["series_avg"].Points[index];
                point_avg.YValues[0] = avg;
                point_avg.XValue = index;
            }

            //设定空数据
            if (DataSource[index].Avg == emptyvalue)
            {
                point_avg.IsEmpty = true;
            }
            else
            {
                point_avg.IsEmpty = false;

                double alarm;
                double target;
                double yrange;
                bool ispercent;

                GetActualTargetAlarm(out target, out alarm, out yrange, out ispercent);

                double threshold1 = alarm;
                double threshold2 = alarm * 2;


                System.Drawing.Color c;
                if ((avg > (target + threshold2)) || (avg < (target - threshold2)))
                {
                    c = System.Drawing.Color.Red;
                }
                else if ((avg > (target + threshold1)) || (avg < (target - threshold1)))
                {
                    c = System.Drawing.Color.Orange;
                }
                else
                {
                    c = System.Drawing.Color.Green;
                }

                point_avg.BorderColor = c;
                point_avg.Color = c;
            }
        }
        void DataBindAll_avg()
        {
            chart1.Series["series_avg"].ChartType = SeriesChartType.Column;
            chart1.Series["series_avg"].Points.Clear();
            chart1.Series["series_sigma"].Points.Clear();
            if (DataSource != null)
            {
                for (int i = 0; i < DataSource.Count(); i++)
                {
                    UpdateOnePoint_avg(i);
                }
            }
            CheckDataSourceEmpty();
            UpdateCalState_avg();
        }

        /// <summary>
        /// 与 Multi,EmptyValue,DataFirst,DataLast 相关
        /// </summary>
        void UpdateCalState_avg()
        {
            int emptyvalue = EmptyValue;
            double multi = Multi;

            if (DataSource == null)
            {
                goto _err;
            }

            string title;
            var dats = from data in DataSource where data.Avg != emptyvalue select data.Avg;
            if (dats.Count() == 0)
            {
                goto _err;
            }
            else
            {
                //AutoTarget = (int)dats.Average();

                double avg = dats.Average() / multi;
                double max = dats.Max() / multi;
                double min = dats.Min() / multi;

                double sum = 0;
                foreach (int dat in dats)
                {
                    sum += Math.Pow(avg - (double)dat / multi, 2);
                }
                sum /= (dats.Count() - 1);
                double sigma = Math.Sqrt(sum);


                if (IsPercent)
                {
                    title = "Max:+" + ((max / avg * 100) - 100).ToString("N1") + "% " +
                        "Min:-" + (100 - (min / avg * 100)).ToString("N1") + "% " +
                        "Avg:" + avg.ToString("N1") + " " +
                        "2σ:" + (sigma / avg * 2 * 100).ToString("N1") + "%";
                }
                else
                {
                    title = "Max:" + max.ToString("N1") + " " +
                        "Min:" + min.ToString("N1") + " " +
                        "Avg:" + avg.ToString("N1") + " " +
                        "2σ:" + (sigma * 2).ToString("N1");
                }

            }
            goto _end;
        _err:
            //title = Properties.Resources.StrMax + ":" + "NULL " +
            //        Properties.Resources.StrMin+":"+"NULL "+
            //        Properties.Resources.StrAvg+":"+"NULL "+
            //        "3Sigma:"+"NULL ";
            title = "Max:null Min:null Avg:null 2σ:null";

        _end:



            CalState = title;
            UpdateDataCalRange_avg();
        }

        /// <summary>
        /// 与 FirstBoltNo,DataFirst,DataLast 相关
        /// </summary>
        void UpdateDataCalRange_avg()
        {
            System.Windows.Forms.DataVisualization.Charting.Axis axisX = chart1.ChartAreas["chartArea1"].AxisX;

            axisX.CustomLabels.Clear();

            System.Windows.Forms.DataVisualization.Charting.CustomLabel customlabel
                = new System.Windows.Forms.DataVisualization.Charting.CustomLabel(0,DataCnt,
                    CalState, 1,
                System.Windows.Forms.DataVisualization.Charting.LabelMarkStyle.LineSideMark);

            axisX.CustomLabels.Add(customlabel);
        }
        #endregion


        #region INotifyPropertyChanged 成员
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion



    }


    public interface IGraphRange
    {
        #region 附加属性
        /// <summary>
        /// 标题1
        /// </summary>
        string Title { get; set; }
        /// <summary>
        /// 标题2
        /// </summary>
        string Title2 { get; set; }

        /// <summary>
        /// 计算结果
        /// </summary>
        string CalState { get; set; }
        /// <summary>
        /// 空的数据,定义某个值为空,默认为 99999998
        /// </summary>
        int EmptyValue { get; set; }

        /// <summary>
        /// 放大倍数, 目标值= Target/放大倍数
        /// </summary>
        int Multi { get; set; }

        /// <summary>
        /// %显示
        /// </summary>
        bool IsPercent { get; set; }

        /// <summary>
        /// 自动目标值
        /// </summary>
        bool IsAutoTarget { get; set; }

        /// <summary>
        /// 图显示的内容, 
        /// 当为Sigma2, 只有 IsPercent 有效
        /// 当为Avg, 
        /// 当为All,  
        /// </summary>
        GraphRangeType Type { get; set; }

        /// <summary>
        /// 目标值
        /// </summary>
        int Target { get; set; }

        /// <summary>
        /// 报警值
        /// </summary>
        int Alarm { get; set; }

        /// <summary>
        /// 数据总量
        /// </summary>
        int DataCnt { get; set; }

        /// <summary>
        /// 数据,当 数据>数据总量, 只显示最后的数据!
        /// </summary>
        ObservableCollection<GraphRangeData> DataSource { get; set; }

        /// <summary>
        /// 设置事件
        /// </summary>
        event EventHandler ShowSettingDialogEvent;
        #endregion
    }
    public enum GraphRangeType 
    {
        /// <summary>
        /// 只显示 Sigma2 趋势图
        /// </summary>
        Sigma2,
        /// <summary>
        /// 只显示 平均值趋势图
        /// </summary>
        Avg,
        /// <summary>
        /// 平均值  与 Sigma2 趋势图
        /// </summary>
        All
    }
    /// <summary>
    /// X轴显示类型
    /// </summary>
    public enum GraphRangeXAxisType
    {
        /// <summary>
        /// 时间
        /// </summary>
        Time,
        /// <summary>
        /// 序号!!
        /// </summary>
        Index
    }
    public struct GraphRangeData 
    {
        public DateTime Time;
        public int Avg;
        public int Sigma;
        public bool InCtrl;//控制中
    }
}