using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Globalization;
using System.Windows.Forms;
namespace DevComponents.Instrumentation
{
    [TypeConverter(typeof(GaugePinConvertor))]
    public class GaugePin : GaugeItem
    {
        #region Private variables
        private GaugeMarkerStyle _Style = GaugeMarkerStyle.Circle;
        private DisplayPlacement _Placement = DisplayPlacement.Center;
        private float _ScaleOffset;         // Relative distance from scale
        private float _EndOffset;           // Relative distance or degrees from end
        private GradientFillColor _FillColor;
        private Image _Image;
        private int _Radius;
        private float _Angle;
        private Rectangle _Bounds;
        private float _Width;
        private float _Length;
        private bool _IsMaxPin;
        private GaugeMarker _GaugeMarker;
        private GaugePinLabel _Label;
        private GaugeScale _Scale;
        #endregion
        public GaugePin(GaugeScale scale, bool isMmaxPin, string name)
        {
            _Scale = scale;
            _IsMaxPin = isMmaxPin;
            Name = name;
            _GaugeMarker = new GaugeMarker();
            _Label = new GaugePinLabel(this);
            FillColor = new GradientFillColor(Color.WhiteSmoke);
            FillColor.BorderColor = Color.DimGray;
            FillColor.BorderWidth = 1;
            Length = .06f;
            Width = .06f;
            _EndOffset = .02f;
            HookEvents(true);
        }
        #region Public properties
        #region EndOffset
        /// 
        /// Gets or sets the offset from the end of the scale, specified as a percentage
        /// 
        [Browsable(true), Category("Layout"), DefaultValue(.02f)]
        [Editor("DevComponents.Instrumentation.Design.OffsetRangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        [Description("Indicates the offset from the end of the scale, specified as a percentage.")]
        public float EndOffset
        {
            get { return (_EndOffset); }
            set
            {
                if (_EndOffset != value)
                {
                    if (value < -1 || value > 1)
                        throw new ArgumentException("Value must be between -1 and +1.");
                    _EndOffset = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region FillColor
        /// 
        /// Gets or sets Indicates the Pin FillColor
        /// 
        [Browsable(true), Category("Appearance")]
        [Description("Indicates the Pin FillColor.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public GradientFillColor FillColor
        {
            get
            {
                if (_FillColor == null)
                {
                    _FillColor = new GradientFillColor();
                    _FillColor.ColorTableChanged += FillColor_ColorTableChanged;
                }
                return (_FillColor);
            }
            set
            {
                if (_FillColor != null)
                    _FillColor.ColorTableChanged -= FillColor_ColorTableChanged;
                _FillColor = value;
                if (_FillColor != null)
                    _FillColor.ColorTableChanged += FillColor_ColorTableChanged;
                OnGaugeItemChanged(true);
            }
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        internal bool ShouldSerializeFillColor()
        {
            return (FillColor.IsEqualTo(Color.WhiteSmoke,
                Color.Empty, 90, GradientFillType.Auto, Color.DimGray, 1) == false);
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        internal void ResetFillColor()
        {
            FillColor = new GradientFillColor(Color.WhiteSmoke);
            FillColor.BorderColor = Color.DimGray;
            FillColor.BorderWidth = 1;
        }
        #endregion
        #region Image
        /// 
        /// Gets or sets the Image to use for the Pin
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(null)]
        [Description("Indicates the Image to use for the Pin.")]
        public Image Image
        {
            get { return (_Image); }
            set
            {
                if (_Image != value)
                {
                    _Image = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region IsMaxPin
        /// 
        /// Gets whether the pin is the Maximum Pin
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool IsMaxPin
        {
            get { return (_IsMaxPin); }
            internal set { _IsMaxPin = value; }
        }
        #endregion
        #region Label
        /// 
        /// Gets or sets the Label associated with the Pin
        /// 
        [Browsable(true), Category("Appearance")]
        [Description("Indicates the Label associated with the Pin.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public GaugePinLabel Label
        {
            get { return (_Label); }
            internal set { _Label = value; }
        }
        #endregion
        #region Length
        /// 
        /// Gets or sets the Length of the Pin, specified as a percentage
        /// 
        [Browsable(true), Category("Layout"), DefaultValue(.06f)]
        [Editor("DevComponents.Instrumentation.Design.RangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        [Description("Indicates the Length of the Pin, specified as a percentage.")]
        public float Length
        {
            get { return (_Length); }
            set
            {
                if (_Length != value)
                {
                    if (value < 0 || value > 1)
                        throw new ArgumentException("Value must be between 0 and +1.");
                    _Length = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region Placement
        /// 
        /// Gets or sets the Placement of the Pin with respect to the Scale
        /// 
        [Browsable(true)]
        [Category("Layout"), DefaultValue(DisplayPlacement.Center)]
        [Description("Indicates the Placement of the Pin with respect to the Scale.")]
        public DisplayPlacement Placement
        {
            get { return (_Placement); }
            set
            {
                if (_Placement != value)
                {
                    _Placement = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region Scale
        /// 
        /// Gets the pin's associated Scale
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public GaugeScale Scale
        {
            get { return (_Scale); }
            internal set
            {
                _Scale = value;
                _Label.Scale = value;
                OnGaugeItemChanged(true);
            }
        }
        #endregion
        #region ScaleOffset
        /// 
        /// Gets or sets the distance from the Pin to the Scale, 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 distance from the Pin to the Scale, measured as a percentage.")]
        public float ScaleOffset
        {
            get { return (_ScaleOffset); }
            set
            {
                if (_ScaleOffset != value)
                {
                    _ScaleOffset = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        
        #region Style
        /// 
        /// Gets or sets the Pin display style
        /// 
        [Browsable(true)]
        [Category("Appearance"), DefaultValue(GaugeMarkerStyle.Circle)]
        [Description("Indicates the Pin display style.")]
        public GaugeMarkerStyle Style
        {
            get { return (_Style); }
            set
            {
                if (_Style != value)
                {
                    _Style = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region Width
        /// 
        /// Gets or sets the Width of the Pin, specified as a percentage
        /// 
        [Browsable(true), Category("Layout"), DefaultValue(.06f)]
        [Editor("DevComponents.Instrumentation.Design.WidthRangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        [Description("Indicates the Width of the Pin, specified as a percentage.")]
        public float Width
        {
            get { return (_Width); }
            set
            {
                if (_Width != value)
                {
                    if (value < 0 || value > 1)
                        throw new ArgumentException("Value must be between 0 and +1.");
                    _Width = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #endregion
        #region Internal properties
        #region Angle
        internal float Angle
        {
            get { return (_Angle); }
        }
        #endregion
        #region Bounds
        internal Rectangle Bounds
        {
            get { return (_Bounds); }
            set { _Bounds = value; }
        }
        #endregion
        #endregion
        #region HookEvents
        private void HookEvents(bool hook)
        {
            if (hook == true)
                _Label.GaugeItemChanged += GaugePinLabel_GaugeItemChanged;
            else
                _Label.GaugeItemChanged -= GaugePinLabel_GaugeItemChanged;
        }
        #endregion
        #region Event processing
        void FillColor_ColorTableChanged(object sender, EventArgs e)
        {
            OnGaugeItemChanged(true);
        }
        void GaugePinLabel_GaugeItemChanged(object sender, EventArgs e)
        {
            OnGaugeItemChanged(true);
        }
        #endregion
        #region RecalcLayout
        public override void RecalcLayout()
        {
            if (NeedRecalcLayout == true)
            {
                base.RecalcLayout();
                CalcPinMetrics();
                _GaugeMarker.Clear();
                _Label.NeedRecalcLayout = true;
            }
        }
        #region CalcPinMetrics
        private void CalcPinMetrics()
        {
            if (Scale is GaugeCircularScale)
                CalcCircularMetrics(Scale as GaugeCircularScale);
            else if (Scale is GaugeLinearScale)
                CalcLinearMetrics(Scale as GaugeLinearScale);
        }
        #region CalcCircularMetrics
        private void CalcCircularMetrics(GaugeCircularScale scale)
        {
            _Radius = scale.AbsRadius;
            _Bounds = new Rectangle(0, 0, (int)(_Radius * _Width), (int)(_Radius * _Length));
            int m = scale.AbsScaleWidth;
            int offset = (int)(scale.AbsRadius * _ScaleOffset);
            switch (_Placement)
            {
                case DisplayPlacement.Near:
                    _Radius -= ((_Bounds.Height + m) / 2 + offset);
                    break;
                case DisplayPlacement.Center:
                    _Radius += (offset);
                    break;
                case DisplayPlacement.Far:
                    _Radius += ((_Bounds.Height + m) / 2 + offset);
                    break;
            }
            float angle = _EndOffset * 360;
            if (_IsMaxPin != scale.Reversed)
                _Angle = scale.StartAngle + scale.SweepAngle + angle;
            else
                _Angle = scale.StartAngle - angle;
            Point pt = scale.GetPoint(_Radius, _Angle);
            _Bounds.Location = new Point(pt.X - _Bounds.Width / 2, pt.Y - _Bounds.Height / 2);
        }
        #endregion
        #region CalcLinearMetrics
        private void CalcLinearMetrics(GaugeLinearScale scale)
        {
            if (scale.Orientation == Orientation.Horizontal)
                CalcHorizontalMetrics(scale);
            else
                CalcVerticalMetrics(scale);
        }
        #region CalcHorizontalMetrics
        private void CalcHorizontalMetrics(GaugeLinearScale scale)
        {
            int width = scale.AbsWidth;
            int offset = (int)(width * _ScaleOffset);
            int m = scale.AbsScaleWidth;
            _Bounds = new Rectangle(0, 0, (int)(width * _Width), (int)(width * _Length));
            int x = (int)(_EndOffset * scale.AbsLength);
            if (_IsMaxPin != scale.Reversed)
                x += scale.ScaleBounds.Right;
            else
                x = scale.ScaleBounds.Left - x;
            x -= _Bounds.Width / 2;
            int y = scale.ScaleBounds.Y;
            switch (_Placement)
            {
                case DisplayPlacement.Near:
                    y -= (_Bounds.Height + offset);
                    break;
                case DisplayPlacement.Center:
                    y -= ((_Bounds.Height - m) / 2 + offset);
                    break;
                case DisplayPlacement.Far:
                    y += (m + offset);
                    break;
            }
            _Bounds.Location = new Point(x, y);
        }
        #endregion
        #region CalcVerticalMetrics
        private void CalcVerticalMetrics(GaugeLinearScale scale)
        {
            int width = scale.AbsWidth;
            int offset = (int)(width * _ScaleOffset);
            int m = scale.AbsScaleWidth;
            _Bounds = new Rectangle(0, 0, (int)(width * _Width), (int)(width * _Length));
            int y = (int)(_EndOffset * scale.AbsLength);
            if (_IsMaxPin != scale.Reversed)
                y = scale.ScaleBounds.Top - y;
            else
                y += scale.ScaleBounds.Bottom;
            y -= _Bounds.Height / 2;
            int x = scale.ScaleBounds.X;
            switch (_Placement)
            {
                case DisplayPlacement.Near:
                    x -= (_Bounds.Width + offset);
                    break;
                case DisplayPlacement.Center:
                    x -= ((_Bounds.Width - m ) / 2 + offset);
                    break;
                case DisplayPlacement.Far:
                    x += (m + offset);
                    break;
            }
            _Bounds.Location = new Point(x, y);
        }
        #endregion
        #endregion
        #endregion
        #endregion
        #region OnPaint
        public override void OnPaint(PaintEventArgs e)
        {
            RecalcLayout();
            if (Scale.GaugeControl.OnPreRenderScaleGaugePin(e, this) == false)
            {
                Graphics g = e.Graphics;
                if (_Bounds.Width > 0 && _Bounds.Height > 0)
                {
                    Image image = _Image ?? GetPinBitmap(g);
                    if (image != null)
                    {
                        if (Scale is GaugeCircularScale)
                            PaintCircularPin(g, image);
                        else if (Scale is GaugeLinearScale)
                            PaintLinearPin(g, image);
                    }
                    if (_Label.Visible)
                        _Label.OnPaint(e);
                }
                Scale.GaugeControl.OnPostRenderScaleGaugePin(e, this);
            }
        }
        #region PaintCircularPin
        private void PaintCircularPin(Graphics g, Image image)
        {
            Rectangle r = new Rectangle(0, 0, _Bounds.Width, _Bounds.Height);
            float angle = _Angle + 90;
            if (_Placement == DisplayPlacement.Near)
                angle += 180;
            g.TranslateTransform(_Bounds.X + _Bounds.Width / 2, _Bounds.Y + _Bounds.Height / 2);
            g.RotateTransform(angle % 360);
            r.X -= _Bounds.Width / 2;
            r.Y -= _Bounds.Height / 2;
            g.DrawImage(image, r);
            g.ResetTransform();
        }
        #endregion
        #region PaintLinearPin
        #region PaintLinearPin
        private void PaintLinearPin(Graphics g, Image image)
        {
            g.DrawImage(image, _Bounds);
        }
        #endregion
        #endregion
        #region GetPinBitmap
        internal Bitmap GetPinBitmap(Graphics g)
        {
            if (_Style != GaugeMarkerStyle.None)
                return (_GaugeMarker.GetMarkerBitmap(g, _Style, _FillColor, _Bounds.Width, _Bounds.Height));
            return (null);
        }
        #endregion
        #endregion
        #region OnDispose
        protected override void OnDispose()
        {
            HookEvents(false);
            _GaugeMarker.Dispose();
            base.OnDispose();
        }
        #endregion
        #region ICloneable Members
        public override object Clone()
        {
            GaugePin copy = new
                GaugePin(_Scale, _IsMaxPin, Name);
            CopyToItem(copy);
            return (copy);
        }
        #endregion
        #region CopyToItem
        public override void CopyToItem(GaugeItem copy)
        {
            GaugePin c = copy as GaugePin;
            if (c != null)
            {
                base.CopyToItem(c);
                c.EndOffset = _EndOffset;
                if (_FillColor != null)
                    c.FillColor = (GradientFillColor)_FillColor.Clone();
                c.Image = _Image;
                c.IsMaxPin = _IsMaxPin;
                c.Length = _Length;
                c.Placement = _Placement;
                c.Scale = _Scale;
                c.ScaleOffset = _ScaleOffset;
                c.Style = _Style;
                c.Width = _Width;
                c.Label = new GaugePinLabel(c);
                _Label.CopyToItem(c.Label);
            }
        }
        #endregion
    }
    #region GaugePinConvertor
    public class GaugePinConvertor : ExpandableObjectConverter
    {
        public override object ConvertTo(
            ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                GaugePin pin = value as GaugePin;
                if (pin != null)
                {
                    //ColorConverter cvt = new ColorConverter();
                    //if (lct.Start != Color.Empty)
                    //    return (cvt.ConvertToString(lct.Start));
                    //if (lct.End != Color.Empty)
                    //    return (cvt.ConvertToString(lct.End));
                    //if (lct.GradientAngle != 90)
                    //    return (lct.GradientAngle.ToString());
                    return (String.Empty);
                }
            }
            return (base.ConvertTo(context, culture, value, destinationType));
        }
    }
    #endregion
}