//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright ?2004  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.Collections.Generic;
namespace DrawGraph
{
	/// 
	/// A collection class containing a list of  objects
	/// that define the set of curves to be displayed on the graph.
	/// 
	/// 
	///  John Champion
	/// modified by Jerry Vos
	///  $Revision: 3.41 $ $Date: 2007/04/16 00:03:01 $ 
	[Serializable]
	public class CurveList : List, ICloneable
	{
	#region Properties
		// internal temporary value that keeps
		// the max number of points for any curve
		// associated with this curveList
		private int	maxPts;
		/// 
		/// Read only value for the maximum number of points in any of the curves
		/// in the list.
		/// 
		public int MaxPts
		{
			get { return maxPts; }
		}
		/// 
		/// Read only property that returns the number of curves in the list that are of
		/// type .
		/// 
		public int NumBars
		{
			get
			{
				int count = 0;
				foreach ( CurveItem curve in this )
				{
					if ( curve.IsBar )
						count++;
				}
				return count;
			}
		}
		/// 
		/// Read only property that returns the number of pie slices in the list (class type is
		///  ).
		/// 
		public int NumPies
		{
			get
			{
				int count = 0;
				foreach ( CurveItem curve in this )
				{
					if ( curve.IsPie )
						count++;
				}
				return count;
			}
		}
		/// 
		/// Read only property that determines if all items in the  are
		/// Pies.
		/// 
		public bool IsPieOnly
		{
 			get
 			{
				bool hasPie = false;
 				foreach ( CurveItem curve in this )
 				{
 					if ( !curve.IsPie )
 						return false;
					else
						hasPie = true;
 				}
				return hasPie;
 			}
		}
		/// 
		/// Determine if there is any data in any of the 
		/// objects for this graph.  This method does not verify valid data, it
		/// only checks to see if  > 0.
		/// 
		/// true if there is any data, false otherwise
		public bool HasData()
		{
			foreach( CurveItem curve in this )
			{
				if ( curve.Points.Count > 0 )
					return true;
			}
			return false;
		}
	#endregion
	
	#region Constructors
	
		/// 
		/// Default constructor for the collection class
		/// 
		public CurveList()
		{
			maxPts = 1;
		}
		/// 
		/// The Copy Constructor
		/// 
		/// The XAxis object from which to copy
		public CurveList( CurveList rhs )
		{
			this.maxPts = rhs.maxPts;
			foreach ( CurveItem item in rhs )
			{
				this.Add( (CurveItem) ((ICloneable)item).Clone() );
			}
		}
		/// 
		/// Implement the  interface in a typesafe manner by just
		/// calling the typed version of 
		/// 
		/// A deep copy of this object
		object ICloneable.Clone()
		{
			return this.Clone();
		}
		/// 
		/// Typesafe, deep-copy clone method.
		/// 
		/// A new, independent copy of this class
		public CurveList Clone()
		{
			return new CurveList( this );
		}
		
	#endregion
	#region IEnumerable Methods
		//CJBL
		/// 
		/// Iterate backwards through the  items.
		/// 
		public IEnumerable Backward
		{
			get
			{
				for ( int i = this.Count - 1; i >= 0; i-- )
					yield return this[i];
			}
		}
		/// 
		/// Iterate forward through the  items.
		/// 
		public IEnumerable Forward
		{
			get
			{
				for ( int i = 0; i < this.Count; i++ )
					yield return this[i];
			}
		}
	#endregion
	#region List Methods
/*
		/// 
		/// Indexer to access the specified  object by
		/// its ordinal position in the list.
		/// 
		/// The ordinal position (zero-based) of the
		///  object to be accessed.
		/// A  object reference.
		public CurveItem this[ int index ]  
		{
			get { return( (CurveItem) List[index] ); }
			set { List[index] = value; }
		}
*/
		/// 
		/// Indexer to access the specified  object by
		/// its  string.
		/// 
		/// The string label of the
		///  object to be accessed.
		/// A  object reference.
		public CurveItem this[ string label ]  
		{
			get
			{
				int index = IndexOf( label );
				if ( index >= 0 )
					return( this[index]  );
				else
					return null;
			}
		}
/*
		/// 
		/// Add a  object to the collection at the end of the list.
		/// 
		/// A reference to the  object to
		/// be added
		/// 
		public void Add( CurveItem curve )
		{
			List.Add( curve );
		}
*/
/*
		/// 
		/// Remove a  object from the collection based on an object reference.
		/// 
		/// A reference to the  object that is to be
		/// removed.
		/// 
		public void Remove( CurveItem curve )
		{
			List.Remove( curve );
		}
*/
/*
		/// 
		/// Insert a  object into the collection at the specified
		/// zero-based index location.
		/// 
		/// The zero-based index location for insertion.
		/// A reference to the  object that is to be
		/// inserted.
		/// 
		public void Insert( int index, CurveItem curve )
		{
			List.Insert( index, curve );
		}
*/
		/// 
		/// Return the zero-based position index of the
		///  with the specified .
		/// 
		/// The  label that is in the
		///  attribute of the item to be found.
		/// 
		/// The zero-based index of the specified ,
		/// or -1 if the  is not in the list
		/// 
		public int IndexOf( string label )
		{
			int index = 0;
			foreach ( CurveItem p in this )
			{
				if ( String.Compare( p._label._text, label, true ) == 0 )
					return index;
				index++;
			}
			return -1;
		}
		/// 
		/// Return the zero-based position index of the
		///  with the specified .
		/// 
		/// In order for this method to work, the 
		/// property must be of type .
		/// The  tag that is in the
		///  attribute of the item to be found.
		/// 
		/// The zero-based index of the specified ,
		/// or -1 if the  is not in the list
		public int IndexOfTag( string tag )
		{
			int index = 0;
			foreach ( CurveItem p in this )
			{
				if ( p.Tag is string &&
							String.Compare( (string) p.Tag, tag, true ) == 0 )
					return index;
				index++;
			}
			return -1;
		}
		/// 
		/// Sorts the list according to the point values at the specified index and
		/// for the specified axis.
		/// 
		public void Sort( SortType type, int index )
		{
			this.Sort( new CurveItem.Comparer( type, index ) );
		}
		/// 
		/// Move the position of the object at the specified index
		/// to the new relative position in the list.
		/// For Graphic type objects, this method controls the
		/// Z-Order of the items.  Objects at the beginning of the list
		/// appear in front of objects at the end of the list.
		/// The zero-based index of the object
		/// to be moved.
		/// The relative number of positions to move
		/// the object.  A value of -1 will move the
		/// object one position earlier in the list, a value
		/// of 1 will move it one position later.  To move an item to the
		/// beginning of the list, use a large negative value (such as -999).
		/// To move it to the end of the list, use a large positive value.
		/// 
		/// The new position for the object, or -1 if the object
		/// was not found.
		public int Move( int index, int relativePos )
		{
			if ( index < 0 || index >= Count )
				return -1;
			CurveItem curve = this[index];
			this.RemoveAt( index );
			index += relativePos;
			if ( index < 0 )
				index = 0;
			if ( index > Count )
				index = Count;
			Insert( index, curve );
			return index;
		}
	#endregion
	#region Rendering Methods
		/// 
		/// Go through each  object in the collection,
		/// calling the  member to 
		/// determine the minimum and maximum values in the
		///  list of data value pairs.  If the curves include 
		/// a stack bar, handle within the current GetRange method. In the event that no
		/// data are available, a default range of min=0.0 and max=1.0 are returned.
		/// If the Y axis has a valid data range and the Y2 axis not, then the Y2
		/// range will be a duplicate of the Y range.  Vice-versa for the Y2 axis
		/// having valid data when the Y axis does not.
		/// If any  in the list has a missing
		/// , a new empty one will be generated.
		/// 
		/// ignoreInitial is a boolean value that
		/// affects the data range that is considered for the automatic scale
		/// ranging (see ).  If true, then initial
		/// data points where the Y value is zero are not included when
		/// automatically determining the scale ,
		/// , and  size.  All data after
		/// the first non-zero Y value are included.
		/// 
		/// 
		/// Determines if the auto-scaled axis ranges will subset the
		/// data points based on any manually set scale range values.
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// 
		public void GetRange( bool bIgnoreInitial, bool isBoundedRanges, GraphPane pane )
		{
			double	tXMinVal,
						tXMaxVal,
						tYMinVal,
						tYMaxVal;
			InitScale( pane.XAxis.Scale, isBoundedRanges );
			InitScale( pane.X2Axis.Scale, isBoundedRanges );
			foreach ( YAxis axis in pane.YAxisList )
				InitScale( axis.Scale, isBoundedRanges );
			foreach ( Y2Axis axis in pane.Y2AxisList )
				InitScale( axis.Scale, isBoundedRanges );
			maxPts = 1;
			
			// Loop over each curve in the collection and examine the data ranges
			foreach ( CurveItem curve in this )
			{
				if ( curve.IsVisible )
				{
					// For stacked types, use the GetStackRange() method which accounts for accumulated values
					// rather than simple curve values.
					if ( ( ( curve is BarItem ) && ( pane._barSettings.Type == BarType.Stack ||
							pane._barSettings.Type == BarType.PercentStack ) ) ||
						( ( curve is LineItem ) && pane.LineType == LineType.Stack ) )
					{
						GetStackRange( pane, curve, out tXMinVal, out tYMinVal,
										out tXMaxVal, out tYMaxVal );
					}
					else
					{
						// Call the GetRange() member function for the current
						// curve to get the min and max values
						curve.GetRange( out tXMinVal, out tXMaxVal,
										out tYMinVal, out tYMaxVal, bIgnoreInitial, true, pane );
					}
					// isYOrd is true if the Y axis is an ordinal type
					Scale yScale = curve.GetYAxis( pane ).Scale;
					Scale xScale = curve.GetXAxis( pane ).Scale;
					bool isYOrd = yScale.IsAnyOrdinal;
					// isXOrd is true if the X axis is an ordinal type
					bool isXOrd = xScale.IsAnyOrdinal;
					// For ordinal Axes, the data range is just 1 to Npts
					if ( isYOrd && !curve.IsOverrideOrdinal )
					{
						tYMinVal = 1.0;
						tYMaxVal = curve.NPts;
					}
					if ( isXOrd && !curve.IsOverrideOrdinal )
					{
						tXMinVal = 1.0;
						tXMaxVal = curve.NPts;
					}
					// Bar types always include the Y=0 value
					if ( curve.IsBar )
					{
						if ( pane._barSettings.Base == BarBase.X )
						{
							if ( pane._barSettings.Type != BarType.ClusterHiLow )
							{
								if ( tYMinVal > 0 )
									tYMinVal = 0;
								else if ( tYMaxVal < 0 )
									tYMaxVal = 0;
							}
							// for non-ordinal axes, expand the data range slightly for bar charts to
							// account for the fact that the bar clusters have a width
							if ( !isXOrd )
							{
								tXMinVal -= pane._barSettings._clusterScaleWidth / 2.0;
								tXMaxVal += pane._barSettings._clusterScaleWidth / 2.0;
							}
						}
						else
						{
							if ( pane._barSettings.Type != BarType.ClusterHiLow )
							{
								if ( tXMinVal > 0 )
									tXMinVal = 0;
								else if ( tXMaxVal < 0 )
									tXMaxVal = 0;
							}
							// for non-ordinal axes, expand the data range slightly for bar charts to
							// account for the fact that the bar clusters have a width
							if ( !isYOrd )
							{
								tYMinVal -= pane._barSettings._clusterScaleWidth / 2.0;
								tYMaxVal += pane._barSettings._clusterScaleWidth / 2.0;
							}
						}
					}
					// determine which curve has the maximum number of points
					if ( curve.NPts > maxPts )
						maxPts = curve.NPts;
					// If the min and/or max values from the current curve
					// are the absolute min and/or max, then save the values
					// Also, differentiate between Y and Y2 values
					if ( tYMinVal < yScale._rangeMin )
						yScale._rangeMin = tYMinVal;
					if ( tYMaxVal > yScale._rangeMax )
						yScale._rangeMax = tYMaxVal;
					if ( tXMinVal < xScale._rangeMin )
						xScale._rangeMin = tXMinVal;
					if ( tXMaxVal > xScale._rangeMax )
						xScale._rangeMax = tXMaxVal;
				}
			}
			pane.XAxis.Scale.SetRange( pane, pane.XAxis );
			pane.X2Axis.Scale.SetRange( pane, pane.X2Axis );
			foreach ( YAxis axis in pane.YAxisList )
				axis.Scale.SetRange( pane, axis );
			foreach ( Y2Axis axis in pane.Y2AxisList )
				axis.Scale.SetRange( pane, axis );
		}
		private void InitScale( Scale scale, bool isBoundedRanges )
		{
			scale._rangeMin = double.MaxValue;
			scale._rangeMax = double.MinValue;
			scale._lBound = ( isBoundedRanges && !scale._minAuto ) ?
				scale._min : double.MinValue;
			scale._uBound = ( isBoundedRanges && !scale._maxAuto ) ?
				scale._max : double.MaxValue;
		}
		/// 
		/// Calculate the range for stacked bars and lines.
		/// 
		/// This method is required for the stacked
		/// types because (for bars), the negative values are a separate stack than the positive
		/// values.  If you just sum up the bars, you will get the sum of the positive plus negative,
		/// which is less than the maximum positive value and greater than the maximum negative value.
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// The  for which to calculate the range
		/// The minimum X value so far
		/// The minimum Y value so far
		/// The maximum X value so far
		/// The maximum Y value so far
		/// 
		private void GetStackRange( GraphPane pane, CurveItem curve, out double tXMinVal,
									out double tYMinVal, out double tXMaxVal, out double tYMaxVal )
		{
			// initialize the values to outrageous ones to start
			tXMinVal = tYMinVal = Double.MaxValue;
			tXMaxVal = tYMaxVal = Double.MinValue;
			ValueHandler valueHandler = new ValueHandler( pane, false );
			Axis baseAxis = curve.BaseAxis( pane );
			bool isXBase = baseAxis is XAxis || baseAxis is X2Axis;
			double lowVal, baseVal, hiVal;
			for ( int i=0; i tXMaxVal )
						tXMaxVal = x;
					if ( y < tYMinVal )
						tYMinVal = y;
					if ( y > tYMaxVal )
						tYMaxVal = y;
					if ( !isXBase )
					{
						if ( lowVal < tXMinVal )
							tXMinVal = lowVal;
						if ( lowVal > tXMaxVal )
							tXMaxVal = lowVal;
					}
					else
					{
						if ( lowVal < tYMinVal )
							tYMinVal = lowVal;
						if ( lowVal > tYMaxVal )
							tYMaxVal = lowVal;
					}
				}
			}
		}
		/// 
		/// Render all the  objects in the list to the
		/// specified 
		/// device by calling the  member function of
		/// each  object.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 
		public void Draw( Graphics g, GraphPane pane, float scaleFactor )
		{
			// Configure the accumulator for stacked bars
			//Bar.ResetBarStack();
			// Count the number of BarItems in the curvelist
			int pos = this.NumBars;
			
			// sorted overlay bars are a special case, since they are sorted independently at each
			// ordinal position.
			if ( pane._barSettings.Type == BarType.SortedOverlay )
			{
				// First, create a new curveList with references (not clones) of the curves
				CurveList tempList = new CurveList();
				foreach ( CurveItem curve in this )
					if ( curve.IsBar )
						tempList.Add( (CurveItem) curve );
				
				// Loop through the bars, graphing each ordinal position separately
				for ( int i=0; i= 0; i-- )
			{
				CurveItem curve = this[i];
				
				if ( curve.IsBar )
					pos--;
					
				// Render the curve
				//	if it's a sorted overlay bar type, it's already been done above
				if ( !( curve.IsBar && pane._barSettings.Type == BarType.SortedOverlay ) )
				{
					curve.Draw( g, pane, pos, scaleFactor );
				}
			}
		}
		/// 
		/// Find the ordinal position of the specified  within
		/// the .  This position only counts 
		/// types, ignoring all other types.
		/// 
		/// The  of interest
		/// The  for which to search.
		/// The ordinal position of the specified bar, or -1 if the bar
		/// was not found.
		public int GetBarItemPos( GraphPane pane, BarItem barItem )
		{
			if (	pane._barSettings.Type == BarType.Overlay ||
					pane._barSettings.Type == BarType.Stack ||
					pane._barSettings.Type == BarType.PercentStack)
				return 0;
			int i = 0;
			foreach ( CurveItem curve in this )
			{
				if ( curve == barItem )
					return i;
				else if ( curve is BarItem )
					i++;
			}
			return -1;
		}
	#endregion
	}
}