/*
 * NPlot - A charting library for .NET
 * 
 * Grid.cs
 * Copyright (C) 2003-2006 Matt Howlett and others.
 * All rights reserved.
 * 
 * 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 above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of NPlot nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
 */

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;

namespace NPlot
{

	/// <summary>
	/// Encapsulates a Grid IDrawable object. Instances of this  to a PlotSurface2D 
	/// instance to produce a grid.
	/// </summary>
	public class Grid : IDrawable
	{

		/// <summary>
		/// 
		/// </summary>
		public enum GridType
		{
			/// <summary>
			/// No grid.
			/// </summary>
			None = 0,
			/// <summary>
			/// Coarse grid. Lines at large tick positions only.
			/// </summary>
			Coarse = 1,
			/// <summary>
			/// Fine grid. Lines at both large and small tick positions.
			/// </summary>
			Fine = 2
		}


		/// <summary>
		/// Default constructor
		/// </summary>
		public Grid()
		{
			minorGridPen_ = new Pen( Color.LightGray );
			float[] pattern = {1.0f, 2.0f};
			minorGridPen_.DashPattern = pattern;
			
			majorGridPen_ = new Pen( Color.LightGray );

			horizontalGridType_ = GridType.Coarse;
			
			verticalGridType_ = GridType.Coarse;
		}


		/// <summary>
		/// Specifies the horizontal grid type (none, coarse or fine).
		/// </summary>
		public GridType HorizontalGridType
		{
			get
			{
				return horizontalGridType_;
			}
			set
			{
				horizontalGridType_ = value;
			}
		}
		GridType horizontalGridType_;


		/// <summary>
		/// Specifies the vertical grid type (none, coarse, or fine).
		/// </summary>
		public GridType VerticalGridType
		{
			get
			{
				return verticalGridType_;
			}
			set
			{
				verticalGridType_ = value;
			}
		}
		GridType verticalGridType_;


		/// <summary>
		/// The pen used to draw major (coarse) grid lines.
		/// </summary>
		public System.Drawing.Pen MajorGridPen
		{
			get
			{
				return majorGridPen_;
			}
			set
			{
				majorGridPen_ = value;
			}
		}
		private Pen majorGridPen_;


		/// <summary>
		/// The pen used to draw minor (fine) grid lines.
		/// </summary>
		public System.Drawing.Pen MinorGridPen
		{
			get
			{
				return minorGridPen_;
			}
			set
			{
				minorGridPen_ = value;
			}
		}
		private Pen minorGridPen_;


		/// <summary>
		/// Does all the work in drawing grid lines.
		/// </summary>
		/// <param name="g">The graphics surface on which to render.</param>
		/// <param name="axis">TODO</param>
		/// <param name="orthogonalAxis">TODO</param>
		/// <param name="a">the list of world values to draw grid lines at.</param>
		/// <param name="horizontal">true if want horizontal lines, false otherwise.</param>
		/// <param name="p">the pen to use to draw the grid lines.</param>
		private void DrawGridLines( 
			Graphics g, PhysicalAxis axis, PhysicalAxis orthogonalAxis,
			System.Collections.ArrayList a, bool horizontal, Pen p )
		{
			for (int i=0; i<a.Count; ++i)
			{
				PointF p1 = axis.WorldToPhysical((double)a[i], true);
				PointF p2 = p1;
				PointF p3 = orthogonalAxis.PhysicalMax;
				PointF p4 = orthogonalAxis.PhysicalMin;
				if (horizontal)
				{
					p1.Y = p4.Y;
					p2.Y = p3.Y;
				}
				else
				{
					p1.X = p4.X;
					p2.X = p3.X;
				}
				// note: casting all drawing was necessary for sane display. why?
				g.DrawLine( p, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y );
			}
		}

		/// <summary>
		/// Draws the grid
		/// </summary>
		/// <param name="g">The graphics surface on which to draw</param>
		/// <param name="xAxis">The physical x axis to draw horizontal lines parallel to.</param>
		/// <param name="yAxis">The physical y axis to draw vertical lines parallel to.</param>
		public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
		{

			ArrayList xLargePositions = null;
			ArrayList yLargePositions = null;
			ArrayList xSmallPositions = null;
			ArrayList ySmallPositions = null;

			if (this.horizontalGridType_ != GridType.None)
			{
				xAxis.Axis.WorldTickPositions_FirstPass( xAxis.PhysicalMin, xAxis.PhysicalMax, out xLargePositions, out xSmallPositions );
				DrawGridLines( g, xAxis, yAxis, xLargePositions, true, this.MajorGridPen );	
			}

			if (this.verticalGridType_ != GridType.None)
			{
				yAxis.Axis.WorldTickPositions_FirstPass( yAxis.PhysicalMin, yAxis.PhysicalMax, out yLargePositions, out ySmallPositions );
				DrawGridLines( g, yAxis, xAxis, yLargePositions, false, this.MajorGridPen );
			}


			if (this.horizontalGridType_ == GridType.Fine)
			{
				xAxis.Axis.WorldTickPositions_SecondPass( xAxis.PhysicalMin, xAxis.PhysicalMax, xLargePositions, ref xSmallPositions );
				DrawGridLines( g, xAxis, yAxis, xSmallPositions, true, this.MinorGridPen );
			}

			if (this.verticalGridType_ == GridType.Fine)
			{
				yAxis.Axis.WorldTickPositions_SecondPass( yAxis.PhysicalMin, yAxis.PhysicalMax, yLargePositions, ref ySmallPositions );
				DrawGridLines( g, yAxis, xAxis, ySmallPositions, false, this.MinorGridPen );
			}

		}

	}
}
