/*
ScPl - A plotting library for .NET

CandlePlot.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: CandlePlot.cs,v 1.3 2004/04/26 04:18:41 mhowlett Exp $

*/

using System;
using System.Drawing;
using System.Data;

namespace scpl
{

	public class PointOLHC
	{
		#region Constructor
		public PointOLHC( double x, double open, double low, double high, double close )
		{
			this.x_ = x;
			this.open_ = open;
			this.close_ = close;
			this.low_ = low;
			this.high_ = high;
		}
		#endregion
		#region X
		public double X
		{
			get
			{
				return x_;
			}
			set
			{
				this.x_ = value;
			}
		}
		private double x_;
		#endregion
		#region Open
		public double Open
		{
			get
			{
				return open_;
			}
			set
			{
				open_ = value;
			}
		}
		private double open_;
		#endregion
		#region Close
		public double Close
		{
			get
			{
				return close_;
			}
			set
			{
				close_ = value;
			}
		}
		private double close_;
		#endregion
		#region Low
		public double Low
		{
			get
			{
				return low_;
			}
			set
			{
				low_ = value;
			}
		}
		private double low_;
		#endregion
		#region High
		public double High
		{
			get
			{
				return high_;
			}
			set
			{
				high_ = value;
			}
		}
		private double high_;
		#endregion
	}

	/// <summary>
	/// TODO
	/// </summary>
	public class CandlePlot : ISequencePlot
	{

		#region class CandleDataAdapter
		private class CandleDataAdapter
		{
			private object openData_;
			private object lowData_;
			private object highData_;
			private object closeData_;
			private object abscissaData_;

			private object dataSource_;
			private string dataMember_;
			DataRowCollection rows_ = null;

			#region Constructor
			public CandleDataAdapter( 
				object dataSource, string dataMember, object abscissaData,
				object openData, object lowData, object highData, object closeData )
			{
				this.openData_ = openData;
				this.lowData_ = lowData;
				this.highData_ = highData;
				this.closeData_ = closeData;
				this.abscissaData_ = abscissaData;

				this.dataSource_ = dataSource;
				this.dataMember_ = dataMember;

				if (dataSource_ != null)
				{
					if ( dataSource_ is DataSet )
					{
						if (dataMember_ != null)
						{
							rows_ = ((DataTable)((DataSet)dataSource_).Tables[dataMember_]).Rows;
						}
						else
						{
							rows_ = ((DataTable)((DataSet)dataSource_).Tables[0]).Rows;
						}
					}

					else if (dataSource_ is DataTable )
					{
						rows_ = ((DataTable)dataSource_).Rows;
					}

					else
					{
						throw new System.Exception ( "not implemented yet" );
					}
				}
			}
			#endregion
			#region this[int i]
			public PointOLHC this[int i]
			{
				get
				{
					// is the data coming from a data source? 
					if (rows_ != null)
					{					
						double x = SequenceAdapter.ConvertToDouble( ((DataRow)(rows_[i]))[(string)abscissaData_] );
						double open = SequenceAdapter.ConvertToDouble( ((DataRow)(rows_[i]))[(string)openData_] );
						double low = SequenceAdapter.ConvertToDouble( ((DataRow)(rows_[i]))[(string)lowData_] );
						double high = SequenceAdapter.ConvertToDouble( ((DataRow)(rows_[i]))[(string)highData_] );
						double close = SequenceAdapter.ConvertToDouble( ((DataRow)(rows_[i]))[(string)closeData_] );

						return new PointOLHC( x, open, low, high, close );
					}
					
					// the data is coming from individual arrays.
					else 
					{
						throw new System.Exception( "Not done yet" );
					}

				}
			}
			#endregion
			#region Count
			public int Count
			{
				get
				{
					// this is inefficient [could set up delegates in constructor].

					if (openData_ == null)
					{
						return 0;
					}

					if (rows_ != null)
					{
						return rows_.Count;
					}
					
					throw new System.Exception( "data not in correct format" );
				}
			}
			#endregion

			#region SuggestXAxis
			public Axis SuggestXAxis()
			{
				object min;
				object max;
				SequenceAdapter.RowArrayMinMax( this.rows_, out min, out max, (string)this.abscissaData_ );
				if ( min is DateTime )
				{
					return new DateTimeAxis( (DateTime)min, (DateTime)max );
				}
				else
				{
					return new LinearAxis( SequenceAdapter.ConvertToDouble(min), SequenceAdapter.ConvertToDouble(max) );
				}
			}
			#endregion

			#region SuggestYAxis
			public Axis SuggestYAxis()
			{
				object min_l;
				object max_l;
				object min_h;
				object max_h;
				SequenceAdapter.RowArrayMinMax( this.rows_, out min_l, out max_l, (string)this.lowData_ );
				SequenceAdapter.RowArrayMinMax( this.rows_, out min_h, out max_h, (string)this.highData_ );
				if ( min_l is DateTime )
				{
					return new DateTimeAxis( (DateTime)min_l, (DateTime)max_h );
				}
				else
				{
					return new LinearAxis( SequenceAdapter.ConvertToDouble(min_l), SequenceAdapter.ConvertToDouble(max_h) );
				}
			}
			#endregion

		}
		#endregion

		#region Constructor
		public CandlePlot()
		{
		}
		#endregion

		#region Draw
		public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
		{
			CandleDataAdapter cd = new CandleDataAdapter( this.DataSource, this.DataMember, 
				this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData );


			for (int i=0; i<cd.Count; ++i)
			{
				int xPos = (int)(xAxis.WorldToPhysical( ((PointOLHC)cd[i]).X, false )).X;
				//int xPos = 40;

				int yPos1 = (int)(yAxis.WorldToPhysical( ((PointOLHC)cd[i]).Low, false )).Y;
				int yPos2 = (int)(yAxis.WorldToPhysical( ((PointOLHC)cd[i]).High, false )).Y;
				int yPos3 = (int)(yAxis.WorldToPhysical( ((PointOLHC)cd[i]).Open, false )).Y;
				int yPos4 = (int)(yAxis.WorldToPhysical( ((PointOLHC)cd[i]).Close, false )).Y;						//int yPos5 = (int)(yAxis.WorldToPhysical( ((DataPoint)data_.Data[i]).AdjClose, false )).Y;

				g.DrawLine( this.pen_, xPos, yPos1, xPos, yPos2 );
				g.DrawLine( this.pen_, xPos-2, yPos3, xPos, yPos3 );
				g.DrawLine( this.pen_, xPos, yPos4, xPos+2, yPos4 );
			}

		}
		#endregion

		#region SuggestXAxis
		public Axis SuggestXAxis()
		{
			CandleDataAdapter candleData = new CandleDataAdapter( this.DataSource, this.DataMember, 
				this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData );
		
			return candleData.SuggestXAxis();
		}
		#endregion
		#region SuggestYAxis
		public Axis SuggestYAxis()
		{
			CandleDataAdapter candleData = new CandleDataAdapter( this.DataSource, this.DataMember, 
				this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData );

			return candleData.SuggestYAxis();
		}
		#endregion

		#region OpenData
		public object OpenData
		{
			get
			{
				return this.openData_;
			}
			set
			{
				this.openData_ = value;
			}
		}
		protected object openData_ = null;
		#endregion		
		#region LowData
		public object LowData
		{
			get
			{
				return this.lowData_;
			}
			set
			{
				this.lowData_ = value;
			}
		}
		protected object lowData_ = null;
		#endregion	
		#region HighData
		public object HighData
		{
			get
			{
				return this.highData_;
			}
			set
			{
				this.highData_ = value;
			}
		}
		protected object highData_ = null;
		#endregion	
		#region CloseData
		public object CloseData
		{
			get
			{
				return this.closeData_;
			}
			set
			{
				this.closeData_ = value;
			}
		}
		protected object closeData_ = null;
		#endregion	
		#region DataSource
		public object DataSource
		{
			get
			{
				return this.dataSource_;
			}
			set
			{
				this.dataSource_ = value;
			}
		}
		protected object dataSource_ = null;
		#endregion
		#region DataMember
		public string DataMember
		{
			get
			{
				return this.dataMember_;
			}
			set
			{
				this.dataMember_ = value;
			}
		}
		protected string dataMember_ = null;
		#endregion
		#region AbscissaData
		public object AbscissaData
		{
			get
			{
				return this.abscissaData_;
			}
			set
			{
				this.abscissaData_ = value;
			}
		}
		protected object abscissaData_ = null;
		#endregion

		#region DrawLegendLine
		/// <summary>
		/// Method that the trace classes use to draw the line
		/// in the plot legend.
		/// </summary>
		/// <param name="g">The Graphics surface.</param>
		/// <param name="startEnd">The RectangleF storing the position of the legend line.</param>
		public virtual void DrawLegendLine( Graphics g, RectangleF startEnd )
		{
			bool needToDisposePen_ = false;
			Pen	p;
			if ( this.pen_ != null )
			{
				p = (System.Drawing.Pen)this.pen_;
			}
			else
			{
				p =	new	Pen(this.color_);
				needToDisposePen_ = true;
			}

			g.DrawLine( p, startEnd.Left, (startEnd.Top + startEnd.Bottom)/2.0f, 
				startEnd.Right, (startEnd.Top + startEnd.Bottom)/2.0f );

			if (needToDisposePen_) p.Dispose();
		}
		#endregion
		#region ShowLegendLine
		public bool ShowLegendLine
		{
			get
			{
				return showLegendLine_;
			}
			set
			{
				this.showLegendLine_ = value;
			}
		}
		private bool showLegendLine_ = true;
		#endregion
		#region set Pen
		/// <summary>
		/// Accessor for the Pen used to draw the trace.
		/// </summary>
		public System.Drawing.Pen Pen
		{
			set
			{
				pen_ = value;
			}
		}
		/// <summary>
		/// The protected Pen member used to draw the trace.
		/// </summary>
		protected System.Drawing.Pen pen_ = new Pen(Color.Black);
		#endregion
		#region get/set Color
		/// <summary>
		/// Plot Color property.
		/// </summary>
		public System.Drawing.Color Color
		{
			set
			{
				color_ = value;
			}
			get
			{
				return color_;
			}
		}
		/// <summary>
		/// Color of Plot [TODO: Did we agree not to have this here!?!!].
		/// </summary>
		protected System.Drawing.Color color_ = Color.Black;
		#endregion
		#region Label
		/// <summary>
		/// The trace label.
		/// </summary>
		public string Label
		{
			get
			{
				return label_;
			}
			set
			{
				this.label_ = value;
			}
		}
		
		private string label_ = "";
		#endregion
	}
}
