using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace DevComponents.Instrumentation
{
    public class GaugeControlItem
    {
        #region Private variables
        private GaugeControl _GaugeControl;
        private GaugeControlItem _Parent;
        private GaugeFrame _GaugeFrame;
        private GaugeScaleCollection _GaugeScaleItems;
        private GaugeImageCollection _GaugeImageItems;
        private GaugeTextCollection _GaugeTextItems;
        private int _SuspendCount;
        private bool _NeedRecalcLayout;
        private float _Height;
        private float _Width;
        private PointF _Location;
        private PointF _PivotPoint;
        private float _Radius;
        private Rectangle _AbsBounds;
        private bool _Visible;
        #endregion
        public GaugeControlItem()
        {
            _GaugeFrame = new GaugeFrame(this);
            _GaugeScaleItems = new GaugeScaleCollection();
            _Width = 1;
            _Height = 1;
            _PivotPoint = new PointF(.5f, .5f);
            _Radius = .5f;
            NeedRecalcLayout = true;
            HookEvents(true);
        }
        #region Public properties
        #region GaugeFrame
        /// 
        /// Gets the Gauge Frame.
        /// 
        [Browsable(true), Category("Appearance")]
        [Description("Indicates the Gauge Frame.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public GaugeFrame GaugeFrame
        {
            get { return (_GaugeFrame); }
        }
        #endregion
        #region GaugeScaleItems
        /// 
        /// Gets the collection GaugeScaleItems contained within the gauge.
        /// 
        [Browsable(true), Category("Appearance")]
        [Description("Indicates the collection GaugeScaleItems contained within the gauge.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public GaugeScaleCollection GaugeScaleItems
        {
            get { return (_GaugeScaleItems); }
        }
        #endregion
        #region Location
        public PointF Location
        {
            get { return (_Location); }
            set
            {
                if (_Location != value)
                {
                    _Location = value;
                    OnGaugeChanged();
                }
            }
        }
        #endregion
        #region Height
        public float Height
        {
            get { return (_Height); }
            set
            {
                if (_Height != value)
                {
                    _Height = value;
                    OnResize(EventArgs.Empty);
                }
            }
        }
        #endregion
        #region PivotPoint
        public PointF PivotPoint
        {
            get { return (_PivotPoint); }
            set
            {
                if (_PivotPoint.Equals(value) == false)
                {
                    _PivotPoint = value;
                    OnGaugeChanged();
                }
            }
        }
        #endregion
        #region Radius
        public float Radius
        {
            get { return (_Radius); }
            set
            {
                if (_Radius != value)
                {
                    _Radius = value;
                    OnGaugeChanged();
                }
            }
        }
        #endregion
        #region Visible
        public bool Visible
        {
            get { return (_Visible); }
            set
            {
                if (_Visible != value)
                {
                    _Visible = value;
                    Refresh();
                }
            }
        }
        #endregion
        #region Width
        public float Width
        {
            get { return (_Width); }
            set
            {
                if (_Width != value)
                {
                    _Width = value;
                    OnResize(EventArgs.Empty);
                }
            }
        }
        #endregion
        #endregion
        #region Internal properties
        #region AbsBounds
        internal Rectangle AbsBounds
        {
            get { return (_AbsBounds); }
        }
        #endregion
        #region AbsHeight
        internal int AbsHeight
        {
            get { return (_AbsBounds.Height); }
        }
        #endregion
        #region AbsWidth
        internal int AbsWidth
        {
            get { return (_AbsBounds.Width); }
        }
        #endregion
        #region GaugeControl
        internal GaugeControl GaugeControl
        {
            get { return (_GaugeControl);  }
            set { _GaugeControl = value; }
        }
        #endregion
        #region NeedRecalcLayout
        internal bool NeedRecalcLayout
        {
            get { return (_NeedRecalcLayout); }
            set
            {
                _NeedRecalcLayout = value;
                if (_NeedRecalcLayout == true)
                    OnLayoutChanged();
            }
        }
        #endregion
        #region Parent
        internal GaugeControlItem Parent
        {
            get { return (_Parent); }
            set { _Parent = value; }
        }
        #endregion
        #endregion
        #region HookEvents
        private void HookEvents(bool hook)
        {
            if (hook == true)
            {
                _GaugeFrame.GaugeFrameChanged += GaugeFrame_GaugeFrameChanged;
            }
            else
            {
                _GaugeFrame.GaugeFrameChanged -= GaugeFrame_GaugeFrameChanged;
            }
        }
        #endregion
        #region Event processing
        void GaugeFrame_GaugeFrameChanged(object sender, EventArgs e)
        {
            OnGaugeChanged();
        }
        #endregion
        #region BeginUpdate
        /// 
        /// Disables any redrawing of the Gauge control. To maintain performance while items
        /// are added one at a time to the control, call the BeginUpdate method. The BeginUpdate
        /// method prevents the control from painting until the
        /// EndUpdate method is called.
        /// 
        public void BeginUpdate()
        {
            _SuspendCount++;
        }
        #endregion
        #region EndUpdate
        /// 
        /// Enables the redrawing of the Gauge control. To maintain performance while items are
        /// added one at a time to the control, call the BeginUpdate
        /// method. The BeginUpdate method prevents the control from painting until the EndUpdate
        /// method is called.
        /// 
        public void EndUpdate()
        {
            if (_SuspendCount > 0)
                _SuspendCount--;
            if (_SuspendCount == 0)
                Refresh();
        }
        #endregion
        #region Refresh
        private void Refresh()
        {
            if (_GaugeControl != null)
                _GaugeControl.Invalidate(_GaugeFrame.Bounds);
        }
        #endregion
        #region RecalcLayout
        private void RecalcLayout()
        {
            if (_NeedRecalcLayout == true)
            {
                NeedRecalcLayout = false;
                _AbsBounds = GetItemBounds();
            }
        }
        #endregion
        #region GetItemBounds
        private Rectangle GetItemBounds()
        {
            Rectangle r;
            if (_Parent != null)
            {
                if (_Parent.NeedRecalcLayout == true)
                    throw new Exception();
                r = _Parent.AbsBounds;
            }
            else
            {
                if (_GaugeControl == null)
                    throw new Exception();
                r = _GaugeControl.Bounds;
            }
            if (_GaugeFrame.Style == GaugeFrameStyle.Circular)
            {
                int n = Math.Min(r.Width, r.Height);
                int x = (r.Width - n) / 2;
                int y = (r.Height - n) / 2;
                n = Math.Max(n, 1);
                r = new Rectangle(x, y, n, n);
            }
            else
            {
                r.X = (int)(r.Width * _Location.X);
                r.Y = (int)(r.Height * _Location.Y);
                r.Width = (int)(r.Width * _Width);
                r.Height = (int)(r.Height * _Height);
            }
            return (r);
        }
        #endregion
        #region OnLayoutChanged
        private void OnLayoutChanged()
        {
            GaugeFrame.NeedRecalcLayout = true;
            if (_GaugeScaleItems != null)
            {
                foreach (GaugeScale item in _GaugeScaleItems)
                    item.NeedRecalcLayout = true;
            }
            if (_GaugeImageItems != null)
            {
                foreach (GaugeImage item in _GaugeImageItems)
                    item.NeedRecalcLayout = true;
            }
            if (_GaugeTextItems != null)
            {
                foreach (GaugeText item in _GaugeTextItems)
                    item.NeedRecalcLayout = true;
            }
        }
        #endregion
        #region OnPaint
        public void OnPaint(PaintEventArgs e)
        {
            RecalcLayout();
            GaugeFrame.OnPaint(e);
            PaintScaleItems(e);
            PaintImageItems(e);
            PaintTextItems(e);
        }
        #region PaintScaleItems
        private void PaintScaleItems(PaintEventArgs e)
        {
            if (_GaugeScaleItems != null)
            {
                foreach (GaugeScale scale in _GaugeScaleItems)
                {
                    if (scale.Visible == true)
                        scale.OnPaint(e);
                }
            }
        }
        #endregion
        #region PaintImageItems
        private void PaintImageItems(PaintEventArgs e)
        {
            if (_GaugeImageItems != null)
            {
                foreach (GaugeImage scale in _GaugeImageItems)
                {
                    if (scale.Visible == true)
                        scale.OnPaint(e);
                }
            }
        }
        #endregion
        #region PaintTextItems
        private void PaintTextItems(PaintEventArgs e)
        {
            if (_GaugeTextItems != null)
            {
                foreach (GaugeText scale in _GaugeTextItems)
                {
                    if (scale.Visible == true)
                        scale.OnPaint(e);
                }
            }
        }
        #endregion
        #region GetAbsPoint
        internal Point GetAbsPoint(PointF ptf)
        {
            if (NeedRecalcLayout == true)
                throw new Exception();
            return (new Point((int) (AbsWidth*ptf.X), (int) (AbsHeight*ptf.Y)));
        }
        #endregion
        #region GetAbsSize
        internal Size GetAbsSize(SizeF sf, bool useMin)
        {
            if (NeedRecalcLayout == true)
                throw new Exception();
            int width = AbsWidth;
            int height = AbsHeight;
            if (useMin == true)
            {
                if (width > height)
                    width = height;
                else
                    height = width;
            }
            return (new Size((int)(width * sf.Width), (int)(height * sf.Height)));
        }
        #endregion
        #endregion
        #region OnResize
        public void OnResize(EventArgs e)
        {
            NeedRecalcLayout = true;
        }
        #endregion
        #region OnGaugeChanged
        protected void OnGaugeChanged()
        {
            Refresh();
        }
        #endregion
    }
}