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 NumericIndicator : GaugeIndicator
    {
        #region Private variables
        private NumericIndicatorStyle _Style;
        private int _NumberOfDigits;
        private int _NumberOfDecimals;
        private NumericElement[] _Digits;
        private Color _DigitColor;
        private Color _DigitDimColor;
        private Color _DecimalColor;
        private Color _DecimalDimColor;
        private float _SegmentWidth;
        private float _SeparatorWidth;
        private GradientFillColor _SeparatorColor;
        private DisplayPlacement _DigitPlacement;
        private string _FormatString;
        private Font _AbsFont;
        private bool _ShowDecimalPoint;
        private bool _ShowLeadingZeros;
        private bool _ShowDimColonPoints;
        private bool _ShowDimDecimalPoint;
        private bool _ShowDimSegments;
        private SignVisibility _SignVisibility;
        private float _ShearFactor;
        private Padding _Padding;
        private NumericRangeCollection _Ranges;
        #endregion
        public NumericIndicator()
        {
            InitIndicator();
        }
        #region InitIndicator
        private void InitIndicator()
        {
            _NumberOfDigits = 6;
            _NumberOfDecimals = 2;
            _DecimalColor = Color.Firebrick;
            _DigitColor = Color.SteelBlue;
            _DigitPlacement = DisplayPlacement.Far;
            _Padding = new Padding(0);
            _SegmentWidth = .5f;
            _SeparatorWidth = .01f;
            _ShearFactor = -0.1f;
            _ShowDimColonPoints = true;
            _ShowDimDecimalPoint = true;
            _ShowDimSegments = true;
            _SignVisibility = SignVisibility.WhenNegative;
            _Style = NumericIndicatorStyle.Mechanical;
            AllocateElements();
        }
        #endregion
        #region Public properties
        #region DecimalColor
        /// 
        /// Gets or sets the default Decimal Color
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(typeof(Color), "FireBrick")]
        [Description("Indicates the default Decimal Color.")]
        public Color DecimalColor
        {
            get { return (_DecimalColor); }
            set
            {
                if (_DecimalColor != value)
                {
                    _DecimalColor = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region DecimalDimColor
        /// 
        /// Gets or sets the default Decimal Dim Color (Dim LED color)
        /// 
        [Browsable(true), Category("Appearance")]
        [Description("Indicates the default Decimal Dim Color (Dim LED color).")]
        public Color DecimalDimColor
        {
            get { return (_DecimalDimColor); }
            set
            {
                if (_DecimalDimColor != value)
                {
                    _DecimalDimColor = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        internal bool ShouldSerializeDecimalDimColor()
        {
            return (_DecimalDimColor.IsEmpty == false);
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        internal void ResetDecimalDimColor()
        {
            _DecimalDimColor = Color.Empty;
        }
        #endregion
        #region DigitColor
        /// 
        /// Gets or sets the default Digit Color
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(typeof(Color), "SteelBlue")]
        [Description("Indicates the default Digit Color.")]
        public Color DigitColor
        {
            get { return (_DigitColor); }
            set
            {
                if (_DigitColor != value)
                {
                    _DigitColor = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region DigitDimColor
        /// 
        /// Gets or sets the default Digit Dim Color (Dim LED color)
        /// 
        [Browsable(true), Category("Appearance")]
        [Description("Indicates the default Digit Dim Color (Dim LED color).")]
        public Color DigitDimColor
        {
            get { return (_DigitDimColor); }
            set
            {
                if (_DigitDimColor != value)
                {
                    _DigitDimColor = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        internal bool ShouldSerializeDigitDimColor()
        {
            return (_DigitDimColor.IsEmpty == false);
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        internal void ResetDigitDimColor()
        {
            _DigitDimColor = Color.Empty;
        }
        #endregion
        #region DigitPlacement
        /// 
        /// Gets or sets the Placement of the digits within the indicator
        /// 
        [Browsable(true)]
        [Category("Appearance"), DefaultValue(DisplayPlacement.Far)]
        [Description("Indicates the Alignment of the digits.")]
        public DisplayPlacement DigitPlacement
        {
            get { return (_DigitPlacement); }
            set
            {
                if (_DigitPlacement != value)
                {
                    _DigitPlacement = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region Digits
        /// 
        /// Gets the array of defined digits (units and decimals).
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public NumericElement[] Digits
        {
            get { return (_Digits); }
        }
        #endregion
        #region FormatString
        /// 
        /// Gets or sets the .Net format string to use when displaying the indicator value.
        /// 
        [Browsable(true), Category("Behavior"), DefaultValue(null)]
        [Description("Indicates the .Net format string to use when displaying the indicator value.")]
        [Editor("DevComponents.Instrumentation.Design.FormatStringEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        public string FormatString
        {
            get { return (_FormatString); }
            set
            {
                if (_FormatString != value)
                {
                    _FormatString = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region NumberOfDecimals
        /// 
        /// Gets or sets the number of decimals to display.
        /// 
        [Browsable(true)]
        [Category("Appearance"), DefaultValue(2)]
        [Description("Determines the number of decimals to display.")]
        public int NumberOfDecimals
        {
            get { return (_NumberOfDecimals); }
            set
            {
                if (_NumberOfDecimals != value)
                {
                    if (value > _NumberOfDigits)
                        throw new ArgumentException("Number of decimals can not exceed NumberOfDigits.");
                    _NumberOfDecimals = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region NumberOfDigits
        /// 
        /// Gets or sets the number of total digits to display (units and decimals).
        /// 
        [Browsable(true)]
        [Category("Appearance"), DefaultValue(6)]
        [Description("Determines the number of total digits to display (units and decimals).")]
        public int NumberOfDigits
        {
            get { return (_NumberOfDigits); }
            set
            {
                if (_NumberOfDigits != value)
                {
                    if (value < _NumberOfDecimals)
                        throw new ArgumentException("Number of digits can not be less than NumberOfDecimals.");
                    _NumberOfDigits = value;
                    AbsFont = null;
                    AllocateElements();
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region Padding
        /// 
        /// Gets or sets the indicator padding.
        /// 
        [Browsable(true), Category("Layout")]
        [Description("Indicates the indicator padding.")]
        public Padding Padding
        {
            get { return (_Padding); }
            set
            {
                if (_Padding != value)
                {
                    _Padding = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        internal bool ShouldSerializePadding()
        {
            return (_Padding.All != 0);
        }
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        internal void ResetPadding()
        {
            _Padding = new Padding(0);
        }
        #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 NumericRangeCollection Ranges
        {
            get
            {
                if (_Ranges == null)
                {
                    _Ranges = new NumericRangeCollection();
                    _Ranges.CollectionChanged += NumericRanges_CollectionChanged;
                }
                return (_Ranges);
            }
            set
            {
                if (_Ranges != null)
                    _Ranges.CollectionChanged -= NumericRanges_CollectionChanged;
                _Ranges = value;
                if (_Ranges != null)
                    _Ranges.CollectionChanged += NumericRanges_CollectionChanged;
                OnGaugeItemChanged(true);
            }
        }
        #endregion
        #region SegmentWidth
        /// 
        /// Gets or sets the Digital segment width, specified as a percentage
        /// 
        [Browsable(true), Category("Layout"), DefaultValue(.5f)]
        [Editor("DevComponents.Instrumentation.Design.RangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        [Description("Indicates the Digital element width, specified as a percentage.")]
        public float SegmentWidth
        {
            get { return (_SegmentWidth); }
            set
            {
                if (_SegmentWidth != value)
                {
                    if (value < 0 || value > 1)
                        throw new ArgumentException("Value must be between 0 and +1.");
                    _SegmentWidth = value;
                    if (_Style != NumericIndicatorStyle.Mechanical)
                        SetRecalcSegments();
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region SeparatorColor
        /// 
        /// Gets or sets the Digit Separator Color
        /// 
        [Browsable(true), Category("Appearance")]
        [Description("Indicates the Digit Separator Color.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public GradientFillColor SeparatorColor
        {
            get
            {
                if (_SeparatorColor == null)
                {
                    _SeparatorColor = new GradientFillColor();
                    _SeparatorColor.ColorTableChanged += Color_ColorTableChanged;
                }
                return (_SeparatorColor);
            }
            set
            {
                if (_SeparatorColor != null)
                    _SeparatorColor.ColorTableChanged -= Color_ColorTableChanged;
                _SeparatorColor = value;
                if (_SeparatorColor != null)
                    _SeparatorColor.ColorTableChanged += Color_ColorTableChanged;
                OnGaugeItemChanged();
            }
        }
        #endregion
        #region SeparatorWidth
        /// 
        /// Gets or sets the Length of the Pin, specified as a percentage
        /// 
        [Browsable(true), Category("Layout"), DefaultValue(.01f)]
        [Editor("DevComponents.Instrumentation.Design.RangeValueEditor, DevComponents.Instrumentation.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=76cb4c6eb576bca5", typeof(UITypeEditor))]
        [Description("Indicates the width of the Mechanical Style Digits Separator, specified as a percentage.")]
        public float SeparatorWidth
        {
            get { return (_SeparatorWidth); }
            set
            {
                if (_SeparatorWidth != value)
                {
                    if (value < 0 || value > 1)
                        throw new ArgumentException("Value must be between 0 and +1.");
                    _SeparatorWidth = value;
                    AbsFont = null;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region ShearFactor
        /// 
        /// Gets or Sets the shear coefficient for italicizing the Digits display
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(-0.1F)]
        [Description("Indicates the shear coefficient for italicizing the Digits display. Default is -0.1")]
        public float ShearFactor
        {
            get { return (_ShearFactor); }
            set
            {
                if (_ShearFactor != value)
                {
                    _ShearFactor = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region ShowDecimalPoint
        /// 
        /// Gets or sets whether the decimal point should be displayed
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(false)]
        [Description("Indicates whether the decimal point should be displayed.")]
        public bool ShowDecimalPoint
        {
            get { return (_ShowDecimalPoint); }
            set
            {
                if (_ShowDecimalPoint != value)
                {
                    _ShowDecimalPoint = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region ShowDimColonPoints
        /// 
        /// Gets or sets whether dim colon points should be displayed
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(true)]
        [Description("Indicates whether dim colon points should be displayed.")]
        public bool ShowDimColonPoints
        {
            get { return (_ShowDimColonPoints); }
            set
            {
                if (_ShowDimColonPoints != value)
                {
                    _ShowDimColonPoints = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region ShowDimDecimalPoint
        /// 
        /// Gets or sets whether dim decimal points should be displayed
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(true)]
        [Description("Indicates whether dim decimal points should be displayed.")]
        public bool ShowDimDecimalPoint
        {
            get { return (_ShowDimDecimalPoint); }
            set
            {
                if (_ShowDimDecimalPoint != value)
                {
                    _ShowDimDecimalPoint = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region ShowDimSegments
        /// 
        /// Gets or sets whether dim segments should be displayed
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(true)]
        [Description("Indicates whether dim segments should be displayed.")]
        public bool ShowDimSegments
        {
            get { return (_ShowDimSegments); }
            set
            {
                if (_ShowDimSegments != value)
                {
                    _ShowDimSegments = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region ShowLeadingZeros
        /// 
        /// Gets or sets whether leading zeros should be displayed
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(false)]
        [Description("Indicates whether leading zeros should be displayed.")]
        public bool ShowLeadingZeros
        {
            get { return (_ShowLeadingZeros); }
            set
            {
                if (_ShowLeadingZeros != value)
                {
                    _ShowLeadingZeros = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region SignVisibility
        /// 
        /// Gets or sets when the +/- sign should be displayed
        /// 
        [Browsable(true), Category("Appearance"), DefaultValue(SignVisibility.WhenNegative)]
        [Description("Indicates when the +/- sign should be displayed.")]
        public SignVisibility SignVisibility
        {
            get { return (_SignVisibility); }
            set
            {
                if (_SignVisibility != value)
                {
                    _SignVisibility = value;
                    OnGaugeItemChanged(true);
                }
            }
        }
        #endregion
        #region Style
        /// 
        /// Gets or sets the Indicator Style
        /// 
        [Browsable(true), Category("Behavior"), DefaultValue(NumericIndicatorStyle.Mechanical)]
        [Description("Indicates the Indicator Style.")]
        public NumericIndicatorStyle Style
        {
            get { return (_Style); }
            set
            {
                if (_Style != value)
                {
                    _Style = value;
                    OnGaugeItemChanged(true);
                    AllocateElements();
                }
            }
        }
        #endregion
        #endregion
        #region Internal properties
        #region AbsFont
        internal override Font AbsFont
        {
            get
            {
                if (AutoSize == false || _NumberOfDigits <= 0)
                    return (Font);
                if (_AbsFont == null)
                    AbsFont = GetNewAbsFont();
                return (_AbsFont);
            }
            set
            {
                if (_AbsFont != null)
                    _AbsFont.Dispose();
                _AbsFont = value;
            }
        }
        #region GetNewAbsFont
        private Font GetNewAbsFont()
        {
            int sepWidth = AbsSeparatorWidth;
            int width = (Bounds.Width - (_Padding.Left + _Padding.Right + BackColor.BorderWidth * 2));
            int height = (Bounds.Height - (_Padding.Top + _Padding.Bottom + BackColor.BorderWidth * 2));
            int n = (width - (_NumberOfDigits - 1) * sepWidth) / _NumberOfDigits;
            if (n <= 0)
                n = 2;
            int low = 2;
            int high = 128;
            while (low <= high)
            {
                int mid = (low + high) / 2;
                using (Font font = new Font(Font.FontFamily, mid, Font.Style))
                {
                    Size size = TextRenderer.MeasureText(
                        string.IsNullOrEmpty(Text) ? "8" : "M", font);
                    if (size.Height > height || size.Width > n)
                        high = mid - 1;
                    else if (size.Height < height && size.Width < n)
                        low = mid + 1;
                    else
                    {
                        low = mid;
                        break;
                    }
                }
            }
            return (new Font(Font.FontFamily, low, Font.Style));
        }
        #endregion
        #endregion
        #region AbsSeparatorWidth
        internal int AbsSeparatorWidth
        {
            get { return ((int)(Bounds.Width * _SeparatorWidth)); }
        }
        #endregion
        #endregion
        #region Event processing
        #region Color_ColorTableChanged processing
        void Color_ColorTableChanged(object sender, EventArgs e)
        {
            OnGaugeItemChanged(true);
        }
        #endregion
        #region NumericRange processing
        void NumericRanges_CollectionChanged(object sender, EventArgs e)
        {
            foreach (NumericRange range in _Ranges)
            {
                range.NumericIndicator = this;
                range.IndicatorRangeChanged -= NumericRange_ItemChanged;
                range.IndicatorRangeChanged += NumericRange_ItemChanged;
            }
        }
        void NumericRange_ItemChanged(object sender, EventArgs e)
        {
            OnGaugeItemChanged(true);
        }
        #endregion
        #endregion
        #region SetRecalcSegments
        private void SetRecalcSegments()
        {
            if (_Digits != null)
            {
                foreach (DigitalElement item in _Digits)
                {
                    if (item != null)
                    {
                        item.NeedRecalcLayout = true;
                        item.NeedRecalcSegments = true;
                    }
                }
            }
        }
        #endregion
        #region RecalcLayout
        public override void RecalcLayout()
        {
            if (NeedRecalcLayout == true)
            {
                base.RecalcLayout();
                NeedRecalcLayout = true;
                AbsFont = null;
                if (_NumberOfDigits > 0)
                {
                    LayoutElements();
                    UpdateElements();
                }
                NeedRecalcLayout = false;
            }
        }
        #region AllocateElements
        private void AllocateElements()
        {
            NumericElement[] digit = new NumericElement[_NumberOfDigits];
            for (int i = 0; i < _NumberOfDigits; i++)
            {
                NumericElement d;
                switch (_Style)
                {
                    case NumericIndicatorStyle.Mechanical:
                        d = new MechanicalElement(this);
                        break;
                    case NumericIndicatorStyle.Digital7Segment:
                        d = new Seg7Element(this);
                        break;
                    default:
                        d = new Seg16Element(this);
                        break;
                }
                digit[i] = d;
                if (_Digits != null)
                {
                    if (i < _Digits.Length)
                        _Digits[i].CopyToItem(d);
                }
            }
            if (_Digits != null)
            {
                for (int i = _NumberOfDigits; i < _Digits.Length; i++)
                    _Digits[i].Dispose();
            }
            _Digits = digit;
        }
        #endregion
        #region LayoutElements
        private void LayoutElements()
        {
            if (_NumberOfDigits > 0)
            {
                int sepWidth = AbsSeparatorWidth;
                int width = Bounds.Width - (_Padding.Left + _Padding.Right + BackColor.BorderWidth * 2);
                int height = Bounds.Height - (_Padding.Top + _Padding.Bottom + BackColor.BorderWidth * 2);
                int shear = 0;
                if (Style != NumericIndicatorStyle.Mechanical)
                {
                    int w = width / _NumberOfDigits;
                    if (ShearFactor > 0)
                        width -= (int)(w * _SegmentWidth);
                    if (ShearFactor != 0)
                    {
                        shear = (int)(w * ShearFactor);
                        width -= Math.Abs(shear);
                    }
                }
                int ewidth = (width - (_NumberOfDigits - 1) * sepWidth) / _NumberOfDigits;
                int espread = width - ((ewidth + sepWidth) * _NumberOfDigits - sepWidth);
                Rectangle r = new Rectangle(
                    Bounds.X + BackColor.BorderWidth + _Padding.Left,
                    Bounds.Y + BackColor.BorderWidth + _Padding.Top, ewidth, height);
                if (shear < 0)
                    r.X += -shear;
                for (int i = 0; i < _NumberOfDigits; i++)
                {
                    r.Width = (ewidth + (i < espread ? 1 : 0));
                    _Digits[i].Bounds = r;
                    r.X += (r.Width + sepWidth);
                }
            }
        }
        #endregion
        #region UpdateElements
        private void UpdateElements()
        {
            bool numeric;
            string s = GetOutputString(out numeric);
            if (s != null)
            {
                if (_Style == NumericIndicatorStyle.Mechanical)
                    UpdateMechanicalElements(s, numeric);
                else
                    UpdateSegmentedElements(s, numeric);
            }
        }
        #region GetOutputString
        private string GetOutputString(out bool numeric)
        {
            numeric = false;
            string s = GetOverrideString();
            int nDigits;
            if (s != null)
                return (AlignOutputString(s, false, out nDigits));
            numeric = true;
            return (GetValueString());
        }
        #region GetValueString
        private string GetValueString()
        {
            int n = _NumberOfDigits;
            if ((_Style != NumericIndicatorStyle.Mechanical) || _ShowDecimalPoint == false)
                n++;
            string s = "";
            if (String.IsNullOrEmpty(_FormatString) == false)
            {
                try
                {
                    switch (_FormatString[0])
                    {
                        case 'X':
                        case 'x':
                            s = String.Format("{0:" + _FormatString + "}", (int)DValue);
                            break;
                        default:
                            s = String.Format("{0:" + _FormatString + "}", DValue);
                            break;
                    }
                }
                catch
                {
                }
            }
            else
            {
                string t = ".".PadLeft(Math.Max(1, n - _NumberOfDecimals),
                                       _ShowLeadingZeros ? '0' : '#') + "".PadLeft(_NumberOfDecimals, '0');
                s = String.Format("{0:" + t + "}", Math.Abs(DValue));
                s = ProcessSign(s);
            }
            int nDigits;
            s = AlignOutputString(s, _ShowDecimalPoint == false, out nDigits);
            if (s.Length > nDigits)
                return ("".PadRight(_NumberOfDigits, '-'));
            return (s);
        }
        #endregion
        #region ProcessSign
        private string ProcessSign(string s)
        {
            char sign = '\0';
            if (Value < 0)
            {
                if (_SignVisibility == SignVisibility.Always ||
                    _SignVisibility == SignVisibility.WhenNegative)
                {
                    sign = '-';
                }
            }
            else
            {
                if (_SignVisibility == SignVisibility.Always ||
                    _SignVisibility == SignVisibility.WhenPositive)
                {
                    sign = '+';
                }
            }
            if (sign != '\0')
            {
                if (_ShowLeadingZeros == true)
                {
                    if (s[0] != '0')
                        return ("".PadRight(_NumberOfDigits, '-'));
                    return (sign + s.Substring(1));
                }
                return (sign + s);
            }
            return (s);
        }
        #endregion
        #region AlignOutputString
        private string AlignOutputString(
            string s, bool removeDp, out int nDigits)
        {
            nDigits = _NumberOfDigits;
            if (string.IsNullOrEmpty(s) == false)
            {
                if (Style != NumericIndicatorStyle.Mechanical && removeDp == false)
                {
                    removeDp = true;
                    if (s[0] == '.' || s[0] == ':')
                        s = " " + s;
                }
                if (removeDp == true)
                {
                    foreach (char c in s)
                    {
                        if (c == '.' || c == ':')
                            nDigits++;
                    }
                }
                switch (_DigitPlacement)
                {
                    case DisplayPlacement.Center:
                        int n = (nDigits - s.Length)/2;
                        if (n > 0)
                            s = s.PadLeft(s.Length + n, ' ');
                        break;
                    case DisplayPlacement.Far:
                        if (s.Length < nDigits)
                            s = s.PadLeft(nDigits, ' ');
                        break;
                }
            }
            return (s);
        }
        #endregion
        #endregion
        #region UpdateMechanicalElements
        private void UpdateMechanicalElements(string s, bool numeric)
        {
            Color color, decimalColor;
            GetIndColors(out color, out decimalColor);
            int index = 0;
            for (int i = 0; i < s.Length; i++)
            {
                if (index >= _NumberOfDigits)
                    break;
                if (numeric == true && s[i] == '.')
                {
                    color = decimalColor;
                    if (_ShowDecimalPoint == false)
                        continue;
                }
                NumericElement element = _Digits[index++];
                element.Value = s[i];
                element.DigitColor = color;
            }
            for (int i = index; i < _NumberOfDigits; i++)
            {
                _Digits[i].Value = ' ';
                _Digits[i].DigitColor = color;
            }
        }
        #endregion
        #region UpdateSegmentedElements
        private void UpdateSegmentedElements(string s, bool numeric)
        {
            Color color, dimColor;
            Color decimalColor, decimalDimColor;
            GetIndColors(out color, out dimColor, out decimalColor, out decimalDimColor);
            int index = 0;
            for (int i = 0; i < s.Length; i++)
            {
                if (s[i] == '.')
                {
                    if (numeric == true)
                    {
                        color = decimalColor;
                        dimColor = decimalDimColor;
                        if (_ShowDecimalPoint == false)
                            continue;
                    }
                    if (index > 0)
                        _Digits[index - 1].DecimalPointOn = true;
                }
                else if (s[i] == ':')
                {
                    if (index > 0)
                        _Digits[index - 1].ColonPointsOn = true;
                }
                else
                {
                    if (index >= _NumberOfDigits)
                        break;
                    InitElement(_Digits[index++], s[i], color, dimColor);
                }
            }
            for (int i = index; i < _NumberOfDigits; i++)
                InitElement(_Digits[i], ' ', color, dimColor);
        }
        #region InitElement
        private void InitElement(NumericElement element,
            char value, Color color, Color dimColor)
        {
            element.Value = value;
            element.DigitColor = color;
            element.DigitDimColor = dimColor;
            element.ColonPointsOn = false;
            element.DecimalPointOn = false;
            element.ShowDimColonPoints = _ShowDimColonPoints;
            element.ShowDimDecimalPoint = _ShowDimDecimalPoint;
            element.ShowDimSegments = _ShowDimSegments;
        }
        #endregion
        #endregion
        #endregion
        #endregion
        #region OnPaint
        public override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            RecalcLayout();
            Region rgn = g.Clip;
            Rectangle r = Bounds;
            r.Width++;
            r.Height++;
            g.SetClip(r, CombineMode.Intersect);
            if (GaugeControl.OnPreRenderIndicator(e, this) == false)
            {
                FillBackground(g, Bounds, GetBackColor());
                DrawElements(e);
                DrawBorder(e);
                GaugeControl.OnPostRenderIndicator(e, this);
            }
            g.Clip = rgn;
        }
        #region FillBackground
        private void FillBackground(Graphics g,
            Rectangle bounds, GradientFillColor fillColor)
        {
            Rectangle r = bounds;
            GradientFillType fillType =
                fillColor.End.IsEmpty ? GradientFillType.None : fillColor.GradientFillType;
            switch (fillType)
            {
                case GradientFillType.Angle:
                    using (Brush br = fillColor.GetBrush(r))
                    {
                        if (br is LinearGradientBrush)
                            ((LinearGradientBrush)br).WrapMode = WrapMode.TileFlipXY;
                        g.FillRectangle(br, r);
                    }
                    break;
                case GradientFillType.StartToEnd:
                    using (Brush br = fillColor.GetBrush(r, 90))
                    {
                        if (br is LinearGradientBrush)
                            ((LinearGradientBrush)br).WrapMode = WrapMode.TileFlipXY;
                        g.FillRectangle(br, r);
                    }
                    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.FillRectangle(br, bounds);
                    }
                    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.FillRectangle(br, bounds);
                    }
                    break;
                case GradientFillType.Center:
                    using (GraphicsPath path = new GraphicsPath())
                    {
                        path.AddRectangle(r);
                        using (PathGradientBrush br = new PathGradientBrush(path))
                        {
                            br.CenterColor = fillColor.Start;
                            br.SurroundColors = new Color[] { fillColor.End };
                            g.FillRectangle(br, r);
                        }
                    }
                    break;
                default:
                    using (Brush br = new SolidBrush(fillColor.Start))
                        g.FillRectangle(br, r);
                    break;
            }
        }
        #endregion
        #region DrawElements
        private void DrawElements(PaintEventArgs e)
        {
            if (_Digits != null)
            {
                for (int i = 0; i < _Digits.Length; i++)
                {
                    if (GaugeControl.OnPreRenderIndicatorDigit(e, this, _Digits[i], i) == false)
                    {
                        _Digits[i].OnPaint(e);
                        GaugeControl.OnPostRenderIndicatorDigit(e, this, _Digits[i], i);
                    }
                }
            }
        }
        #endregion
        #region DrawBorder
        private void DrawBorder(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            if (_Digits != null && _Style == NumericIndicatorStyle.Mechanical)
            {
                int sepWidth = AbsSeparatorWidth;
                if (sepWidth > 0 && _SeparatorColor != null)
                {
                    for (int i = 0; i < _Digits.Length - 1; i++)
                    {
                        Rectangle r = _Digits[i].Bounds;
                        r.X = r.Right + 1;
                        r.Y = Bounds.Y + BackColor.BorderWidth;
                        r.Width = sepWidth;
                        r.Height = Bounds.Height - (BackColor.BorderWidth * 2);
                        FillBackground(g, r, _SeparatorColor);
                        if (_SeparatorColor.BorderWidth > 0)
                        {
                            using (Pen pen = new Pen(_SeparatorColor.BorderColor, _SeparatorColor.BorderWidth))
                            {
                                pen.Alignment = PenAlignment.Inset;
                                g.DrawLine(pen, r.X, r.Y, r.X, r.Bottom);
                                g.DrawLine(pen, r.Right, r.Y, r.Right, r.Bottom);
                            }
                        }
                    }
                }
            }
            if (BackColor.BorderWidth > 0)
            {
                using (Pen pen = new Pen(BackColor.BorderColor, BackColor.BorderWidth))
                {
                    pen.Alignment = PenAlignment.Inset;
                    g.DrawRectangle(pen, Bounds);
                }
            }
        }
        #endregion
        #endregion
        #region GetBackColor
        private GradientFillColor GetBackColor()
        {
            if (_Ranges != null)
            {
                NumericRange range = _Ranges.GetValueRange(Value);
                if (range != null && (range.BackColor.IsEmpty == false))
                    return (range.BackColor);
            }
            return (BackColor);
        }
        #endregion
        #region GetIndColors
        private void GetIndColors(out Color digitColor, out Color decimalColor)
        {
            digitColor = _DigitColor;
            decimalColor = _DecimalColor;
            if (_Ranges != null)
            {
                foreach (NumericRange range in _Ranges)
                {
                    if (Value >= range.StartValue && Value <= range.EndValue)
                    {
                        if (range.DigitColor.IsEmpty == false)
                            digitColor = range.DigitColor;
                        if (range.DecimalColor.IsEmpty == false)
                            decimalColor = range.DecimalColor;
                        break;
                    }
                }
            }
        }
        private void GetIndColors(out Color digitColor, out Color digitDimColor, 
            out Color decimalColor,out Color decimalDimColor)
        {
            digitColor = _DigitColor;
            decimalColor = _DecimalColor;
            digitDimColor = _DigitDimColor;
            decimalDimColor = _DecimalDimColor;
            if (_Ranges != null)
            {
                foreach (NumericRange range in _Ranges)
                {
                    if (Value >= range.StartValue && Value <= range.EndValue)
                    {
                        if (range.DigitColor.IsEmpty == false)
                            digitColor = range.DigitColor;
                        if (range.DecimalColor.IsEmpty == false)
                            decimalColor = range.DecimalColor;
                        if (range.DigitDimColor.IsEmpty == false)
                            digitDimColor = range.DigitDimColor;
                        if (range.DecimalDimColor.IsEmpty == false)
                            decimalDimColor = range.DecimalDimColor;
                        break;
                    }
                }
            }
        }
        #endregion
        #region Refresh
        internal void Refresh()
        {
            if (NeedRecalcLayout == false)
                OnGaugeItemChanged(false);
        }
        #endregion
        #region RefreshElements
        public void RefreshElements()
        {
            if (_Digits != null)
            {
                foreach (NumericElement item in _Digits)
                    item.RefreshElements();
                Refresh();
            }
        }
        #endregion
        #region CopyToItem
        public override void CopyToItem(GaugeItem copy)
        {
            NumericIndicator c = copy as NumericIndicator;
            if (c != null)
            {
                base.CopyToItem(c);
                c.DecimalColor = _DecimalColor;
                c.DecimalDimColor = _DecimalDimColor;
                c.DigitColor = _DigitColor;
                c.DigitDimColor = _DigitDimColor;
                c.DigitPlacement = _DigitPlacement;
                c.FormatString = _FormatString;
                c.NumberOfDecimals = _NumberOfDecimals;
                c.NumberOfDigits = _NumberOfDigits;
                c.Padding = _Padding;
                if (_Ranges != null)
                    c.Ranges = (NumericRangeCollection)_Ranges.Clone();
                c.SegmentWidth = _SegmentWidth;
                if (_SeparatorColor != null)
                    c.SeparatorColor = (GradientFillColor)_SeparatorColor.Clone();
                c.SeparatorWidth = _SeparatorWidth;
                c.ShearFactor = _ShearFactor;
                c.ShowDecimalPoint = _ShowDecimalPoint;
                c.ShowDimColonPoints = _ShowDimColonPoints;
                c.ShowDimDecimalPoint = _ShowDimDecimalPoint;
                c.ShowDimSegments = _ShowDimSegments;
                c.ShowLeadingZeros = _ShowLeadingZeros;
                c.SignVisibility = _SignVisibility;
                c.Style = _Style;
            }
        }
        #endregion
    }
    #region Enums
    public enum NumericIndicatorStyle
    {
        Mechanical,
        Digital7Segment,
        Digital16Segment
    }
    public enum SignVisibility
    {
        Never,
        WhenNegative,
        WhenPositive,
        Always
    }
    #endregion
}