//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright ?2006  John Champion
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//=============================================================================
using System;
using System.Drawing;
using System.Text;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace DrawGraph
{
	/// 
	/// Class that handles the global settings for bar charts
	/// 
	/// 
	///  John Champion 
	///  $Revision: 3.4 $ $Date: 2007/06/02 06:56:03 $ 
	[Serializable]
	public class BarSettings : ISerializable
	{
	#region Fields
		/// Private field that determines the size of the gap between bar clusters
		/// for bar charts.  This gap is expressed as a fraction of the bar size (1.0 means
		/// leave a 1-barwidth gap between clusters).
		/// Use the public property  to access this value. 
		private float _minClusterGap;
		/// Private field that determines the size of the gap between individual bars
		/// within a bar cluster for bar charts.  This gap is expressed as a fraction of the
		/// bar size (1.0 means leave a 1-barwidth gap between each bar).
		/// Use the public property  to access this value. 
		private float _minBarGap;
		/// Private field that determines the base axis from which 
		/// graphs will be displayed.  The base axis is the axis from which the bars grow with
		/// increasing value. The value is of the enumeration type .
		/// To access this value, use the public property .
		/// 
		/// 
		private BarBase _base;
		/// Private field that determines how the 
		/// graphs will be displayed. See the  enum
		/// for the individual types available.
		/// To access this value, use the public property .
		/// 
		/// 
		private BarType _type;
		/// Private field that determines the width of a bar cluster (for bar charts)
		/// in user scale units.  Normally, this value is 1.0 because bar charts are typically
		///  or , and the bars are
		/// defined at ordinal values (1.0 scale units apart).  For 
		/// or other scale types, you can use this value to scale the bars to an arbitrary
		/// user scale. Use the public property  to access this
		/// value. 
		internal double _clusterScaleWidth;
		/// 
		/// Private field that determines if the  will be
		/// calculated automatically.  Use the public property 
		/// to access this value.
		/// 
		internal bool _clusterScaleWidthAuto;
		/// 
		/// private field that stores the owner GraphPane that contains this BarSettings instance.
		/// 
		internal GraphPane _ownerPane;
	#endregion
	#region Constructors
		/// 
		/// Constructor to build a  instance from the defaults.
		/// 
		public BarSettings( GraphPane parentPane )
		{
			_minClusterGap = Default.MinClusterGap;
			_minBarGap = Default.MinBarGap;
			_clusterScaleWidth = Default.ClusterScaleWidth;
			_clusterScaleWidthAuto = Default.ClusterScaleWidthAuto;
			_base = Default.Base;
			_type = Default.Type;
			_ownerPane = parentPane;
		}
		/// 
		/// Copy constructor
		/// 
		/// the  instance to be copied.
		/// The  that will be the
		/// parent of this new BarSettings object.
		public BarSettings( BarSettings rhs, GraphPane parentPane )
		{
			_minClusterGap = rhs._minClusterGap;
			_minBarGap = rhs._minBarGap;
			_clusterScaleWidth = rhs._clusterScaleWidth;
			_clusterScaleWidthAuto = rhs._clusterScaleWidthAuto;
			_base = rhs._base;
			_type = rhs._type;
			_ownerPane = parentPane;
		}
	#endregion
	#region Bar Properties
		/// 
		/// The minimum space between  clusters, expressed as a
		/// fraction of the bar size.
		/// 
		/// 
		/// 
		/// 
		public float MinClusterGap
		{
			get { return _minClusterGap; }
			set { _minClusterGap = value; }
		}
		/// 
		/// The minimum space between individual Bars
		/// within a cluster, expressed as a
		/// fraction of the bar size.
		/// 
		/// 
		/// 
		/// 
		public float MinBarGap
		{
			get { return _minBarGap; }
			set { _minBarGap = value; }
		}
		/// Determines the base axis from which 
		/// graphs will be displayed.
		/// 
		/// The base axis is the axis from which the bars grow with
		/// increasing value. The value is of the enumeration type .
		/// 
		/// 
		public BarBase Base
		{
			get { return _base; }
			set { _base = value; }
		}
		/// Determines how the 
		/// graphs will be displayed. See the  enum
		/// for the individual types available.
		/// 
		/// 
		public BarType Type
		{
			get { return _type; }
			set { _type = value; }
		}
		/// 
		/// The width of an individual bar cluster on a  graph.
		/// This value only applies to bar graphs plotted on non-ordinal X axis
		/// types (, , and
		/// .
		/// 
		/// 
		/// This value can be calculated automatically if 
		/// is set to true.  In this case, ClusterScaleWidth will be calculated if
		///  refers to an  of a non-ordinal type
		/// ( is false).  The ClusterScaleWidth is calculated
		/// from the minimum difference found between any two points on the 
		///  for any  in the
		/// .  The ClusterScaleWidth is set automatically
		/// each time  is called.  Calculations are
		/// done by the  method.
		/// 
		/// 
		/// 
		/// 
		/// 
		public double ClusterScaleWidth
		{
			get { return _clusterScaleWidth; }
			set { _clusterScaleWidth = value; _clusterScaleWidthAuto = false; }
		}
		/// 
		/// Gets or sets a property that determines if the  will be
		/// calculated automatically.
		/// 
		/// true for the  to be calculated
		/// automatically based on the available data, false otherwise.  This value will
		/// be set to false automatically if the  value
		/// is changed by the user.
		/// 
		/// 
		/// 
		public bool ClusterScaleWidthAuto
		{
			get { return _clusterScaleWidthAuto; }
			set { _clusterScaleWidthAuto = value; }
		}
	#endregion
	#region Serialization
		/// 
		/// Current schema value that defines the version of the serialized file
		/// 
		public const int schema = 10;
		/// 
		/// Constructor for deserializing objects
		/// 
		/// 
		/// You MUST set the _ownerPane property after deserializing a BarSettings object.
		/// 
		/// A  instance that defines the
		/// serialized data
		/// 
		/// A  instance that contains
		/// the serialized data
		/// 
		internal BarSettings( SerializationInfo info, StreamingContext context )
		{
			// The schema value is just a file version parameter.  You can use it to make future versions
			// backwards compatible as new member variables are added to classes
			int sch = info.GetInt32( "schema" );
			_minClusterGap = info.GetSingle( "minClusterGap" );
			_minBarGap = info.GetSingle( "minBarGap" );
			_clusterScaleWidth = info.GetDouble( "clusterScaleWidth" );
			_clusterScaleWidthAuto = info.GetBoolean( "clusterScaleWidthAuto" );
			_base = (BarBase)info.GetValue( "base", typeof( BarBase ) );
			_type = (BarType)info.GetValue( "type", typeof( BarType ) );
		}
		/// 
		/// Populates a  instance with the data needed to serialize the target object
		/// 
		/// A  instance that defines the serialized data
		/// A  instance that contains the serialized data
		[SecurityPermissionAttribute( SecurityAction.Demand, SerializationFormatter = true )]
		public virtual void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			info.AddValue( "schema", schema );
			info.AddValue( "minClusterGap", _minClusterGap );
			info.AddValue( "minBarGap", _minBarGap );
			info.AddValue( "clusterScaleWidth", _clusterScaleWidth );
			info.AddValue( "clusterScaleWidthAuto", _clusterScaleWidthAuto );
			info.AddValue( "base", _base );
			info.AddValue( "type", _type );
		}
	#endregion
	#region Methods
		/// 
		/// Calculate the width of an individual bar cluster on a  graph.
		/// This value only applies to bar graphs plotted on non-ordinal X axis
		/// types ( is false).
		/// 
		/// 
		/// This value can be calculated automatically if 
		/// is set to true.  In this case, ClusterScaleWidth will be calculated if
		///  refers to an  of a non-ordinal type
		/// ( is false).  The ClusterScaleWidth is calculated
		/// from the minimum difference found between any two points on the 
		///  for any  in the
		/// .  The ClusterScaleWidth is set automatically
		/// each time  is called.
		/// 
		/// 
		/// 
		/// 
		/// 
		public void CalcClusterScaleWidth()
		{
			Axis baseAxis = BarBaseAxis();
			// First, calculate the clusterScaleWidth for BarItem objects
			if ( _clusterScaleWidthAuto && !baseAxis.Scale.IsAnyOrdinal )
			{
				double minStep = Double.MaxValue;
				foreach ( CurveItem curve in _ownerPane.CurveList )
				{
					IPointList list = curve.Points;
					if ( curve is BarItem )
					{
						double step = GetMinStepSize( curve.Points, baseAxis );
						minStep = step < minStep ? step : minStep;
					}
				}
				if ( minStep == Double.MaxValue )
					minStep = 1.0;
				_clusterScaleWidth = minStep;
			}
			// Second, calculate the sizes of any HiLowBarItem and JapaneseCandleStickItem objects
			foreach ( CurveItem curve in _ownerPane.CurveList )
			{
				IPointList list = curve.Points;
				if ( curve is HiLowBarItem &&
						(curve as HiLowBarItem).Bar.IsAutoSize )
				{
					( curve as HiLowBarItem ).Bar._userScaleSize =
								GetMinStepSize( list, baseAxis );
				}
				else if ( curve is JapaneseCandleStickItem &&
						(curve as JapaneseCandleStickItem).Stick.IsAutoSize )
				{
					( curve as JapaneseCandleStickItem ).Stick._userScaleSize =
								GetMinStepSize( list, baseAxis );
				}
			}
		}
		/// 
		/// Determine the minimum increment between individual points to be used for
		/// calculating a bar size that fits without overlapping
		/// 
		/// The  list of points for the bar
		/// of interest
		/// The base axis for the bar
		/// The minimum increment between bars along the base axis
		internal static double GetMinStepSize( IPointList list, Axis baseAxis )
		{
			double minStep = Double.MaxValue;
			if ( list.Count <= 0 || baseAxis._scale.IsAnyOrdinal )
				return 1.0;
			PointPair lastPt = list[0];
			for ( int i = 1; i < list.Count; i++ )
			{
				PointPair pt = list[i];
				if ( !pt.IsInvalid || !lastPt.IsInvalid )
				{
					double step;
					if ( baseAxis is XAxis || baseAxis is X2Axis )
						step = pt.X - lastPt.X;
					else
						step = pt.Y - lastPt.Y;
					if ( step > 0 && step < minStep )
						minStep = step;
				}
				lastPt = pt;
			}
			double range = baseAxis.Scale._maxLinearized - baseAxis.Scale._minLinearized;
			if ( range <= 0 )
				minStep = 1.0;
			else if ( minStep <= 0 || minStep < 0.001 * range || minStep > range )
				minStep = 0.1 * range;
			return minStep;
		}
		/// 
		/// Determine the width, in screen pixel units, of each bar cluster including
		/// the cluster gaps and bar gaps.
		/// 
		/// This method calls the 
		/// method for the base  for  graphs
		/// (the base  is assigned by the 
		/// property).
		/// 
		/// 
		/// 
		/// 
		/// 
		/// The width of each bar cluster, in pixel units
		public float GetClusterWidth()
		{
			return BarBaseAxis()._scale.GetClusterWidth( _ownerPane );
		}
		/// 
		/// Determine the  from which the  charts are based.
		/// 
		/// 
		/// 
		/// 
		/// 
		/// The  class for the axis from which the bars are based
		public Axis BarBaseAxis()
		{
			Axis barAxis;
			if ( _base == BarBase.Y )
				barAxis = _ownerPane.YAxis;
			else if ( _base == BarBase.Y2 )
				barAxis = _ownerPane.Y2Axis;
			else if ( _base == BarBase.X2 )
				barAxis = _ownerPane.X2Axis;
			else
				barAxis = _ownerPane.XAxis;
			return barAxis;
		}
	#endregion
	#region Defaults
		/// 
		/// A simple struct that defines the
		/// default property values for the  class.
		/// 
		public struct Default
		{
			/// 
			/// The default dimension gap between clusters of bars on a
			///  graph.
			/// This dimension is expressed in terms of the normal bar width.
			/// 
			/// 
			/// 
			public static float MinClusterGap = 1.0F;
			/// 
			/// The default dimension gap between each individual bar within a bar cluster
			/// on a  graph.
			/// This dimension is expressed in terms of the normal bar width.
			/// 
			/// 
			/// 
			public static float MinBarGap = 0.2F;
			/// The default value for the , which determines the base
			///  from which the  graphs will be displayed.
			/// 
			/// 
			public static BarBase Base = BarBase.X;
			/// The default value for the  property, which
			/// determines if the bars are drawn overlapping eachother in a "stacked" format,
			/// or side-by-side in a "cluster" format.  See the 
			/// for more information.
			/// 
			/// 
			public static BarType Type = BarType.Cluster;
			/// 
			/// The default width of a bar cluster 
			/// on a  graph.  This value only applies to
			///  graphs, and only when the
			///  is ,
			///  or .
			/// This dimension is expressed in terms of X scale user units.
			/// 
			/// 
			/// 
			public static double ClusterScaleWidth = 1.0;
			/// 
			/// The default value for .
			/// 
			public static bool ClusterScaleWidthAuto = true;
		}
	#endregion
	}
}