using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace DevComponents.Instrumentation
{
    public class StateIndicator : GaugeIndicator
    {
        #region Private variables
        private StateIndicatorStyle _Style;
        private StateRangeCollection _Ranges;
        private Image _Image;
        private float _Angle;
        private float _RoundRectangleArc;
        private Color _TextColor;
        private TextAlignment _TextAlignment;
        private float _TextVerticalOffset;
        private float _TextHorizontalOffset;
        private Font _AbsFont;
        #endregion
        public StateIndicator()
        {
            InitIndicator();
        }
        #region InitIndicator
        private void InitIndicator()
        {
            _Style = StateIndicatorStyle.Circular;
            _RoundRectangleArc = .75f;
            _TextColor = Color.Black;
            _TextAlignment = TextAlignment.MiddleCenter;
            BackColor.Color1 = Color.PaleGreen;
            BackColor.Color2 = Color.DarkGreen;
        }
        #endregion
        #region Public properties
        #region Angle
        /// 
        /// Gets or sets the amount to rotate the indicator, specified in degrees.
        /// 
        [Browsable(true)]
        [Category("Layout"), DefaultValue(0f)]
        [Editor("DevComponents.Instrumentation.Design.AngleRangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        [Description("Determines the amount to rotate the indicator, specified in degrees.")]
        public float Angle
        {
            get { return (_Angle); }
            set
            {
                if (_Angle != value)
                {
                    _Angle = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region Image
        /// 
        /// Gets or sets the Image to use
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(null)]
        [Description("Indicates the Image to use.")]
        public Image Image
        {
            get { return (_Image); }
            set
            {
                if (_Image != value)
                {
                    _Image = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region Ranges
        /// 
        /// Gets the collection of Ranges associated with the Indicator
        /// 
        [Browsable(true), Category("Behavior")]
        [Description("Indicates the collection of Ranges associated with the Indicator.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Editor("DevComponents.Instrumentation.Design.GaugeCollectionEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        public StateRangeCollection Ranges
        {
            get
            {
                if (_Ranges == null)
                {
                    _Ranges = new StateRangeCollection();
                    _Ranges.CollectionChanged += StateRanges_CollectionChanged;
                }
                return (_Ranges);
            }
            set
            {
                if (_Ranges != null)
                    _Ranges.CollectionChanged -= StateRanges_CollectionChanged;
                _Ranges = value;
                if (_Ranges != null)
                    _Ranges.CollectionChanged += StateRanges_CollectionChanged;
                OnGaugeItemChanged(true);
            }
        }
        #endregion
        #region RoundRectangleArc
        /// 
        /// Gets or sets the RoundRectangle corner radius,
        /// measured as a percentage of the width/height.
        /// 
        [Browsable(true)]
        [Category("Appearance"), DefaultValue(.75f)]
        [Description("Indicates the RoundRectangle corner radius, measured as a percentage of the width/height.")]
        [Editor("DevComponents.Instrumentation.Design.WidthMaxRangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        public float RoundRectangleArc
        {
            get { return (_RoundRectangleArc); }
            set
            {
                if (_RoundRectangleArc != value)
                {
                    if (value < 0 || value > 1f)
                        throw new ArgumentException("Radius must be between 0 and 1");
                    _RoundRectangleArc = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region Style
        /// 
        /// Gets or sets the Indicator Style
        /// 
        [Browsable(true), Category("Behavior"), DefaultValue(StateIndicatorStyle.Circular)]
        [Description("Indicates the Indicator Style.")]
        public StateIndicatorStyle Style
        {
            get { return (_Style); }
            set
            {
                if (_Style != value)
                {
                    _Style = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region TextAlignment
        /// 
        /// Gets or sets the alignment of the text
        /// 
        [Browsable(true)]
        [Category("Layout"), DefaultValue(TextAlignment.MiddleCenter)]
        [Description("Indicates the alignment of the text.")]
        public TextAlignment TextAlignment
        {
            get { return (_TextAlignment); }
            set
            {
                if (_TextAlignment != value)
                {
                    _TextAlignment = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region TextColor
        /// 
        /// Gets or sets the text Color
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(typeof(Color), "Black")]
        [Description("Indicates the text Color.")]
        public Color TextColor
        {
            get { return (_TextColor); }
            set
            {
                if (_TextColor != value)
                {
                    _TextColor = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region TextHorizontalOffset
        /// 
        /// Gets or sets the horizontal distance to offset the Indicator Text, measured as a percentage
        /// 
        [Browsable(true)]
        [Category("Layout"), DefaultValue(0f)]
        [Editor("DevComponents.Instrumentation.Design.OffsetRangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        [Description("Indicates the horizontal distance to offset the Indicator Text, measured as a percentage.")]
        public float TextHorizontalOffset
        {
            get { return (_TextHorizontalOffset); }
            set
            {
                if (_TextHorizontalOffset != value)
                {
                    _TextHorizontalOffset = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region TextVerticalOffset
        /// 
        /// Gets or sets the vertical distance to offset the Indicator Text, measured as a percentage
        /// 
        [Browsable(true)]
        [Category("Layout"), DefaultValue(0f)]
        [Editor("DevComponents.Instrumentation.Design.OffsetRangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        [Description("Indicates the vertical distance to offset the Indicator Text, measured as a percentage.")]
        public float TextVerticalOffset
        {
            get { return (_TextVerticalOffset); }
            set
            {
                if (_TextVerticalOffset != value)
                {
                    _TextVerticalOffset = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #endregion
        #region Internal properties
        #region AbsFont
        internal override Font AbsFont
        {
            get
            {
                if (AutoSize == false)
                    return (Font);
                if (_AbsFont == null)
                {
                    int n = Math.Min(Bounds.Width, Bounds.Height);
                    float emSize = Font.SizeInPoints;
                    emSize = (emSize / 40) * n;
                    AbsFont = new Font(Font.FontFamily, emSize, Font.Style);
                }
                return (_AbsFont);
            }
            set
            {
                if (_AbsFont != null)
                    _AbsFont.Dispose();
                _AbsFont = value;
            }
        }
        #endregion
        #endregion
        #region Event processing
        #region StateRange processing
        void StateRanges_CollectionChanged(object sender, EventArgs e)
        {
            foreach (StateRange range in _Ranges)
            {
                range.StateIndicator = this;
                range.IndicatorRangeChanged -= StateRange_ItemChanged;
                range.IndicatorRangeChanged += StateRange_ItemChanged;
            }
        }
        void StateRange_ItemChanged(object sender, EventArgs e)
        {
            OnGaugeItemChanged(true);
        }
        #endregion
        #endregion
        #region RecalcLayout
        public override void RecalcLayout()
        {
            if (NeedRecalcLayout == true)
            {
                Size size = Bounds.Size;
                
                base.RecalcLayout();
                if (size.Equals(Bounds.Size) == false)
                    AbsFont = null;
            }
        }
        #endregion
        #region OnPaint
        #region OnPaint
        public override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            RecalcLayout();
            if (GaugeControl.OnPreRenderIndicator(e, this) == false)
            {
                InterpolationMode im = g.InterpolationMode;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.TranslateTransform(Center.X, Center.Y);
                g.RotateTransform(_Angle % 360);
                Rectangle r = new Rectangle(0, 0, Bounds.Width, Bounds.Height);
                r.X -= Bounds.Width / 2;
                r.Y -= Bounds.Height / 2;
                StateRange range = GetStateRange();
                DrawStateImage(g, r, range);
                DrawStateText(g, r, range);
                GaugeControl.OnPostRenderIndicator(e, this);
                g.ResetTransform();
                g.InterpolationMode = im;
            }
        }
        #region GetStateRange
        private StateRange GetStateRange()
        {
            return (_Ranges != null ? _Ranges.GetValueRange(DValue) : null);
        }
        #endregion
        #region DrawStateImage
        private void DrawStateImage(Graphics g, Rectangle r, StateRange range)
        {
            Image image = (range != null) ? range.Image : _Image;
            if (image != null)
            {
                if (AutoSize == true)
                {
                    g.DrawImage(image, r);
                }
                else
                {
                    r.X += (r.Width - image.Width) / 2;
                    r.Y += (r.Height - image.Height) / 2;
                    g.DrawImageUnscaled(image, r);
                }
            }
            else
            {
                GradientFillColor fillColor =
                    (range != null) ? range.BackColor : BackColor;
                using (GraphicsPath path = GetStatePath(r))
                    RenderBackPath(g, path, r, fillColor);
            }
        }
        #region GetStatePath
        private GraphicsPath GetStatePath(Rectangle r)
        {
            switch (_Style)
            {
                case StateIndicatorStyle.Circular:
                    return (GetCircularState(r));
                case StateIndicatorStyle.Rectangular:
                    return (GetRectState(r));
                default:
                    return (GetRoundRectState(r));
            }
        }
        #endregion
        #region GetCircularState
        private GraphicsPath GetCircularState(Rectangle bounds)
        {
            GraphicsPath path = new GraphicsPath();
            path.AddEllipse(bounds);
            return (path);
        }
        #endregion
        #region GetRectState
        private GraphicsPath GetRectState(Rectangle bounds)
        {
            GraphicsPath path = new GraphicsPath();
            path.AddRectangle(bounds);
            return (path);
        }
        #endregion
        #region GetRoundRectState
        private GraphicsPath GetRoundRectState(Rectangle bounds)
        {
            GraphicsPath path = new GraphicsPath();
            int m = Math.Min(bounds.Width, bounds.Height);
            int n = (int)(m * _RoundRectangleArc) + 1;
            Rectangle t = new Rectangle(bounds.Right - n, bounds.Bottom - n, n, n);
            path.AddArc(t, 0, 90);
            t.X = bounds.X;
            path.AddArc(t, 90, 90);
            t.Y = bounds.Y;
            path.AddArc(t, 180, 90);
            t.X = bounds.Right - n;
            path.AddArc(t, 270, 90);
            path.CloseAllFigures();
            return (path);
        }
        #endregion
        #region RenderBackPath
        private void RenderBackPath(Graphics g,
            GraphicsPath path, Rectangle bounds, GradientFillColor fillColor)
        {
            Rectangle r = bounds;
            GradientFillType fillType = fillColor.GradientFillType;
            if (fillColor.End.IsEmpty)
            {
                fillType = GradientFillType.None;
            }
            else
            {
                if (fillColor.GradientFillType == GradientFillType.Auto)
                    fillType = GradientFillType.Center;
            }
            switch (fillType)
            {
                case GradientFillType.Angle:
                    using (Brush br = fillColor.GetBrush(r))
                    {
                        if (br is LinearGradientBrush)
                            ((LinearGradientBrush)br).WrapMode = WrapMode.TileFlipXY;
                        g.FillPath(br, path);
                    }
                    break;
                case GradientFillType.StartToEnd:
                    using (Brush br = fillColor.GetBrush(r, 90))
                    {
                        if (br is LinearGradientBrush)
                            ((LinearGradientBrush)br).WrapMode = WrapMode.TileFlipXY;
                        g.FillPath(br, path);
                    }
                    break;
                case GradientFillType.HorizontalCenter:
                    r.Height /= 2;
                    if (r.Height <= 0)
                        r.Height = 1;
                    using (LinearGradientBrush br = new
                        LinearGradientBrush(r, fillColor.Start, fillColor.End, 90))
                    {
                        br.WrapMode = WrapMode.TileFlipXY;
                        g.FillPath(br, path);
                    }
                    break;
                case GradientFillType.VerticalCenter:
                    r.Width /= 2;
                    if (r.Width <= 0)
                        r.Width = 1;
                    using (LinearGradientBrush br = new
                        LinearGradientBrush(r, fillColor.Start, fillColor.End, 0f))
                    {
                        br.WrapMode = WrapMode.TileFlipXY;
                        g.FillPath(br, path);
                    }
                    break;
                case GradientFillType.Center:
                    using (PathGradientBrush br = new PathGradientBrush(path))
                    {
                        br.CenterPoint = new PointF(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2);
                        br.CenterColor = fillColor.Start;
                        br.SurroundColors = new Color[] { fillColor.End };
                        g.FillPath(br, path);
                    }
                    break;
                default:
                    using (Brush br = new SolidBrush(fillColor.Start))
                        g.FillPath(br, path);
                    break;
            }
            if (fillColor.BorderWidth > 0 && fillColor.BorderColor.IsEmpty == false)
            {
                using (Pen pen = new Pen(fillColor.BorderColor, fillColor.BorderWidth))
                    g.DrawPath(pen, path);
            }
            else
            {
                if (fillType == GradientFillType.Center)
                {
                    using (Pen pen = new Pen(fillColor.End))
                        g.DrawPath(pen, path);
                }
            }
        }
        #endregion
        #endregion
        #region DrawStateText
        private void DrawStateText(Graphics g, Rectangle r, StateRange range)
        {
            string text = (range != null)
                ? range.Text : GetOverrideString();
            if (string.IsNullOrEmpty(text) == false)
            {
                using (StringFormat sf = new StringFormat())
                {
                    SetStringAlignment(sf);
                    Color color = _TextColor;
                    if (range != null)
                    {
                        color = range.TextColor;
                        int dx = (int)(r.Width * range.TextHorizontalOffset);
                        int dy = (int)(r.Height * range.TextVerticalOffset);
                        r.Offset(dx, dy);
                    }
                    else
                    {
                        int dx = (int)(r.Width * _TextHorizontalOffset);
                        int dy = (int)(r.Height * _TextVerticalOffset);
                        r.Offset(dx, dy);
                    }
                    using (Brush br = new SolidBrush(color))
                        g.DrawString(text, AbsFont, br, r, sf);
                }
            }
        }
        #region SetStringAlignment
        private void SetStringAlignment(StringFormat sf)
        {
            switch (_TextAlignment)
            {
                case TextAlignment.TopLeft:
                    sf.LineAlignment = StringAlignment.Near;
                    sf.Alignment = StringAlignment.Near;
                    break;
                case TextAlignment.TopCenter:
                    sf.LineAlignment = StringAlignment.Near;
                    sf.Alignment = StringAlignment.Center;
                    break;
                case TextAlignment.TopRight:
                    sf.LineAlignment = StringAlignment.Near;
                    sf.Alignment = StringAlignment.Far;
                    break;
                case TextAlignment.MiddleLeft:
                    sf.LineAlignment = StringAlignment.Center;
                    sf.Alignment = StringAlignment.Near;
                    break;
                case TextAlignment.MiddleCenter:
                    sf.LineAlignment = StringAlignment.Center;
                    sf.Alignment = StringAlignment.Center;
                    break;
                case TextAlignment.MiddleRight:
                    sf.LineAlignment = StringAlignment.Center;
                    sf.Alignment = StringAlignment.Far;
                    break;
                case TextAlignment.BottomLeft:
                    sf.LineAlignment = StringAlignment.Far;
                    sf.Alignment = StringAlignment.Near;
                    break;
                case TextAlignment.BottomCenter:
                    sf.LineAlignment = StringAlignment.Far;
                    sf.Alignment = StringAlignment.Center;
                    break;
                case TextAlignment.BottomRight:
                    sf.LineAlignment = StringAlignment.Far;
                    sf.Alignment = StringAlignment.Far;
                    break;
            }
        }
        #endregion
        #endregion
        #endregion
        #endregion
        #region Refresh
        internal void Refresh()
        {
            if (NeedRecalcLayout == false)
                OnGaugeItemChanged(false);
        }
        #endregion
        #region Contains
        internal override bool Contains(Point pt)
        {
            using (GraphicsPath path = GetStatePath(Bounds))
            {
                Matrix matrix = new Matrix();
                matrix.RotateAt(_Angle, Center);
                path.Transform(matrix);
                return (path.IsVisible(pt));
            }
        }
        #endregion
        #region CopyToItem
        public override void CopyToItem(GaugeItem copy)
        {
            StateIndicator c = copy as StateIndicator;
            if (c != null)
            {
                base.CopyToItem(c);
                c.Angle = _Angle;
                c.Image = _Image;
                if (_Ranges != null)
                    c.Ranges = (StateRangeCollection)_Ranges.Clone();
                c.RoundRectangleArc = _RoundRectangleArc;
                c.Style = _Style;
                c.TextAlignment = _TextAlignment;
                c.TextColor = _TextColor;
                c.TextHorizontalOffset = _TextHorizontalOffset;
                c.TextVerticalOffset = _TextVerticalOffset;
            }
        }
        #endregion
    }
    #region Enums
    public enum StateIndicatorStyle
    {
        Rectangular,
        RoundedRectangular,
        Circular
    }
    #endregion
}