/*
ScPl - A plotting library for .NET

StepPlot.cs
Copyright (C) 2003
Matt Howlett

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
   
2. Redistributions in binary form must reproduce the following text in 
   the documentation and / or other materials provided with the 
   distribution: 
   
   "This product includes software developed as part of 
   the ScPl plotting library project available from: 
   http://www.netcontrols.org/scpl/" 

------------------------------------------------------------------------

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

$Id: StepPlot.cs,v 1.21 2004/05/07 21:19:38 mhowlett Exp $

*/

using System;
using System.Drawing;

namespace scpl
{
	/// <summary>
	/// Provides ability to trace step plots.
	/// </summary>
	public class StepPlot : BasePlot, ISequencePlot
	{
		#region Constructor
		/// <summary>
		/// Constructor for the step plot.
		/// </summary>
		/// <param name="data">A Data Adapter object implementing the ISequenceAdapter interface.</param>
		public StepPlot()
		{
			this.Center = false;
		}
		#endregion

		#region DataSource
		public object DataSource
		{
			get
			{
				return this.dataSource_;
			}
			set
			{
				this.dataSource_ = value;
			}
		}
		private object dataSource_ = null;
		#endregion
		#region ValueData
		public object ValueData
		{
			get
			{
				return this.valueData_;
			}
			set
			{
				this.valueData_ = value;
			}
		}
		private object valueData_ = null;
		#endregion		
		#region AbscissaData
		public object AbscissaData
		{
			get
			{
				return this.abscissaData_;
			}
			set
			{
				this.abscissaData_ = value;
			}
		}
		private object abscissaData_ = null;
		#endregion

		#region Draw
		/// <summary>
		/// Renders the step plot.
		/// </summary>
		/// <param name="g">The Graphics surface.</param>
		/// <param name="xAxis">The X axis where the trace plot is attached to.</param>
		/// <param name="yAxis">The Y axis where the trace plot is attached to.</param>
		public virtual void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
		{

			SequenceAdapter data = 
				new SequenceAdapter( this.DataSource, this.DataMember, this.ValueData, this.AbscissaData );

			Pen	p;
			if ( this.pen_ != null )
			{
				p = (System.Drawing.Pen)this.pen_;
			}
			else
			{
				p =	new	Pen( this.color_ );
			}

			for (int i=0; i<data.Count; ++i)
			{
				PointD p1 = data[i];
				if (Double.IsNaN(p1.X) || Double.IsNaN(p1.Y))
				{
					continue;
				}

				PointD p2;
				PointD p3;
				if (i+1 != data.Count)
				{
					p2 = data[i+1];
					if (Double.IsNaN(p2.X) || Double.IsNaN(p2.Y))
					{
						continue;
					}
					p2.Y = p1.Y;
					p3 = data[i+1];
				}
				else
				{
					p2 = data[i-1];
					double offset = p1.X - p2.X;
					p2.X = p1.X + offset;
					p2.Y = p1.Y;
					p3 = p2; 
				}

				if ( this.center_ )
				{
					double offset = ( p2.X - p1.X ) / 2.0f;
					p1.X -= offset;
					p2.X -= offset;
					p3.X -= offset;
				}

				PointF xPos1 = xAxis.WorldToPhysical( p1.X, false );
				PointF yPos1 = yAxis.WorldToPhysical( p1.Y, false );
				PointF xPos2 = xAxis.WorldToPhysical( p2.X, false );
				PointF yPos2 = yAxis.WorldToPhysical( p2.Y, false );
				PointF xPos3 = xAxis.WorldToPhysical( p3.X, false );
				PointF yPos3 = yAxis.WorldToPhysical( p3.Y, false );

				g.DrawLine( p, xPos1.X, yPos1.Y, xPos2.X, yPos2.Y );
				g.DrawLine( p, xPos2.X, yPos2.Y, xPos3.X, yPos3.Y );
			}

		}
		#endregion

		#region SuggestXAxis
		/// <summary>
		/// Provides a hint for the X axis.
		/// </summary>
		/// <returns>The X axis.</returns>
		public Axis SuggestXAxis()
		{
			SequenceAdapter data = 
				new SequenceAdapter( this.DataSource, this.DataMember, this.ValueData, this.AbscissaData );

			if (data.Count < 2)
			{
				return data.SuggestXAxis();
			}

			// else

			Axis a = data.SuggestXAxis();

			PointD p1 = data[0];
			PointD p2 = data[1];
			PointD p3 = data[data.Count-2];
			PointD p4 = data[data.Count-1];

			double offset1;
			double offset2;

			if (!center_)
			{
				offset1 = 0.0f;
				offset2 = p4.X - p3.X;
			}
			else
			{
				offset1 = (p2.X - p1.X)/2.0f;
				offset2 = (p4.X - p3.X)/2.0f;
			}

			a.WorldMin -= offset1;
			a.WorldMax += offset2;

			return a;
		}
		#endregion

		#region SuggestYAxis
		/// <summary>
		/// Provides a hint for the Y axis.
		/// </summary>
		/// <returns>The Y axis.</returns>
		public Axis SuggestYAxis()
		{
			SequenceAdapter data = 
				new SequenceAdapter( this.DataSource, this.DataMember, this.ValueData, this.AbscissaData );

			return data.SuggestYAxis();
		}
		#endregion

		#region Center
		/// <summary>
		/// Centers the step plot.
		/// </summary>
		public bool Center
		{
			set
			{
				center_ = value;
			}
			get
			{
				return center_;
			}
		}
		#endregion
		private bool center_;
	}
}
