using System;
using System.Drawing;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Threading;
using DevComponents.DotNetBar.Controls;
using DevComponents.DotNetBar.Rendering;
namespace DevComponents.DotNetBar
{
    /// 
    /// Represents circular progress indicator.
    /// 
    [ToolboxItem(false), Designer("DevComponents.DotNetBar.Design.SimpleItemDesigner, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=90f470f34c89ccaf")]
    public class CircularProgressItem : BaseItem
    {
        #region Events
        #endregion
        #region Constructor
        /// 
        /// Creates new instance of circular progress indicator.
        /// 
        public CircularProgressItem() : this("", "") { }
        /// 
        /// Creates new instance of circular progress indicator and assigns the name to it.
        /// 
        /// Item name.
        public CircularProgressItem(string sItemName) : this(sItemName, "") { }
        /// 
        /// Creates new instance of circular progress indicator and assigns the name and text to it.
        /// 
        /// Item name.
        /// item text.
        public CircularProgressItem(string sItemName, string ItemText)
            : base(sItemName, ItemText)
        {
            _SpokeAngles = GetSpokeAngles(_SpokeCount);
        }
        /// 
        /// Returns copy of the item.
        /// 
        public override BaseItem Copy()
        {
            CircularProgressItem objCopy = new CircularProgressItem(m_Name);
            this.CopyToItem(objCopy);
            return objCopy;
        }
        /// 
        /// Copies the ProgressBarItem specific properties to new instance of the item.
        /// 
        /// New ProgressBarItem instance.
        internal void InternalCopyToItem(ProgressBarItem copy)
        {
            CopyToItem(copy);
        }
        /// 
        /// Copies the ProgressBarItem specific properties to new instance of the item.
        /// 
        /// New ProgressBarItem instance.
        protected override void CopyToItem(BaseItem copy)
        {
            CircularProgressItem objCopy = copy as CircularProgressItem;
            base.CopyToItem(objCopy);
            
        }
        private bool _IsDisposing = false;
        protected override void Dispose(bool disposing)
        {
            _IsDisposing = true;
            Stop(true);
            base.Dispose(disposing);
            _IsDisposing = false;
        }
        #endregion
        #region Implementation
        public override void Paint(ItemPaintArgs e)
        {
            //e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
            //e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            if (_ProgressBarType == eCircularProgressType.Line)
            {
                PaintLineProgressBar(e);
            }
            else if (_ProgressBarType == eCircularProgressType.Dot)
            {
                PaintDotProgressBar(e);
            }
            else if (_ProgressBarType == eCircularProgressType.Donut)
            {
                PaintDonutProgressBar(e);
            }
            else if (_ProgressBarType == eCircularProgressType.Spoke)
            {
                PaintSpokeProgressBar(e);
            }
            else if (_ProgressBarType == eCircularProgressType.Pie)
            {
                PaintPieProgressBar(e);
            }
            PaintLabel(e);
            
            if (this.Focused && this.DesignMode)
            {
                Rectangle r = this.DisplayRectangle;
                r.Inflate(-1, -1);
                DesignTime.DrawDesignTimeSelection(e.Graphics, r, e.Colors.ItemDesignTimeBorder);
            }
            this.DrawInsertMarker(e.Graphics);
        }
        private void PaintLabel(ItemPaintArgs e)
        {
            if (!_TextVisible || string.IsNullOrEmpty(this.Text))
                return;
            Font font = e.Font;
            Graphics g = e.Graphics;
            Color textColor = GetTextColor(e);
            Rectangle textBounds = Rectangle.Empty;
            Rectangle bounds = m_Rect;
            Rectangle progressBounds = GetProgressBarBounds();
            eTextFormat format = eTextFormat.Default | eTextFormat.NoClipping;
            int textContentSpacing = Dpi.Width(TextContentSpacing);
            if (_TextPosition == eTextPosition.Left)
            {
                textBounds = new Rectangle(bounds.X + _TextPadding.Left, bounds.Y + _TextPadding.Top,
                    m_Rect.Width - _TextPadding.Horizontal - textContentSpacing - progressBounds.Width,
                    m_Rect.Height - _TextPadding.Vertical);
                format |= eTextFormat.VerticalCenter;
            }
            else if (_TextPosition == eTextPosition.Right)
            {
                textBounds = new Rectangle(bounds.X + _TextPadding.Left + textContentSpacing + progressBounds.Width, bounds.Y + _TextPadding.Top, 
                    m_Rect.Width - _TextPadding.Horizontal - textContentSpacing - progressBounds.Width,
                    m_Rect.Height - _TextPadding.Vertical);
                format |= eTextFormat.VerticalCenter;
            }
            else if (_TextPosition == eTextPosition.Top)
            {
                textBounds = new Rectangle(bounds.X + _TextPadding.Left, bounds.Y + _TextPadding.Top,
                    m_Rect.Width - _TextPadding.Horizontal,
                    m_Rect.Height - _TextPadding.Vertical - progressBounds.Height - textContentSpacing);
                format |= eTextFormat.HorizontalCenter;
            }
            else if (_TextPosition == eTextPosition.Bottom)
            {
                textBounds = new Rectangle(bounds.X + _TextPadding.Left, bounds.Y + _TextPadding.Top + textContentSpacing + progressBounds.Height,
                     m_Rect.Width - _TextPadding.Horizontal,
                    m_Rect.Height - _TextPadding.Vertical - progressBounds.Height - textContentSpacing);
                format |= eTextFormat.HorizontalCenter;
            }
            if (_TextWidth > 0)
            {
                textBounds.Width = _TextWidth;
                format |= eTextFormat.WordBreak;
            }
            //g.FillRectangle(Brushes.WhiteSmoke, textBounds);
            TextDrawing.DrawString(g, this.Text, font, textColor, textBounds, format);
        }
        private Color GetTextColor(ItemPaintArgs e)
        {
            if (!_TextColor.IsEmpty) return _TextColor;
            return LabelItem.GetTextColor(e, this.EffectiveStyle, this.GetEnabled(), _TextColor);
        }
        private bool RenderesProgressText
        {
            get
            {
                return _ProgressTextVisible && (!_IsEndlessProgressBar || !string.IsNullOrEmpty(_ProgressText));
            }
        }
        private void PaintPieProgressBar(ItemPaintArgs e)
        {
            Graphics g = e.Graphics;
            Rectangle bounds = GetProgressBarBounds();
            PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2);
            float penWidth = (float)Math.Max(1.5f, bounds.Height * .2);
            bounds.Inflate(-1, -1);
            bounds.Width--;
            bounds.Height--;
            float borderWidth = 1f;
            if (bounds.Height > 31)
                borderWidth = bounds.Height * .05f;
            if (!_IsEndlessProgressBar)
            {
                int value = GetValue();
                Rectangle pieBounds = bounds;
                pieBounds.Inflate(-(int)borderWidth, -(int)borderWidth);
                int sweepAngle = (int)(360 * ((double)value / Math.Max(1, (_Maximum - _Minimum))));
                using (SolidBrush brush = new SolidBrush(_ProgressColor))
                {
                    g.FillPie(brush, pieBounds, 270, sweepAngle);
                }
            }
            else
            {
                Rectangle pieBounds = bounds;
                pieBounds.Inflate(-(int)borderWidth, -(int)borderWidth);
                int startAngle = (int)(360 * _EndlessProgressValue / (double)_SpokeCount);
                int sweepAngle = 90;
                using (SolidBrush brush = new SolidBrush(_ProgressColor))
                {
                    g.FillPie(brush, pieBounds, startAngle, sweepAngle);
                }
            }
            Rectangle borderBounds = bounds;
            borderBounds.Width--;
            borderBounds.Height--;
            borderBounds.Offset(1, 1);
            using (Pen pen = new Pen(_PieBorderDark, borderWidth))
            {
                pen.Alignment = PenAlignment.Inset;
                g.DrawEllipse(pen, borderBounds);
            }
            borderBounds.Offset(-1, -1);
            //using (Pen pen = new Pen(_PieBorderLight, borderWidth))
            //    g.DrawEllipse(pen, borderBounds);
            using (Pen pen = new Pen(_PieBorderLight, borderWidth + .5f))
            {
                pen.Alignment = PenAlignment.Inset;
                g.DrawEllipse(pen, borderBounds);
                g.DrawEllipse(pen, borderBounds);
            }
            if (RenderesProgressText)
            {
                bounds.Offset(1, 0);
                PaintProgressText(g, bounds, (int)(bounds.Height * .4f), e.Font);
            }
        }
        private void PaintSpokeProgressBar(ItemPaintArgs e)
        {
            Graphics g = e.Graphics;
            Rectangle bounds = GetProgressBarBounds();
            // Account for border and shade
            bounds.Width -= 2;
            bounds.Height -= 2;
            PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2);
            float penWidth = (float)Math.Max(1.5f, bounds.Height * .2);
            //bounds.Inflate(-(int)(penWidth / 2), -(int)(penWidth / 2));
            GraphicsPath clipPath = new GraphicsPath();
            Rectangle clipPathEllipse = bounds;
            clipPathEllipse.Inflate(-(int)((float)bounds.Height / 3f), -(int)((float)bounds.Height / 3f));
            clipPath.AddEllipse(clipPathEllipse);
            Region oldClip = g.Clip;
            g.SetClip(clipPath, CombineMode.Exclude);
            if (!_IsEndlessProgressBar)
            {
                int value = GetValue();
                int sweepAngle = (int)(360 * ((double)value / Math.Max(1, (_Maximum - _Minimum))));
                using (SolidBrush brush = new SolidBrush(_ProgressColor))
                {
                    g.FillPie(brush, bounds, 270, sweepAngle);
                }
            }
            else
            {
                int startAngle = (int)(360 * _EndlessProgressValue / (double)_SpokeCount);
                int sweepAngle = 90;
                using (SolidBrush brush = new SolidBrush(_ProgressColor))
                {
                    g.FillPie(brush, bounds, startAngle, sweepAngle);
                }
            }
            int radius = bounds.Width / 2;
            PointF shadeCenterPoint = centerPoint;
            float shadeSpokeWidth = 1f;
            float circleWidth = 1f;
            float shadeCircleWidth = 1f;
            //if (bounds.Height < 28)
            //{
            //    shadeSpokeWidth = 1f;
            //    circleWidth = 1f;
            //    shadeCircleWidth = 1f;
            //}
            radius--;
            using (Pen pen = new Pen(_SpokeBorderDark, shadeSpokeWidth))
            {
                pen.Alignment = PenAlignment.Right;
                PointF p1 = GetCoordinate(centerPoint, radius, 315); p1.X++;
                PointF p2 = GetCoordinate(shadeCenterPoint, radius, 135); p2.X++;
                g.DrawLine(pen, p1, p2); g.DrawLine(pen, p1, p2);
                p1 = GetCoordinate(centerPoint, radius, 270); p1.X++;
                p2 = GetCoordinate(shadeCenterPoint, radius, 90); p2.X++;
                g.DrawLine(pen, p1, p2);
                p1 = GetCoordinate(centerPoint, radius, 225); p1.Y++;
                p2 = GetCoordinate(shadeCenterPoint, radius, 45); p2.Y++;
                g.DrawLine(pen, p1, p2);
                p1 = GetCoordinate(centerPoint, radius, 180); p1.Y++;
                p2 = GetCoordinate(shadeCenterPoint, radius, 0); p2.Y++;
                g.DrawLine(pen, p1, p2);
            }
            using (Pen pen = new Pen(_SpokeBorderDark, shadeCircleWidth))
            {
                pen.Alignment = PenAlignment.Inset;
                Rectangle shadeBounds = bounds;
                shadeBounds.Offset(1, 1);
                //shadeBounds.Width--;
                //shadeBounds.Height--;
                g.DrawEllipse(pen, shadeBounds);
            }
            using (Pen pen = new Pen(_SpokeBorderLight, shadeSpokeWidth))
            {
                g.DrawLine(pen, GetCoordinate(centerPoint, radius, 315), GetCoordinate(centerPoint, radius, 135));
                g.DrawLine(pen, GetCoordinate(centerPoint, radius, 270), GetCoordinate(centerPoint, radius, 90));
                g.DrawLine(pen, GetCoordinate(centerPoint, radius, 225), GetCoordinate(centerPoint, radius, 45));
                g.DrawLine(pen, GetCoordinate(centerPoint, radius, 180), GetCoordinate(centerPoint, radius, 0));
            }
            using (Pen pen = new Pen(_SpokeBorderLight, circleWidth))
            {
                pen.Alignment = PenAlignment.Inset;
                g.DrawEllipse(pen, bounds);
                g.DrawEllipse(pen, bounds);
            }
            g.Clip = oldClip;
            oldClip.Dispose();
            float innerCircleWidth = 1f;
            using (Pen pen = new Pen(Color.White, innerCircleWidth))
            {
                pen.Alignment = PenAlignment.Inset;
                g.DrawEllipse(pen, clipPathEllipse);
                g.DrawEllipse(pen, clipPathEllipse);
            }
            if (RenderesProgressText)
            {
                //bounds.Y--;
                PaintProgressText(g, bounds, (int)(bounds.Height * .35f), e.Font);
            }
        }
        private int GetValue()
        {
            int value = Math.Min(_Maximum, Math.Max(_Minimum, _Value));
            if (this.DesignMode && value == _Minimum) value = (int)(_Maximum * .75d);
            return value;
        }
        private void PaintDonutProgressBar(ItemPaintArgs e)
        {
            Graphics g = e.Graphics;
            RectangleF bounds = GetProgressBarBounds();
            PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2);
            float penWidth = (float)Math.Max(1.5f, bounds.Height * .2);
            bounds.Inflate(-Math.Max(1, penWidth / 2), -Math.Max(1, penWidth / 2));
            bounds.Width--;
            bounds.Height--;
            if (!_IsEndlessProgressBar)
            {
                int value = GetValue();
                int sweepAngle = (int)(360 * ((double)value / Math.Max(1, (_Maximum - _Minimum))));
                using (Pen pen = new Pen(_ProgressColor, penWidth))
                {
                    g.DrawArc(pen, bounds, 270, sweepAngle);
                }
            }
            else
            {
                int startAngle = (int)(360 * _EndlessProgressValue / (double)_SpokeCount);
                int sweepAngle = 140;
                using (Pen pen = new Pen(_ProgressColor, penWidth))
                {
                    pen.EndCap = LineCap.Round;
                    pen.StartCap = LineCap.Round;
                    g.DrawArc(pen, bounds, startAngle, sweepAngle);
                }
            }
            if (RenderesProgressText)
            {
                PaintProgressText(g, bounds, (int)(bounds.Height * .4f), e.Font);
            }
        }
        private void PaintDotProgressBar(ItemPaintArgs e)
        {
            Graphics g = e.Graphics;
            Rectangle bounds = GetProgressBarBounds();
            PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2);
            int outerRadius = 14;
            int circleSize = 2;
            outerRadius = (int)Math.Round(bounds.Width * .40d);
            circleSize = Math.Max(1, (int)Math.Round(outerRadius * .25d));
            int value = GetValue();
            if (!_IsEndlessProgressBar)
            {
                int spoke = (int)Math.Round(_SpokeCount * ((double)value / Math.Max(1, (_Maximum - _Minimum))));
                for (int i = 0; i < spoke; i++)
                {
                    PointF endPoint = GetCoordinate(centerPoint, outerRadius, _SpokeAngles[i]);
                    RectangleF circleBounds = new RectangleF(endPoint, Size.Empty);
                    circleBounds.Inflate(circleSize, circleSize);
                    using (SolidBrush brush = new SolidBrush(ColorFromSpokeIndex(i)))
                    {
                        g.FillEllipse(brush, circleBounds);
                    }
                }
            }
            else if (_IsRunning) // Endless Progress Bar
            {
                int position = _EndlessProgressValue;
                for (int i = 0; i < _SpokeCount; i++)
                {
                    position = position % _SpokeCount;
                    PointF endPoint = GetCoordinate(centerPoint, outerRadius, _SpokeAngles[position]);
                    RectangleF circleBounds = new RectangleF(endPoint, Size.Empty);
                    circleBounds.Inflate(circleSize, circleSize);
                    using (SolidBrush brush = new SolidBrush(ColorFromSpokeIndex(i)))
                    {
                        g.FillEllipse(brush, circleBounds);
                    }
                    position++;
                }
            }
            if (RenderesProgressText)
            {
                PaintProgressText(g, bounds, (int)(bounds.Height * .35f), e.Font);
            }
        }
        private int _SpokeCount = 12;
        private int _EndlessProgressValue = 0;
        private void PaintLineProgressBar(ItemPaintArgs e)
        {
            //e.Graphics.DrawRectangle(Pens.Green, new Rectangle(0, 0, this.Width - 1, this.Height - 1));
            Graphics g = e.Graphics;
            Rectangle bounds = GetProgressBarBounds();
            PointF centerPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2);
            int innerRadius = 6;
            int outerRadius = 14;
            int spokeSize = 2;
            outerRadius = (int)Math.Round(bounds.Width * .45d);
            innerRadius = (int)Math.Round(outerRadius * .45d);
            spokeSize = Math.Max(2, (int)Math.Round(outerRadius * .15d));
            int value = GetValue();
            if (!_IsEndlessProgressBar)
            {
                int spoke = (int)Math.Round(_SpokeCount * ((double)value / Math.Max(1, (_Maximum - _Minimum))));
                for (int i = 0; i < spoke; i++)
                {
                    PointF startPoint = GetCoordinate(centerPoint, innerRadius, _SpokeAngles[i]);
                    PointF endPoint = GetCoordinate(centerPoint, outerRadius, _SpokeAngles[i]);
                    using (Pen pen = new Pen(ColorFromSpokeIndex(i), spokeSize))
                    {
                        pen.EndCap = System.Drawing.Drawing2D.LineCap.Round;
                        pen.StartCap = System.Drawing.Drawing2D.LineCap.Round;
                        g.DrawLine(pen, startPoint, endPoint);
                    }
                }
            }
            else if (_IsRunning) // Endless Progress Bar
            {
                int position = _EndlessProgressValue;
                for (int i = 0; i < _SpokeCount; i++)
                {
                    position = position % _SpokeCount;
                    PointF startPoint = GetCoordinate(centerPoint, innerRadius, _SpokeAngles[position]);
                    PointF endPoint = GetCoordinate(centerPoint, outerRadius, _SpokeAngles[position]);
                    using (Pen pen = new Pen(ColorFromSpokeIndex(i), spokeSize))
                    {
                        pen.EndCap = System.Drawing.Drawing2D.LineCap.Round;
                        pen.StartCap = System.Drawing.Drawing2D.LineCap.Round;
                        g.DrawLine(pen, startPoint, endPoint);
                    }
                    position++;
                }
            }
            if (RenderesProgressText)
            {
                PaintProgressText(g, bounds, innerRadius, e.Font);
            }
        }
        private string GetProgressValueText()
        {
            try
            {
                if (!string.IsNullOrEmpty(_ProgressText)) return _ProgressText;
                return string.Format(_ProgressTextFormat, _Value);
            }
            catch
            {
                return "Format Error";
            }
        }
        private void PaintProgressText(Graphics g, RectangleF bounds, int innerRadius, Font baseFont)
        {
            //StringFormat format = (StringFormat)StringFormat.GenericDefault.Clone();
            //format.Alignment = StringAlignment.Center;
            //format.LineAlignment = StringAlignment.Center;
            //format.FormatFlags = StringFormatFlags.NoWrap;
            //format.Trimming = StringTrimming.None;
            eTextFormat format = eTextFormat.HorizontalCenter | eTextFormat.VerticalCenter | eTextFormat.NoPadding | eTextFormat.SingleLine | eTextFormat.NoClipping;
            float fontSize = Math.Max(1f, innerRadius / 1.8f);
            Color textColor = _ProgressTextColor.IsEmpty ? _ProgressColor : _ProgressTextColor;
            using (Font font = new Font(baseFont.FontFamily, fontSize, FontStyle.Regular))
            {
                TextDrawing.DrawString(g, GetProgressValueText(), font, textColor, Rectangle.Round(bounds), format);
                //using (SolidBrush brush = new SolidBrush(textColor))
                //    g.DrawString(string.Format("{0}%", _Value), font, brush, bounds, format);
            }
        }
        private Color ColorFromSpokeIndex(int spokeIndex, int spokeCount)
        {
            return Color.FromArgb((int)(210 * (double)spokeIndex / spokeCount) + 45, _ProgressColor);
        }
        private Color ColorFromSpokeIndex(int spokeIndex)
        {
            return ColorFromSpokeIndex(spokeIndex, _SpokeCount);
        }
        private PointF GetCoordinate(PointF centerPoint, int radius, double angle)
        {
            double dblAngle = Math.PI * angle / 180;
            return new PointF(centerPoint.X + radius * (float)Math.Cos(dblAngle),
                              centerPoint.Y + radius * (float)Math.Sin(dblAngle));
        }
        internal static readonly int TextContentSpacing = 3;
        private Size _TextSize = Size.Empty;
        public override void RecalcSize()
        {
            int diameter = _Diameter;
            if (!(this.ContainerControl is CircularProgress))
                diameter = Dpi.Width(_Diameter);
            Rectangle r = new Rectangle(m_Rect.X, m_Rect.Y, diameter, diameter);
            if (_TextVisible && !string.IsNullOrEmpty(this.Text))
            {
                Control parent = this.ContainerControl as Control;
                if (parent != null)
                {
                    Font font = parent.Font;
                    using (Graphics g = parent.CreateGraphics())
                    {
                        Size textSize = ButtonItemLayout.MeasureItemText(this, g, _TextWidth, font, (_TextWidth > 0 ? eTextFormat.WordBreak : eTextFormat.SingleLine), parent.RightToLeft == RightToLeft.Yes);
                        _TextSize = textSize;
                        textSize.Width += _TextPadding.Horizontal;
                        textSize.Height += _TextPadding.Vertical;
                        int textContentSpacing = Dpi.Width(TextContentSpacing);
                        if (_TextPosition == eTextPosition.Left || _TextPosition == eTextPosition.Right)
                        {
                            textSize.Width += textContentSpacing;
                            r.Width += textSize.Width;
                            r.Height = Math.Max(r.Height, textSize.Height);
                        }
                        else
                        {
                            textSize.Height += textContentSpacing;
                            r.Height += textSize.Height;
                            r.Width = Math.Max(r.Width, textSize.Width);
                        }
                    }
                }
            }
            m_Rect = r;
            base.RecalcSize();
        }
        private Rectangle _ProgressBarBounds = Rectangle.Empty;
        private Rectangle GetProgressBarBounds()
        {
            int diameter = _Diameter;
            if (!(this.ContainerControl is CircularProgress))
                diameter = Dpi.Width(_Diameter);
            if (string.IsNullOrEmpty(Text) || !_TextVisible)
            {
                return new Rectangle(m_Rect.X + (m_Rect.Width - diameter) / 2, m_Rect.Y + (m_Rect.Height - diameter) / 2, diameter, diameter);
            }
            if (_TextPosition == eTextPosition.Top)
            {
                return new Rectangle(m_Rect.X + (m_Rect.Width - diameter) / 2, m_Rect.Y + (m_Rect.Height - diameter), diameter, diameter);
            }
            else if (_TextPosition == eTextPosition.Right)
            {
                return new Rectangle(m_Rect.X, m_Rect.Y + (m_Rect.Height - diameter) / 2, diameter, diameter);
            }
            else if (_TextPosition == eTextPosition.Left)
            {
                return new Rectangle(m_Rect.Right - diameter, 
                    m_Rect.Y + (m_Rect.Height - diameter) / 2, 
                    diameter, diameter);
            }
            else if (_TextPosition == eTextPosition.Bottom)
            {
                return new Rectangle(m_Rect.X + (m_Rect.Width - diameter) / 2, m_Rect.Y, diameter, diameter);
            }
            return new Rectangle(m_Rect.X, m_Rect.Y, diameter, diameter);
        }
        private double[] _SpokeAngles = null;
        private double[] GetSpokeAngles(int numberOfSpokes)
        {
            double[] angles = new double[numberOfSpokes];
            double angleStep = 360d / numberOfSpokes;
            for (int i = 0; i < numberOfSpokes; i++)
                angles[i] = (i == 0 ? 270 + angleStep : angles[i - 1] + angleStep);
            return angles;
        }
        private eCircularProgressType _ProgressBarType = eCircularProgressType.Line;
        /// 
        /// Gets or sets the circular progress bar type.
        /// 
        [DefaultValue(eCircularProgressType.Line), Category("Appearance"), Description("Indicates circular progress bar type.")]
        public eCircularProgressType ProgressBarType
        {
            get { return _ProgressBarType; }
            set
            {
                if (value != _ProgressBarType)
                {
                    eCircularProgressType oldValue = _ProgressBarType;
                    _ProgressBarType = value;
                    OnProgressBarTypeChanged(oldValue, value);
                }
            }
        }
        private void OnProgressBarTypeChanged(eCircularProgressType oldValue, eCircularProgressType newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("ProgressBarType"));
            this.Refresh();
        }
        private int _Maximum = 100;
        /// 
        /// Gets or sets the maximum value of the progress bar.
        /// 
        [Description("Indicates maximum value of the progress bar."), Category("Behavior"), DefaultValue(100)]
        public int Maximum
        {
            get { return _Maximum; }
            set
            {
                if (value != _Maximum)
                {
                    int oldValue = _Maximum;
                    _Maximum = value;
                    OnMaximumChanged(oldValue, value);
                }
            }
        }
        private void OnMaximumChanged(int oldValue, int newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("Maximum"));
            CoerceValue();
        }
        private int _Minimum = 0;
        /// 
        /// Gets or sets the minimum value of the progress bar.
        /// 
        [Description("Indicates minimum value of the progress bar."), Category("Behavior"), DefaultValue(0)]
        public int Minimum
        {
            get { return _Minimum; }
            set
            {
                if (value != _Minimum)
                {
                    int oldValue = _Minimum;
                    _Minimum = value;
                    OnMinimumChanged(oldValue, value);
                }
            }
        }
        private void OnMinimumChanged(int oldValue, int newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("Minimum"));
            CoerceValue();
        }
        private void CoerceValue()
        {
            int newValue = _Value;
            if (_Value < _Minimum)
                newValue = _Minimum;
            else if (_Value > _Maximum)
                newValue = _Maximum;
            Value = newValue;
        }
        private Color _ProgressTextColor = Color.Empty;
        /// 
        /// Gets or sets the color of the progress percentage text.
        /// 
        [Category("Appearance"), Description("Indicates color of progress percentage text")]
        public Color ProgressTextColor
        {
            get { return _ProgressTextColor; }
            set { _ProgressTextColor = value; this.Refresh(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeProgressTextColor()
        {
            return !_ProgressTextColor.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetProgressTextColor()
        {
            this.ProgressTextColor = Color.Empty;
        }
        private bool _ProgressTextVisible = false;
        /// 
        /// Gets or sets whether text that displays the progress bar completion percentage text is visible. Default value is false.
        /// 
        [DefaultValue(false), Category("Appearance"), Description("Indicates whether text that displays the progress bar completion percentage text is visible")]
        public bool ProgressTextVisible
        {
            get { return _ProgressTextVisible; }
            set
            {
                if (value != _ProgressTextVisible)
                {
                    bool oldValue = _ProgressTextVisible;
                    _ProgressTextVisible = value;
                    OnProgressTextVisibleChanged(oldValue, value);
                }
            }
        }
        private void OnProgressTextVisibleChanged(bool oldValue, bool newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("ProgressTextVisible"));
            this.Refresh();
        }
        private string _ProgressText = "";
        /// 
        /// Gets or sets the text displayed on top of the circular progress bar.
        /// 
        [DefaultValue(""), Category("Appearance"), Description("Indicates text displayed on top of the circular progress bar.")]
        public string ProgressText
        {
            get { return _ProgressText; }
            set
            {
                if (value != _ProgressText)
                {
                    string oldValue = _ProgressText;
                    _ProgressText = value;
                    OnProgressTextChanged(oldValue, value);
                }
            }
        }
        private void OnProgressTextChanged(string oldValue, string newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("ProgressText"));
            Refresh();
        }
        private int _Value;
        /// 
        /// Gets or sets the current value of the progress bar.
        /// 
        [Description("Indicates current value of the progress bar."), Category("Behavior"), DefaultValue(0)]
        public int Value
        {
            get { return _Value; }
            set
            {
                value = Math.Min(_Maximum, Math.Max(value, _Minimum));
                if (value != _Value)
                {
                    int oldValue = _Value;
                    _Value = value;
                    OnValueChanged(oldValue, value);
                }
            }
        }
        private void OnValueChanged(int oldValue, int newValue)
        {
            if (!_IsEndlessProgressBar)
                this.Refresh();
            OnValueChanged(EventArgs.Empty);
            OnPropertyChanged(new PropertyChangedEventArgs("Value"));
        }
        /// 
        /// Occurs when Value property has changed.
        /// 
        public event EventHandler ValueChanged;
        /// 
        /// Raises ValueChanged event.
        /// 
        /// Provides event arguments.
        protected virtual void OnValueChanged(EventArgs e)
        {
            EventHandler handler = ValueChanged;
            if (handler != null)
                handler(this, e);
        }
        /// 
        /// Called when property on CircularProgressBar changes.
        /// 
        /// Property Change Arguments
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
        }
        private void MoveEndlessProgressBar()
        {
            if (!this.IsRunning || this.IsDisposed || _IsDisposing) return;
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(delegate()
                {
                    MoveEndlessProgressBar();
                }));
                return;
            }
            _EndlessProgressValue = ++_EndlessProgressValue % _SpokeCount;
            this.Refresh();
            Control container = this.ContainerControl as Control;
            if (container != null)
                container.Update();
        }
        private bool _IsEndlessProgressBar = false;
        private BackgroundWorker _LoopWorker = null;
        /// 
        /// Starts the progress bar loop for endless type progress bar. Progress bar will continue to run until Stop() method is called.
        /// 
        public void Start()
        {
            if (_IsRunning) return;
            _IsEndlessProgressBar = true;
            _IsRunning = true;
            _LoopWorker = new BackgroundWorker();
            _LoopWorker.WorkerSupportsCancellation = true;
            _LoopWorker.DoWork += LoopWorkerDoWork;
            _LoopWorker.RunWorkerAsync();
        }
        void LoopWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;
            while (!worker.CancellationPending)
            {
                MoveEndlessProgressBar();
                try
                {
                    using (
                                        System.Threading.ManualResetEvent wait =
                                            new System.Threading.ManualResetEvent(false))
                        wait.WaitOne(_AnimationSpeed);
                    //Thread.Sleep(_AnimationSpeed);
                }
                catch
                {
                    e.Cancel = true;
                }
            }
            e.Cancel = true;
        }
        /// 
        /// Stops the progress bar loop for endless type progress bar.
        /// 
        public void Stop()
        {
            Stop(false);
        }
        public void Stop(bool disposing)
        {
            if (!_IsRunning) return;
            _IsEndlessProgressBar = false;
            _IsRunning = false;
            BackgroundWorker worker = _LoopWorker;
            _LoopWorker = null;
            worker.CancelAsync();
            while (worker.IsBusy)
                Application.DoEvents();
            worker.DoWork -= LoopWorkerDoWork;
            worker.Dispose();
            if (!disposing)
                this.Refresh();
        }
        private bool _IsRunning = false;
        /// 
        /// Gets or sets whether endless type progress bar is running.
        /// 
        [Browsable(false), DefaultValue(false)]
        public bool IsRunning
        {
            get { return _IsRunning; }
            set
            {
                if (_IsRunning != value)
                {
                    if (value)
                        Start();
                    else
                        Stop();
                }
            }
        }
        private static readonly Color DefaultProgressColor = Color.DarkSlateGray;//Color.FromArgb(143, 223, 95);
        private Color _ProgressColor = DefaultProgressColor;
        /// 
        /// Gets or sets the color of the color of progress indicator.
        /// 
        [Category("Columns"), Description("Indicates color of progress indicator.")]
        public Color ProgressColor
        {
            get { return _ProgressColor; }
            set { _ProgressColor = value; this.Refresh(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeProgressColor()
        {
            return _ProgressColor != DefaultProgressColor;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetProgressColor()
        {
            this.ProgressColor = DefaultProgressColor;
        }
        private int _Diameter = 24;
        /// 
        /// Gets or sets circular progress indicator diameter in pixels.
        /// 
        [DefaultValue(24), Category("Appearance"), Description("Indicates circular progress indicator diameter in pixels.")]
        public int Diameter
        {
            get { return _Diameter; }
            set
            {
                if (value != _Diameter)
                {
                    int oldValue = _Diameter;
                    _Diameter = value;
                    OnDiameterChanged(oldValue, value);
                }
            }
        }
        private void OnDiameterChanged(int oldValue, int newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("Diameter"));
            NeedRecalcSize = true;
            this.Refresh();
        }
        private eTextPosition _TextPosition = eTextPosition.Left;
        /// 
        /// Gets or sets the text position in relation to the circular progress indicator.
        /// 
        [DefaultValue(eTextPosition.Left), Category("Appearance"), Description("Indicatesd text position in relation to the circular progress indicator.")]
        public eTextPosition TextPosition
        {
            get { return _TextPosition; }
            set
            {
                if (value != _TextPosition)
                {
                    eTextPosition oldValue = _TextPosition;
                    _TextPosition = value;
                    OnTextPositionChanged(oldValue, value);
                }
            }
        }
        private void OnTextPositionChanged(eTextPosition oldValue, eTextPosition newValue)
        {
            NeedRecalcSize = true;
            this.Refresh();
            OnPropertyChanged(new PropertyChangedEventArgs("TextPosition"));
        }
        private bool _TextVisible = true;
        /// 
        /// Gets or sets whether text/label displayed next to the item is visible. 
        /// 
        [DefaultValue(true), Category("Appearance"), Description("Indicates whether caption/label set using Text property is visible.")]
        public bool TextVisible
        {
            get { return _TextVisible; }
            set
            {
                if (value != _TextVisible)
                {
                    bool oldValue = _TextVisible;
                    _TextVisible = value;
                    OnTextVisibleChanged(oldValue, value);
                }
            }
        }
        private void OnTextVisibleChanged(bool oldValue, bool newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("TextVisible"));
            NeedRecalcSize = true;
            this.Refresh();
        }
        private int _TextWidth = 0;
        /// 
        /// Gets or sets the suggested text-width. If you want to make sure that text you set wraps over multiple lines you can set suggested text-width so word break is performed.
        /// 
        [DefaultValue(0), Category("Appearance"), Description("Indicates suggested text-width. If you want to make sure that text you set wraps over multiple lines you can set suggested text-width so word break is performed.")]
        public int TextWidth
        {
            get { return _TextWidth; }
            set
            {
                if (value != _TextWidth)
                {
                    int oldValue = _TextWidth;
                    _TextWidth = value;
                    OnTextWidthChanged(oldValue, value);
                }
            }
        }
        private void OnTextWidthChanged(int oldValue, int newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("TextWidth"));
            NeedRecalcSize = true;
            Refresh();
        }
        private Padding _TextPadding = new Padding(0, 0, 0, 0);
        /// 
        /// Gets or sets text padding.
        /// 
        [Browsable(true), Category("Appearance"), Description("Gets or sets text padding."), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public Padding TextPadding
        {
            get { return _TextPadding; }
        }
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeTextPadding()
        {
            return _TextPadding.Bottom != 0 || _TextPadding.Top != 0 || _TextPadding.Left != 0 || _TextPadding.Right != 0;
        }
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetTextPadding()
        {
            _TextPadding = new Padding(0, 0, 0, 0);
        }
        private void TextPaddingPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            NeedRecalcSize = true;
            this.Refresh();
        }
        private Color _TextColor = Color.Empty;
        /// 
        /// Gets or sets the color of the text label.
        /// 
        [Category("Columns"), Description("Indicates color of text label.")]
        public Color TextColor
        {
            get { return _TextColor; }
            set { _TextColor = value; this.Refresh(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeTextColor()
        {
            return !_TextColor.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetTextColor()
        {
            this.TextColor = Color.Empty;
        }
        private static readonly Color DefaultPieBorderDarkColor = Color.FromArgb(50, Color.Black);
        private Color _PieBorderDark = DefaultPieBorderDarkColor;
        /// 
        /// Gets or sets the color of the pie progress bar dark border. 
        /// 
        [Category("Pie"), Description("Indicates color of pie progress bar dark border.")]
        public Color PieBorderDark
        {
            get { return _PieBorderDark; }
            set { _PieBorderDark = value; this.Refresh(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializePieBorderDark()
        {
            return _PieBorderDark != DefaultPieBorderDarkColor;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetPieBorderDark()
        {
            this.PieBorderDark = DefaultPieBorderDarkColor;
        }
        private static readonly Color DefaultPieBorderLightColor = Color.FromArgb(255, Color.White);
        private Color _PieBorderLight = DefaultPieBorderLightColor;
        /// 
        /// Gets or sets the color of the pie progress bar light border. 
        /// 
        [Category("Pie"), Description("Indicates color of pie progress bar light border. ")]
        public Color PieBorderLight
        {
            get { return _PieBorderLight; }
            set { _PieBorderLight = value; this.Refresh(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializePieBorderLight()
        {
            return _PieBorderLight != DefaultPieBorderLightColor;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetPieBorderLight()
        {
            this.PieBorderLight = DefaultPieBorderLightColor;
        }
        private static readonly Color DefaultSpokeBorderDarkColor = Color.FromArgb(48, Color.Black);
        private Color _SpokeBorderDark = DefaultSpokeBorderDarkColor;
        /// 
        /// Gets or sets the color of the spoke progress bar dark border.
        /// 
        [Category("Spoke"), Description("Indicates color of spoke progress bar dark border.")]
        public Color SpokeBorderDark
        {
            get { return _SpokeBorderDark; }
            set { _SpokeBorderDark = value; this.Refresh(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeSpokeBorderDark()
        {
            return _SpokeBorderDark != DefaultSpokeBorderDarkColor;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetSpokeBorderDark()
        {
            this.SpokeBorderDark = DefaultSpokeBorderDarkColor;
        }
        private static readonly Color DefaultSpokeBorderLightColor = Color.FromArgb(255, Color.White);
        private Color _SpokeBorderLight = DefaultSpokeBorderLightColor;
        /// 
        /// Gets or sets the color of the spoke progress bar light border.
        /// 
        [Category("Spoke"), Description("Indicates color of spoke progress bar light border..")]
        public Color SpokeBorderLight
        {
            get { return _SpokeBorderLight; }
            set { _SpokeBorderLight = value; this.Refresh(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeSpokeBorderLight()
        {
            return _SpokeBorderLight != DefaultSpokeBorderLightColor;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetSpokeBorderLight()
        {
            this.SpokeBorderLight = DefaultSpokeBorderLightColor;
        }
        private string _ProgressTextFormat = "{0}%";
        /// 
        /// Gets or sets format string for progress value.
        /// 
        [DefaultValue("{0}%"), Category("Appearance"), Description("Indicates format string for progress value.")]
        public string ProgressTextFormat
        {
            get { return _ProgressTextFormat; }
            set
            {
                if (value != _ProgressTextFormat)
                {
                    string oldValue = _ProgressTextFormat;
                    _ProgressTextFormat = value;
                    OnProgressTextFormatChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when ProgressTextFormat property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnProgressTextFormatChanged(string oldValue, string newValue)
        {
            this.Refresh();
            OnPropertyChanged(new PropertyChangedEventArgs("ProgressTextFormat"));
        }
        private int _AnimationSpeed = 100;
        /// 
        /// Gets or sets the animation speed for endless running progress. Lower number means faster running.
        /// 
        [DefaultValue(100), Description("Indicates the animation speed for endless running progress. Lower number means faster running."), Category("Behavior")]
        public int AnimationSpeed
        {
            get { return _AnimationSpeed; }
            set 
            {
                if (value < 1)
                    value = 1;
                _AnimationSpeed = value; 
            }
        }
        #endregion
    }
    /// 
    /// Defines available circular progress bar types.
    /// 
    public enum eCircularProgressType
    {
        /// 
        /// Line spokes progress bar.
        /// 
        Line,
        /// 
        /// Dot type/FireFox progress bar.
        /// 
        Dot,
        /// 
        /// Donut type progress bar.
        /// 
        Donut,
        /// 
        /// Spoke type progress bar.
        /// 
        Spoke,
        /// 
        /// Pie type progress bar.
        /// 
        Pie
    }
}