using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
namespace DevComponents.DotNetBar.MicroCharts
{
    internal class LineMicroChart : MicroChartBase
    {
        public override void CreateChart(MicroChartRenderInfo info)
        {
            Graphics graphics = info.Graphics;
            int pointRadius = Dpi.Width(PointRadius);
            int chartHeight = info.ChartHeight - pointRadius * 2;
            int chartWidth = info.ChartWidth - pointRadius * 2;
            int drawStep = (int)Math.Max(1, chartWidth / (Math.Max(1, info.DataPoints.Count - 1)));
            int dataStep = Math.Max(1, ((info.DataPoints.Count * drawStep) / chartWidth));
            if (info.DataPoints.Count <= 2) dataStep = 1;
            int x = pointRadius;
            double dataPointMinValue = info.DataPointMinValue;
            double dataPointMaxValue = info.DataPointMaxValue;
            if (_MicroChartStyle.DrawZeroLine && dataPointMinValue > _MicroChartStyle.ZeroLineValue)
                dataPointMinValue = _MicroChartStyle.ZeroLineValue;
            if (_MicroChartStyle.DrawControlLine1)
            {
                if (dataPointMinValue > _MicroChartStyle.ControlLine1StartValue)
                    dataPointMinValue = _MicroChartStyle.ControlLine1StartValue;
                if (dataPointMinValue > _MicroChartStyle.ControlLine1EndValue)
                    dataPointMinValue = _MicroChartStyle.ControlLine1EndValue;
                if (dataPointMaxValue < _MicroChartStyle.ControlLine1StartValue)
                    dataPointMaxValue = _MicroChartStyle.ControlLine1StartValue;
                if (dataPointMaxValue < _MicroChartStyle.ControlLine1EndValue)
                    dataPointMaxValue = _MicroChartStyle.ControlLine1EndValue;
            }
            if (_MicroChartStyle.DrawControlLine2)
            {
                if (dataPointMinValue > _MicroChartStyle.ControlLine2StartValue)
                    dataPointMinValue = _MicroChartStyle.ControlLine2StartValue;
                if (dataPointMinValue > _MicroChartStyle.ControlLine2EndValue)
                    dataPointMinValue = _MicroChartStyle.ControlLine2EndValue;
                if (dataPointMaxValue < _MicroChartStyle.ControlLine2StartValue)
                    dataPointMaxValue = _MicroChartStyle.ControlLine2StartValue;
                if (dataPointMaxValue < _MicroChartStyle.ControlLine2EndValue)
                    dataPointMaxValue = _MicroChartStyle.ControlLine2EndValue;
            }
            
            double range = dataPointMaxValue - dataPointMinValue;
            if (range == 0) range = 1;
            int totalPoints = (int)Math.Ceiling((double)info.DataPoints.Count / dataStep);
            Point[] chartPoints = new Point[totalPoints];
            MicroChartHotPoint[] microHotPoints = new MicroChartHotPoint[totalPoints];
            Point lowPoint = Point.Empty, highPoint = Point.Empty;
            int index = 0;
            for (int i = 0; i < info.DataPoints.Count; i += dataStep)
            {
                double value = info.DataPoints[i];
                Point p = new Point(x, (int)Math.Min((int)(chartHeight * (1 - (value - dataPointMinValue) / range)), chartHeight - 1) + pointRadius);
                if (lowPoint.IsEmpty && value == info.DataPointMinValue)
                    lowPoint = p;
                else if (/*highPoint.IsEmpty &&*/ value == dataPointMaxValue)
                    highPoint = p;
                chartPoints[index] = p;
                microHotPoints[index] = new MicroChartHotPoint(GetHotPointBounds(p, info), new Rectangle(p.X - drawStep / 2, 0, drawStep, chartHeight), _MicroChartStyle.LineColor, value, index);
                index++;
                x += drawStep;
                if (x > chartWidth || i >= info.DataPoints.Count - dataStep * 2) x = chartWidth;
            }
            if (_MicroChartStyle.DrawAverageLine && !_MicroChartStyle.AverageLineColor.IsEmpty)
            {
                using (Pen pen = new Pen(_MicroChartStyle.AverageLineColor, Dpi.Width1))
                    graphics.DrawLine(pen, 0, chartHeight / 2, chartWidth, chartHeight / 2);
            }
            //if (_MicroChartStyle.DrawTrendLine && !_MicroChartStyle.TrendLineColor.IsEmpty)
            //{
            //    using (Pen pen = new Pen(_MicroChartStyle.TrendLineColor))
            //        graphics.DrawLine(pen, 0, (int)(chartHeight * (1 - (info.TrendInfo.Start - dataPointMinValue) / range)),
            //            chartWidth, (int)(chartHeight * (1 - (info.TrendInfo.End - dataPointMinValue) / range)));
            //}
            if (_MicroChartStyle.DrawZeroLine && !_MicroChartStyle.ZeroLineColor.IsEmpty)
            {
                using (Pen pen = new Pen(_MicroChartStyle.ZeroLineColor, Dpi.Width1))
                {
                    int y = Math.Min((int)(chartHeight * (1 - (_MicroChartStyle.ZeroLineValue - dataPointMinValue) / range)) + pointRadius, (chartHeight + pointRadius) - 1);
                    if (y < 0) y = 0;
                    graphics.DrawLine(pen, 0, y, chartWidth, y);
                }
            }
            if (_MicroChartStyle.DrawControlLine1 && !_MicroChartStyle.ControlLine1Color.IsEmpty)
            {
                using (Pen pen = new Pen(_MicroChartStyle.ControlLine1Color, Dpi.Width1))
                {
                    int y1 = Math.Min((int)(chartHeight * (1 - (_MicroChartStyle.ControlLine1StartValue - dataPointMinValue) / range)) + pointRadius, (chartHeight + pointRadius) - 1);
                    if (y1 < 0) y1 = 0;
                    int y2 = Math.Min((int)(chartHeight * (1 - (_MicroChartStyle.ControlLine1EndValue - dataPointMinValue) / range)) + pointRadius, (chartHeight + pointRadius) - 1);
                    if (y2 < 0) y2 = 0;
                    graphics.DrawLine(pen, 0, y1, chartWidth, y2);
                }
            }
            if (_MicroChartStyle.DrawControlLine2 && !_MicroChartStyle.ControlLine2Color.IsEmpty)
            {
                using (Pen pen = new Pen(_MicroChartStyle.ControlLine2Color, Dpi.Width1))
                {
                    int y1 = Math.Min((int)(chartHeight * (1 - (_MicroChartStyle.ControlLine2StartValue - dataPointMinValue) / range)) + pointRadius, (chartHeight + pointRadius) - 1);
                    if (y1 < 0) y1 = 0;
                    int y2 = Math.Min((int)(chartHeight * (1 - (_MicroChartStyle.ControlLine2EndValue - dataPointMinValue) / range)) + pointRadius, (chartHeight + pointRadius) - 1);
                    if (y2 < 0) y2 = 0;
                    graphics.DrawLine(pen, 0, y1, chartWidth, y2);
                }
            }
            if (chartPoints.Length > 1)
            {
                using (Pen pen = new Pen(_MicroChartStyle.LineColor, Dpi.Width1))
                    graphics.DrawLines(pen, chartPoints);
            }
            if (!lowPoint.IsEmpty && !_MicroChartStyle.LowPointColor.IsEmpty && chartPoints.Length > 0)
            {
                using (SolidBrush brush = new SolidBrush(_MicroChartStyle.LowPointColor))
                    graphics.FillPolygon(brush, GetChartPointBounds(lowPoint));
            }
            if (!highPoint.IsEmpty && !_MicroChartStyle.HighPointColor.IsEmpty && chartPoints.Length > 0)
            {
                using (SolidBrush brush = new SolidBrush(_MicroChartStyle.HighPointColor))
                    graphics.FillPolygon(brush, GetChartPointBounds(highPoint));
            }
            if (!_MicroChartStyle.FirstPointColor.IsEmpty && chartPoints.Length > 0)
            {
                using (SolidBrush brush = new SolidBrush(_MicroChartStyle.FirstPointColor))
                    graphics.FillPolygon(brush, GetChartPointBounds(chartPoints[0]));
            }
            if (!_MicroChartStyle.LastPointColor.IsEmpty && chartPoints.Length > 1)
            {
                using (SolidBrush brush = new SolidBrush(_MicroChartStyle.LastPointColor))
                    graphics.FillPolygon(brush, GetChartPointBounds(chartPoints[chartPoints.Length - 1]));
            }
            info.MicroChartHotPoints = microHotPoints;
        }
        private static int HotPointOffset
        {
            get { return Dpi.Width4; }
        }
        private Rectangle GetHotPointBounds(Point hotPoint, MicroChartRenderInfo info)
        {
            //Rectangle bounds = new Rectangle(Math.Min(info.ChartWidth - HotPointOffset * 2, Math.Max(-1, hotPoint.X - HotPointOffset)),
            //    Math.Min(info.ChartHeight - HotPointOffset * 2, Math.Max(-1, hotPoint.Y - HotPointOffset)),
            //    HotPointOffset * 2,
            //    HotPointOffset * 2);
            Rectangle bounds = new Rectangle(hotPoint.X - HotPointOffset,
                hotPoint.Y - HotPointOffset,
                HotPointOffset * 2,
                HotPointOffset * 2);
            return bounds;
        }
        private LineMicroChartStyle _MicroChartStyle;
        public LineMicroChartStyle Style
        {
            get { return _MicroChartStyle; }
            set { _MicroChartStyle = value; }
        }
    }
    /// 
    /// Defines the style for the line micro chart.
    /// 
    [System.ComponentModel.ToolboxItem(false), System.ComponentModel.DesignTimeVisible(false), TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter))]
    public class LineMicroChartStyle
    {
        private Color _LineColor = Color.Black;
        /// 
        /// Gets or sets the color of the chart line.
        /// 
        [Category("Appearance"), Description("Indicates color of chart line .")]
        public Color LineColor
        {
            get { return _LineColor; }
            set { _LineColor = value; OnStyleChanged(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeLineColor()
        {
            return _LineColor != Color.Black;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetLineColor()
        {
            this.LineColor = Color.Black;
        }
        private bool _DrawAverageLine = false;
        /// 
        /// Gets or sets whether average line is drawn.
        /// 
        [DefaultValue(false), Category("Appearance")]
        public bool DrawAverageLine
        {
            get { return _DrawAverageLine; }
            set
            {
                _DrawAverageLine = value;
                OnStyleChanged();
            }
        }
        private Color _AverageLineColor = ColorScheme.GetColor(0xFAC08F);
        /// 
        /// Gets or sets the color of the 
        /// 
        [Category("Columns"), Description("Indicates color of.")]
        public Color AverageLineColor
        {
            get { return _AverageLineColor; }
            set { _AverageLineColor = value; OnStyleChanged(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeAverageLineColor()
        {
            return _AverageLineColor != ColorScheme.GetColor(0xFAC08F);
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetAverageLineColor()
        {
            this.AverageLineColor = ColorScheme.GetColor(0xFAC08F);
        }
        //private bool _DrawTrendLine = false;
        //[DefaultValue(false), Category("Appearance")]
        //public bool DrawTrendLine
        //{
        //    get { return _DrawTrendLine; }
        //    set
        //    {
        //        _DrawTrendLine = value;
        //    }
        //}
        //private Color _TrendLineColor = ColorScheme.GetColor(0xBFBFBF);
        ///// 
        ///// Gets or sets the color of the trend line.
        ///// 
        //[Category("Columns"), Description("Indicates color of trend line.")]
        //public Color TrendLineColor
        //{
        //    get { return _TrendLineColor; }
        //    set { _TrendLineColor = value; }
        //}
        ///// 
        ///// Gets whether property should be serialized.
        ///// 
        //[EditorBrowsable(EditorBrowsableState.Never)]
        //public bool ShouldSerializeTrendLineColor()
        //{
        //    return !_TrendLineColor.IsEmpty;
        //}
        ///// 
        ///// Resets property to its default value.
        ///// 
        //[EditorBrowsable(EditorBrowsableState.Never)]
        //public void ResetTrendLineColor()
        //{
        //    this.TrendLineColor = Color.Empty;
        //}
        private bool _DrawZeroLine = true;
        /// 
        /// Gets or sets whether zero-line is drawn on chart.
        /// 
        [DefaultValue(true), Category("Appearance"), Description("Gets or sets whether zero-line is drawn on chart.")]
        public bool DrawZeroLine
        {
            get { return _DrawZeroLine; }
            set
            {
                _DrawZeroLine = value;
                OnStyleChanged();
            }
        }
        private double _ZeroLineValue = 0d;
        /// 
        /// Gets or sets the value of the zero line, i.e. where zero line is drawn. Default value is 0.
        /// 
        [DefaultValue(0d), Category("Appearance"), Description("Indicates value of the zero line, i.e. where zero line is drawn.")]
        public double ZeroLineValue
        {
            get { return _ZeroLineValue; }
            set
            {
                _ZeroLineValue = value;
                OnStyleChanged();
            }
        }
        private Color _ZeroLineColor = Color.Red;
        /// 
        /// Gets or sets the color of the 
        /// 
        [Category("Columns"), Description("Indicates color of.")]
        public Color ZeroLineColor
        {
            get { return _ZeroLineColor; }
            set { _ZeroLineColor = value; OnStyleChanged(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeZeroLineColor()
        {
            return _ZeroLineColor != Color.Red;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetZeroLineColor()
        {
            this.ZeroLineColor = Color.Red;
        }
        /// 
        /// Occurs when style appearance changes.
        /// 
        public event EventHandler StyleChanged;
        /// 
        /// Raises StyleChanged event.
        /// 
        /// Provides event arguments.
        protected virtual void OnStyleChanged(EventArgs e)
        {
            EventHandler handler = StyleChanged;
            if (handler != null)
                handler(this, e);
        }
        private void OnStyleChanged()
        {
            OnStyleChanged(EventArgs.Empty);
        }
        private Color _HighPointColor = Color.Empty;
        /// 
        /// Gets or sets the color of the high point dot on chart.
        /// 
        [Category("Columns"), Description("Indicates color of high point dot on chart..")]
        public Color HighPointColor
        {
            get { return _HighPointColor; }
            set { _HighPointColor = value; OnStyleChanged(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeHighPointColor()
        {
            return !_HighPointColor.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetHighPointColor()
        {
            this.HighPointColor = Color.Empty;
        }
        private Color _LowPointColor = Color.Empty;
        /// 
        /// Gets or sets the color of the low point dot on chart.
        /// 
        [Category("Columns"), Description("Indicates color of low point dot on chart.")]
        public Color LowPointColor
        {
            get { return _LowPointColor; }
            set { _LowPointColor = value; OnStyleChanged(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeLowPointColor()
        {
            return !_LowPointColor.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetLowPointColor()
        {
            this.LowPointColor = Color.Empty;
        }
        private Color _FirstPointColor = Color.Empty;
        /// 
        /// Gets or sets the color of the first point dot on chart.
        /// 
        [Category("Columns"), Description("Indicates color of first point dot on chart..")]
        public Color FirstPointColor
        {
            get { return _FirstPointColor; }
            set { _FirstPointColor = value; OnStyleChanged(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeFirstPointColor()
        {
            return !_FirstPointColor.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetFirstPointColor()
        {
            this.FirstPointColor = Color.Empty;
        }
        private Color _LastPointColor = Color.Empty;
        /// 
        /// Gets or sets the color of the last point dot on chart.
        /// 
        [Category("Columns"), Description("Indicates color of last point dot on chart.")]
        public Color LastPointColor
        {
            get { return _LastPointColor; }
            set { _LastPointColor = value; OnStyleChanged(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeLastPointColor()
        {
            return !_LastPointColor.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetLastPointColor()
        {
            this.LastPointColor = Color.Empty;
        }
        private bool _DrawControlLine1 = false;
        /// 
        /// Gets or sets whether control line is drawn. Default value is false. Control lines can be used to display for example low and high control bounds for the chart.
        /// 
        [DefaultValue(false), Description("Indicates whether control line is drawn. Control lines can be used to display for example low and high control bounds for the chart")]
        public bool DrawControlLine1
        {
            get { return _DrawControlLine1; }
            set { _DrawControlLine1 = value; OnStyleChanged(); }
        }
        private Color _ControlLine1Color = Color.Empty;
        /// 
        /// Gets or sets the color of the first control line.
        /// 
        [Category("Columns"), Description("Indicates color of.")]
        public  Color ControlLine1Color
        {
        	get {	return _ControlLine1Color; }
            set { _ControlLine1Color = value; OnStyleChanged(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeControlLine1Color()
        {
            return !_ControlLine1Color.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetControlLine1Color()
        {
            this.ControlLine1Color = Color.Empty;
        }
        private double _ControlLine1StartValue = 0d;
        /// 
        /// Gets or sets starting value that is used to draw first control line.
        /// 
        [DefaultValue(0d), Description("Indicates the starting value that is used to draw first control line.")]
        public double ControlLine1StartValue
        {
        	get {	return _ControlLine1StartValue; }
            set { _ControlLine1StartValue = value; OnStyleChanged(); }
        }
        private double _ControlLine1EndValue = 0d;
        /// 
        /// Gets or sets end value that is used to draw first control line.
        /// 
        [DefaultValue(0d), Description("Indicates the end value that is used to draw first control line.")]
        public double ControlLine1EndValue
        {
        	get {	return _ControlLine1EndValue; }
            set { _ControlLine1EndValue = value; OnStyleChanged(); }
        }
        private bool _DrawControlLine2 = false;
        /// 
        /// Gets or sets whether control line is drawn. Default value is false. Control lines can be used to display for example low and high control bounds for the chart.
        /// 
        [DefaultValue(false), Description("Indicates whether control line is drawn. Control lines can be used to display for example low and high control bounds for the chart")]
        public bool DrawControlLine2
        {
            get { return _DrawControlLine2; }
            set { _DrawControlLine2 = value; OnStyleChanged(); }
        }
        private Color _ControlLine2Color = Color.Empty;
        /// 
        /// Gets or sets the color of the second control line.
        /// 
        [Category("Columns"), Description("Indicates color of second control line.")]
        public Color ControlLine2Color
        {
            get { return _ControlLine2Color; }
            set { _ControlLine2Color = value; OnStyleChanged(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeControlLine2Color()
        {
            return !_ControlLine2Color.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetControlLine2Color()
        {
            this.ControlLine2Color = Color.Empty;
        }
        private double _ControlLine2StartValue = 0d;
        /// 
        /// Gets or sets starting value that is used to draw second control line.
        /// 
        [DefaultValue(0d), Description("Indicates the starting value that is used to draw first second line.")]
        public double ControlLine2StartValue
        {
            get { return _ControlLine2StartValue; }
            set { _ControlLine2StartValue = value; OnStyleChanged(); }
        }
        private double _ControlLine2EndValue = 0d;
        /// 
        /// Gets or sets end value that is used to draw second control line.
        /// 
        [DefaultValue(0d), Description("Indicates the end value that is used to draw second control line.")]
        public double ControlLine2EndValue
        {
            get { return _ControlLine2EndValue; }
            set { _ControlLine2EndValue = value; OnStyleChanged(); }
        }
    }
}