using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using DevComponents.DotNetBar.Charts.Style;
namespace DevComponents.DotNetBar.Charts
{
    /// 
    /// Represents a Crosshair element.
    /// 
    [TypeConverter(typeof(BlankExpandableObjectConverter))]
    public class ChartCrosshair : ChartVisualElement
    {
        #region Private variables
        private States _States;
        private PointIntersectMode _PointIntersectMode = PointIntersectMode.Edge;
        private int _PointIntersectMargin = 2;
        private AxisOrientation _AxisOrientation = AxisOrientation.X;
        private CrosshairLabelMode _CrosshairLabelMode = CrosshairLabelMode.Common;
        private CrosshairVisualStyle _CrosshairVisualStyle;
        private EffectiveStyle _EffectiveCrosshairStyle;
        private CrosshairValueVisualStyle _CrosshairLabelVisualStyle;
        private EffectiveStyle _EffectiveCrosshairLabelStyle;
        #endregion
        public ChartCrosshair()
        {
            InitDefaultStates();
            _EffectiveCrosshairStyle = new EffectiveStyle(this);
            _EffectiveCrosshairLabelStyle = new EffectiveStyle(this);
        }
        #region InitDefaultStates
        private void InitDefaultStates()
        {
            SetState(States.ShowCrosshairLabelMarkers, true);
            SetState(States.ShowGroupHeader, true);
        }
        #endregion
        #region Public properties
        #region AxisOrientation
        /// 
        /// Gets or sets the axis orientation driving the crosshair display.
        /// 
        [DefaultValue(AxisOrientation.X), Category("Appearance")]
        [Description("Indicates the axis orientation driving the crosshair display.")]
        public AxisOrientation AxisOrientation
        {
            get { return (_AxisOrientation); }
            set
            {
                if (value != _AxisOrientation)
                {
                    _AxisOrientation = value;
                    OnPropertyChanged("AxisOrientation");
                }
            }
        }
        #endregion
        #region CrosshairLabelMode
        /// 
        /// Gets or sets the mode used to display the Crosshair label.
        /// 
        [DefaultValue(CrosshairLabelMode.Common), Category("Behavior")]
        [Description("Indicates the mode used to display the Crosshair label.")]
        public CrosshairLabelMode CrosshairLabelMode
        {
            get { return (_CrosshairLabelMode); }
            set
            {
                if (value != _CrosshairLabelMode)
                {
                    _CrosshairLabelMode = value;
                    OnPropertyChangedEx("CrosshairLabelMode", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region PointIntersectMode
        /// 
        /// Gets or sets the the Crosshair point intersection mode.
        /// 
        [DefaultValue(PointIntersectMode.Edge), Category("Behavior")]
        [Description("Indicates the Crosshair point intersection mode.")]
        public PointIntersectMode PointIntersectMode
        {
            get { return (_PointIntersectMode); }
            set
            {
                if (value != _PointIntersectMode)
                {
                    _PointIntersectMode = value;
                    OnPropertyChangedEx("PointIntersectMode", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region PointIntersectMargin
        /// 
        /// Gets or sets the the Crosshair point intersection margin.
        /// 
        [DefaultValue(2), Category("Layout")]
        [Description("Indicates the Crosshair point intersection margin.")]
        public int PointIntersectMargin
        {
            get { return (_PointIntersectMargin); }
            set
            {
                if (value != _PointIntersectMargin)
                {
                    _PointIntersectMargin = value;
                    OnPropertyChangedEx("PointIntersectMargin", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region CrosshairLabelVisualStyle
        /// 
        /// Gets or sets the default visual style to be used for Crosshair 
        /// values rendered on the X and Y axes.
        /// 
        [Category("Style")]
        [Description("Indicates visual style to be used for Crosshair values rendered on the X and Y axes")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public CrosshairValueVisualStyle CrosshairLabelVisualStyle
        {
            get
            {
                if (_CrosshairLabelVisualStyle == null)
                {
                    _CrosshairLabelVisualStyle = new CrosshairValueVisualStyle();
                    StyleVisualChangeHandler(null, _CrosshairLabelVisualStyle);
                }
                return (_CrosshairLabelVisualStyle);
            }
            set
            {
                if (_CrosshairLabelVisualStyle != value)
                {
                    CrosshairValueVisualStyle oldValue = _CrosshairLabelVisualStyle;
                    _CrosshairLabelVisualStyle = value;
                    OnVisualStyleChanged("CrosshairLabelVisualStyle", oldValue, value);
                }
            }
        }
        #endregion
        #region CrosshairVisualStyle
        /// 
        /// Gets or sets the visual style for the Crosshair.
        /// 
        [Category("Style")]
        [Description("Indicates the visual style for the Crosshair.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public CrosshairVisualStyle CrosshairVisualStyle
        {
            get
            {
                if (_CrosshairVisualStyle == null)
                {
                    _CrosshairVisualStyle = new CrosshairVisualStyle();
                    StyleVisualChangeHandler(null, _CrosshairVisualStyle);
                }
                return (_CrosshairVisualStyle);
            }
            set
            {
                if (_CrosshairVisualStyle != value)
                {
                    CrosshairVisualStyle oldValue = _CrosshairVisualStyle;
                    _CrosshairVisualStyle = value;
                    OnVisualStyleChanged("CrosshairVisualStyle", oldValue, value);
                    if (oldValue != null)
                        oldValue.Dispose();
                }
            }
        }
        #endregion
        #region EffectiveCrosshairLabelStyle
        /// 
        /// Gets a reference to the Crosshair label's Effective (cached, composite) style.
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public CrosshairValueVisualStyle EffectiveCrosshairLabelStyle
        {
            get { return (_EffectiveCrosshairLabelStyle.Style); }
        }
        #endregion
        #region EffectiveCrosshairStyle
        /// 
        /// Gets a reference to the Crosshair's Effective (cached, composite) style.
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public CrosshairVisualStyle EffectiveCrosshairStyle
        {
            get { return (_EffectiveCrosshairStyle.Style); }
        }
        #endregion
        #region HighlightPoints
        /// 
        /// Gets or sets whether series points are highlighted
        /// when the Crosshair cursor intersects with them.
        /// 
        [DefaultValue(false), Category("Appearance")]
        [Description("Indicates whether series points are highlighted when the Crosshair cursor intersects with them.")]
        public bool HighlightPoints
        {
            get { return (TestState(States.HighlightPoints)); }
            set
            {
                if (value != HighlightPoints)
                {
                    SetState(States.HighlightPoints, value);
                    OnPropertyChangedEx("HighlightPoints", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region HighlightSinglePoint
        /// 
        /// Gets or sets whether only single series points are highlighted
        /// when the Crosshair cursor intersects with them.
        /// 
        [DefaultValue(false), Category("Appearance")]
        [Description("Indicates whether only single series points are highlighted when the Crosshair cursor intersects with them.")]
        public bool HighlightSinglePoint
        {
            get { return (TestState(States.HighlightSinglePoint)); }
            set
            {
                if (value != HighlightSinglePoint)
                {
                    SetState(States.HighlightSinglePoint, value);
                    OnPropertyChangedEx("HighlightSinglePoint", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region ShowValueXLabels
        /// 
        /// Gets or sets whether ValueX Labels are shown.
        /// 
        [DefaultValue(false), Category("Appearance")]
        [Description("Indicates whether ValueX Labels are shown.")]
        public bool ShowValueXLabels
        {
            get { return (TestState(States.ShowValueXLabels)); }
            set
            {
                if (value != ShowValueXLabels)
                {
                    SetState(States.ShowValueXLabels, value);
                    OnPropertyChangedEx("ShowValueXLabels", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region ShowValueXLine
        /// 
        /// Gets or sets whether a ValueX line is shown.
        /// 
        [DefaultValue(false), Category("Appearance")]
        [Description("Indicates whether a ValueX line is shown.")]
        public bool ShowValueXLine
        {
            get { return (TestState(States.ShowValueXLine)); }
            set
            {
                if (value != ShowValueXLine)
                {
                    SetState(States.ShowValueXLine, value);
                    OnPropertyChangedEx("ShowValueXLine", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region ShowCrosshairLabelMarkers
        /// 
        /// Gets or sets whether Crosshair label markers are shown.
        /// 
        [DefaultValue(true), Category("Appearance")]
        [Description("Indicates whether Crosshair label markers are shown.")]
        public bool ShowCrosshairLabelMarkers
        {
            get { return (TestState(States.ShowCrosshairLabelMarkers)); }
            set
            {
                if (value != ShowCrosshairLabelMarkers)
                {
                    SetState(States.ShowCrosshairLabelMarkers, value);
                    OnPropertyChangedEx("ShowCrosshairLabelMarkers", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region ShowCrosshairLabels
        /// 
        /// Gets or sets whether Crosshair labels are shown.
        /// 
        [DefaultValue(false), Category("Appearance")]
        [Description("Indicates whether Crosshair labels are shown.")]
        public bool ShowCrosshairLabels
        {
            get { return (TestState(States.ShowCrosshairLabels)); }
            set
            {
                if (value != ShowCrosshairLabels)
                {
                    SetState(States.ShowCrosshairLabels, value);
                    OnPropertyChangedEx("ShowCrosshairLabels", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region ShowGroupHeaders
        /// 
        /// Gets or sets whether Group Headers are shown for each series.
        /// 
        [DefaultValue(true), Category("Appearance")]
        [Description("Indicates whether Group Headers are shown for each series.")]
        public bool ShowGroupHeaders
        {
            get { return (TestState(States.ShowGroupHeader)); }
            set
            {
                if (value != ShowGroupHeaders)
                {
                    SetState(States.ShowGroupHeader, value);
                    OnPropertyChangedEx("ShowGroupHeaders", VisualChangeType.Layout);
                }
            }
        }
        #endregion
        #region ShowValueYLabels
        /// 
        /// Gets or sets whether a ValueY label is shown.
        /// 
        [DefaultValue(false), Category("Appearance")]
        [Description("Indicates whether a ValueY label is shown.")]
        public bool ShowValueYLabels
        {
            get { return (TestState(States.ShowValueYLabels)); }
            set
            {
                if (value != ShowValueYLabels)
                {
                    SetState(States.ShowValueYLabels, value);
                    OnPropertyChangedEx("ShowValueYLabels", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #region ShowValueYLine
        /// 
        /// Gets or sets whether a ValueY line is shown.
        /// 
        [DefaultValue(false), Category("Appearance")]
        [Description("Indicates whether a ValueY line is shown.")]
        public bool ShowValueYLine
        {
            get { return (TestState(States.ShowValueYLine)); }
            set
            {
                if (value != ShowValueYLine)
                {
                    SetState(States.ShowValueYLine, value);
                    OnPropertyChangedEx("ShowValueYLine", VisualChangeType.Render);
                }
            }
        }
        #endregion
        #endregion
        #region MeasureOverride
        protected override void MeasureOverride(ChartLayoutInfo layoutInfo)
        {
        }
        #endregion
        #region ArrangeOverride
        protected override void ArrangeOverride(ChartLayoutInfo layoutInfo)
        {
        }
        #endregion
        #region RenderOverride
        protected override void RenderOverride(ChartRenderInfo renderInfo)
        {
        }
        #endregion
        #region Style support
        #region ApplyStyles
        public override void ApplyStyles(BaseVisualStyle style)
        {
            base.ApplyStyles(style);
            CrosshairVisualStyle cstyle = style as CrosshairVisualStyle;
            if (cstyle != null)
            {
                ApplyParentStyles(cstyle, Parent as ChartContainer);
                cstyle.ApplyStyle(CrosshairVisualStyle);
                ApplyDefaultLineStyles(cstyle.ValueXLineStyle);
                ApplyDefaultLineStyles(cstyle.ValueYLineStyle);
                if (cstyle.GroupHeaderFont == null)
                    cstyle.GroupHeaderFont = SystemFonts.CaptionFont;
                if (cstyle.GroupHeaderTextColor.IsEmpty)
                    cstyle.GroupHeaderTextColor = Color.Black;
                if (cstyle.Padding.IsEmpty == true)
                    cstyle.Padding = new Style.Padding(4);
                if (cstyle.BorderThickness <= 0)
                    cstyle.BorderThickness = 1;
                if (cstyle.BorderPattern == LinePattern.NotSet)
                    cstyle.BorderPattern = LinePattern.Solid;
                if (cstyle.BorderColor.IsEmpty == true)
                    cstyle.BorderColor = Color.Black;
                if (cstyle.Background.IsEmpty == true)
                    cstyle.Background = new Background(Color.White);
                if (cstyle.Font == null)
                    cstyle.Font = SystemFonts.DefaultFont;
            }
        }
        #region ApplyParentStyles
        private void ApplyParentStyles(
            CrosshairVisualStyle pstyle, ChartContainer item)
        {
            if (item != null)
            {
                ApplyParentStyles(pstyle, item.Parent as ChartContainer);
                if (item is ChartPanel)
                    pstyle.ApplyStyle(((ChartPanel)item).DefaultVisualStyles.CrosshairVisualStyle);
            }
            else
            {
                pstyle.ApplyStyle(ChartControl.BaseVisualStyles.CrosshairVisualStyle);
                pstyle.ApplyStyle(ChartControl.DefaultVisualStyles.CrosshairVisualStyle);
            }
        }
        #endregion
        #region ApplyDefaultLineStyles
        private void ApplyDefaultLineStyles(ChartLineVisualStyle style)
        {
            if (style.LineColor.IsEmpty == true)
                style.LineColor = Color.Fuchsia;
            if (style.LinePattern == LinePattern.NotSet)
                style.LinePattern = LinePattern.Solid;
            if (style.LineWidth < 0)
                style.LineWidth = 1;
        }
        #endregion
        #region InvalidateStyle
        ///
        ///Invalidate the cached Styles
        ///
        public void InvalidateStyle()
        {
            ClearEffectiveStyles();
        }
        #endregion
        #region ClearEffectiveStyles
        protected override void ClearEffectiveStyles()
        {
            _EffectiveCrosshairStyle.InvalidateStyle();
        }
        #endregion
        #endregion
        #endregion
        #region Crosshair States
        [Flags]
        private enum States : uint
        {
            HighlightPoints = (1U << 0),
            HighlightSinglePoint = (1U << 1),
            ShowValueXLabels = (1U << 2),
            ShowValueXLine = (1U << 3),
            ShowCrosshairLabels = (1U << 4),
            ShowGroupHeader = (1U << 5),
            ShowCrosshairLabelMarkers = (1U << 6),
            ShowValueYLabels = (1U << 7),
            ShowValueYLine = (1U << 8),
        }
        #region TestState
        private bool TestState(States state)
        {
            return ((_States & state) == state);
        }
        #endregion
        #region SetState
        private void SetState(States state, bool value)
        {
            if (value == true)
                _States |= state;
            else
                _States &= ~state;
        }
        #endregion
        #endregion
        #region Copy/CopyTo
        public override ChartVisualElement Copy()
        {
            ChartCrosshair copy = new ChartCrosshair();
            CopyTo(copy);
            return (copy);
        }
        public override void CopyTo(ChartVisualElement copy)
        {
            ChartCrosshair c = copy as ChartCrosshair;
            if (c != null)
            {
                base.CopyTo(c);
                c.AxisOrientation = AxisOrientation;
                c.CrosshairLabelMode = CrosshairLabelMode;
                c.PointIntersectMode = PointIntersectMode;
                c.PointIntersectMargin = PointIntersectMargin;
                c.CrosshairLabelVisualStyle =  (_CrosshairLabelVisualStyle != null) ? CrosshairLabelVisualStyle.Copy() : null;
                c.CrosshairVisualStyle = (_CrosshairVisualStyle != null) ? CrosshairVisualStyle.Copy() : null;
                c.HighlightPoints = HighlightPoints;
                c.HighlightSinglePoint = HighlightSinglePoint;
                c.ShowValueXLabels = ShowValueXLabels;
                c.ShowValueXLine = ShowValueXLine;
                c.ShowCrosshairLabels = ShowCrosshairLabels;
                c.ShowGroupHeaders = ShowGroupHeaders;
                c.ShowValueYLabels = ShowValueYLabels;
                c.ShowValueYLine = ShowValueYLine;
            }
        }
        #endregion
        #region GetSerialData
        internal override SerialElementCollection GetSerialData(string serialName)
        {
            SerialElementCollection sec = new SerialElementCollection();
            if (serialName != null)
            {
                if (serialName.Equals("") == true)
                    serialName = "ChartCrosshair";
                sec.AddStartElement(serialName);
            }
            sec.AddValue("AxisOrientation", AxisOrientation, AxisOrientation.X);
            sec.AddValue("CrosshairLabelMode", CrosshairLabelMode, CrosshairLabelMode.Common);
            sec.AddValue("PointIntersectMode", PointIntersectMode, PointIntersectMode.Edge);
            sec.AddValue("PointIntersectMargin", PointIntersectMargin, 2);
            if (_CrosshairLabelVisualStyle != null && _CrosshairLabelVisualStyle.IsEmpty == false)
                sec.AddElement(_CrosshairLabelVisualStyle.GetSerialData("CrosshairLabelVisualStyle"));
            if (_CrosshairVisualStyle != null && _CrosshairVisualStyle.IsEmpty == false)
                sec.AddElement(_CrosshairVisualStyle.GetSerialData("CrosshairVisualStyle"));
            sec.AddValue("HighlightPoints", HighlightPoints, false);
            sec.AddValue("HighlightSinglePoint", HighlightSinglePoint, false);
            sec.AddValue("ShowValueXLabels", ShowValueXLabels, false);
            sec.AddValue("ShowValueXLine", ShowValueXLine, false);
            sec.AddValue("ShowCrosshairLabels", ShowCrosshairLabels, false);
            sec.AddValue("ShowGroupHeaders", ShowGroupHeaders, true);
            sec.AddValue("ShowValueYLabels", ShowValueYLabels, false);
            sec.AddValue("ShowValueYLine", ShowValueYLine, false);
            sec.AddElement(base.GetSerialData(null));
            if (serialName != null)
                sec.AddEndElement(serialName);
            return (sec);
        }
        #endregion
        #region PutSerialData
        #region ProcessValue
        internal override void ProcessValue(SerialElement se)
        {
            switch (se.Name)
            {
                case "AxisOrientation":
                    AxisOrientation = (AxisOrientation)se.GetValueEnum(typeof(AxisOrientation));
                    break;
                case "CrosshairLabelMode":
                    CrosshairLabelMode = (CrosshairLabelMode)se.GetValueEnum(typeof(CrosshairLabelMode));
                    break;
                case "PointIntersectMode":
                    PointIntersectMode = (PointIntersectMode)se.GetValueEnum(typeof(PointIntersectMode));
                    break;
                case "PointIntersectMargin":
                    PointIntersectMargin = int.Parse(se.StringValue);
                    break;
                case "HighlightPoints":
                    HighlightPoints = bool.Parse(se.StringValue);
                    break;
                case "HighlightSinglePoint":
                    HighlightSinglePoint = bool.Parse(se.StringValue);
                    break;
                case "ShowValueXLabels":
                    ShowValueXLabels = bool.Parse(se.StringValue);
                    break;
                case "ShowValueXLine":
                    ShowValueXLine = bool.Parse(se.StringValue);
                    break;
                case "ShowCrosshairLabels":
                    ShowCrosshairLabels = bool.Parse(se.StringValue);
                    break;
                case "ShowGroupHeaders":
                    ShowGroupHeaders = bool.Parse(se.StringValue);
                    break;
                case "ShowValueYLabels":
                    ShowValueYLabels = bool.Parse(se.StringValue);
                    break;
                case "ShowValueYLine":
                    ShowValueYLine = bool.Parse(se.StringValue);
                    break;
                default:
                    base.ProcessValue(se);
                    break;
            }
        }
        #endregion
        #region ProcessCollection
        internal override void ProcessCollection(SerialElement se)
        {
            SerialElementCollection sec = se.Sec;
            switch (se.Name)
            {
                case "CrosshairLabelVisualStyle":
                    sec.PutSerialData(CrosshairLabelVisualStyle);
                    break;
                case "CrosshairVisualStyle":
                    sec.PutSerialData(CrosshairVisualStyle);
                    break;
                default:
                    base.ProcessCollection(se);
                    break;
            }
        }
        #endregion
        #endregion
        #region IDisposable
        public override void Dispose()
        {
            CrosshairLabelVisualStyle = null;
            CrosshairVisualStyle = null;
            base.Dispose();
        }
        #endregion
    }
    #region enums
    #region CrosshairLabelMode
    public enum CrosshairLabelMode
    {
        /// 
        /// A common Crosshair label is shown for all series
        /// 
        Common,
        /// 
        /// A crosshair label is shown for the nearest series
        /// 
        NearestSeries,
        /// 
        /// A crosshair label is shown for each series
        /// 
        EachSeries,
    }
    #endregion
    #region CrosshairLabelPosition
    public enum CrosshairLabelPosition
    {
        /// 
        /// Crosshair labels positioned relative to the Chart
        /// 
        ChartRelative,
        /// 
        /// Crosshair labels positioned relative to the Mouse
        /// 
        MouseRelative,
    }
    #endregion
    #region PointIntersectMode
    public enum PointIntersectMode
    {
        /// 
        /// From center.
        /// 
        Center,
        /// 
        /// From edge.
        /// 
        Edge,
    }
    #endregion
    #endregion
}