/*
 * NPlot - A charting library for .NET
 * 
 * LegendBase.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.Collections;
using System.Drawing;

namespace NPlot
{

	/// <summary>
	/// Provides functionality for drawing legends.
	/// </summary>
	/// <remarks>
	/// The class is quite closely tied to PlotSurface2D. 
	/// </remarks>
	public class LegendBase
	{

		/// <summary>
		/// Constructor.
		/// </summary>
		public LegendBase()
		{
			this.Font = new Font( new FontFamily("Arial"), 10, FontStyle.Regular, GraphicsUnit.Pixel );
			this.BackgroundColor = Color.White;
			this.BorderColor = Color.Black;
			this.TextColor = Color.Black;
			this.borderStyle_ = BorderType.Shadow;
			this.autoScaleText_ = false;
		}


		/// <summary>
		/// Get the bounding box of the rectangle.
		/// </summary>
		/// <param name="position">the position of the top left of the legend.</param>
		/// <param name="plots">Array of plot objects to appear in the legend.</param>
		/// <param name="scale">if the legend is set to scale, the amount to scale by.</param>>
		/// <returns></returns>
		/// <remarks>do implementation that doesn't call draw. Change xPos, yPos to PointF</remarks>
		public Rectangle GetBoundingBox( Point position, ArrayList plots, float scale )
		{
			System.Drawing.Bitmap b = new System.Drawing.Bitmap(1,1);
			Graphics g = Graphics.FromImage(b);
			return this.Draw( g, position, plots, scale );
		}


		/// <summary>
		/// Draw The legend
		/// </summary>
		/// <param name="g">The graphics surface on which to draw</param>
		/// <param name="position">The position of the top left of the axis.</param>
		/// <param name="plots">Array of plot objects to appear in the legend.</param>
		/// <param name="scale">if the legend is set to scale, the amount to scale by.</param>
		/// <returns>bounding box</returns>
		public Rectangle Draw( Graphics g, Point position, ArrayList plots, float scale )
		{

			// first of all determine the Font to use in the legend.
			Font textFont;
			if (this.AutoScaleText)
			{
				textFont = Utils.ScaleFont( this.font_, scale );
			}
			else
			{
				textFont = this.font_;
			}

			// determine max width and max height of label strings and
			// count the labels. 
			int labelCount = 0;
			int maxHt = 0;
			int maxWd = 0;
			int unnamedCount = 0;
			for (int i=0; i<plots.Count; ++i)
			{
				if (!(plots[i] is IPlot))
				{
					continue;
				}

				IPlot p = (IPlot)plots[i];

				if (!p.ShowInLegend)
				{
					continue;
				}
				string label = p.Label;
				if (label == "")
				{
					unnamedCount += 1;
					label = "Series " + unnamedCount.ToString();
				}
				SizeF labelSize = g.MeasureString( label, textFont );
				if ( labelSize.Height > maxHt )
				{
					maxHt = (int)labelSize.Height;
				}
				if ( labelSize.Width > maxWd )
				{
					maxWd = (int)labelSize.Width;
				}

				++labelCount;
			}

			bool extendingHorizontally = numberItemsHorizontally_ == -1;
			bool extendingVertically = numberItemsVertically_ == -1;


			// determine width in legend items count units.
			int widthInItemCount = 0;
			if (extendingVertically)
			{
				if (labelCount >= numberItemsHorizontally_)
				{
					widthInItemCount = numberItemsHorizontally_;
				}
				else
				{
					widthInItemCount = labelCount;
				}
			}
			else if (extendingHorizontally)
			{
				widthInItemCount = labelCount / numberItemsVertically_;
				if (labelCount % numberItemsVertically_ != 0)
					widthInItemCount += 1;
			}
			else
			{
				throw new NPlotException( "logic error in legend base" );
			}


			// determine height of legend in items count units.
			int heightInItemCount = 0;
			if (extendingHorizontally)
			{
				if (labelCount >= numberItemsVertically_)
				{
					heightInItemCount = numberItemsVertically_;
				}
				else
				{
					heightInItemCount = labelCount;
				}
			}
			else // extendingVertically
			{
				heightInItemCount = labelCount / numberItemsHorizontally_;
				if (labelCount % numberItemsHorizontally_ != 0)
					heightInItemCount += 1;
			}

			int lineLength = 20;
			int hSpacing = (int)(5.0f * scale);
			int vSpacing = (int)(3.0f * scale);
			int boxWidth = (int) ((float)widthInItemCount * (lineLength + maxWd + hSpacing * 2.0f ) + hSpacing); 
			int boxHeight = (int)((float)heightInItemCount * (maxHt + vSpacing) + vSpacing);

			int totalWidth = boxWidth;
			int totalHeight = boxHeight;

			// draw box around the legend.

			if ( this.BorderStyle == BorderType.Line )
			{
				g.FillRectangle( new SolidBrush( this.bgColor_ ), position.X, position.Y, boxWidth, boxHeight );
				g.DrawRectangle( new Pen( this.borderColor_ ), position.X, position.Y, boxWidth, boxHeight );
			}
			else if ( this.BorderStyle == BorderType.Shadow )
			{
				int offset = (int)(4.0f * scale);
				g.FillRectangle( new SolidBrush( Color.FromArgb(128, Color.Gray) ), position.X+offset, position.Y+offset, boxWidth, boxHeight );
				g.FillRectangle( new SolidBrush( this.bgColor_ ), position.X, position.Y, boxWidth, boxHeight );
				g.DrawRectangle( new Pen( this.borderColor_ ), position.X, position.Y, boxWidth, boxHeight );

				totalWidth += offset;
				totalHeight += offset;
			}

			/*
			   else if ( this.BorderStyle == BorderType.Curved )
			   {
				   // TODO. make this nice.
			   }
			*/

			else
			{
				// do nothing.
			}

			// now draw entries in box..
			labelCount = 0;
			unnamedCount = 0;

			int plotCount = -1;
			for (int i=0; i<plots.Count; ++i)
			{
				if (!(plots[i] is IPlot))
				{
					continue;
				}

				IPlot p = (IPlot)plots[i];

				if (!p.ShowInLegend)
				{
					continue;
				}

				plotCount += 1;

				int xpos, ypos;
				if (extendingVertically)
				{
					xpos = plotCount % numberItemsHorizontally_;
					ypos = plotCount / numberItemsHorizontally_;
				}
				else
				{
					xpos = plotCount / numberItemsVertically_;
					ypos = plotCount % numberItemsVertically_;
				}

				int lineXPos = (int)(position.X + hSpacing + xpos * (lineLength + maxWd + hSpacing * 2.0f));
				int lineYPos = (int)(position.Y + vSpacing + ypos * (vSpacing + maxHt));
				p.DrawInLegend( g, new Rectangle( lineXPos, lineYPos, lineLength, maxHt ) );
				
				int textXPos = lineXPos + hSpacing + lineLength;
				int textYPos = lineYPos;
				string label = p.Label;
				if (label == "")
				{
					unnamedCount += 1;
					label = "Series " + unnamedCount.ToString();
				}

				g.DrawString( label, textFont,
					new SolidBrush( this.textColor_ ), textXPos, textYPos );

				++labelCount;
			}
			return new Rectangle( position.X, position.Y, totalWidth, totalHeight );

		}


		/// <summary>
		/// The font used to draw text in the legend.
		/// </summary>
		public Font Font
		{
			get
			{
				return this.font_;
			}
			set
			{
				this.font_ = value;
			}
		}
		private Font font_;


		/// <summary>
		/// The color used to draw text in the legend.
		/// </summary>
		public Color TextColor
		{
			get
			{
				return this.textColor_;
			}
			set
			{
				this.textColor_ = value;
			}
		}
		Color textColor_;


		/// <summary>
		/// The background color of the legend.
		/// </summary>
		public Color BackgroundColor
		{
			get
			{
				return bgColor_;
			}
			set
			{
				bgColor_ = value;
			}
		}
		Color bgColor_;


		/// <summary>
		/// The color of the legend border.
		/// </summary>
		public Color BorderColor
		{
			get
			{
				return borderColor_;
			}
			set
			{
				borderColor_ = value;
			}
		}
		Color borderColor_;


		/// <summary>
		/// The types of legend borders (enum).
		/// </summary>
		public enum BorderType
		{
			/// <summary>
			/// No border.
			/// </summary>
			None = 0,
			/// <summary>
			/// Line border.
			/// </summary>
			Line = 1,
			/// <summary>
			/// Shaded border.
			/// </summary>
			Shadow = 2
			//Curved = 3
		}


		/// <summary>
		/// The border style to use for the legend.
		/// </summary>
		public Legend.BorderType BorderStyle
		{
			get
			{
				return borderStyle_;
			}
			set
			{
				borderStyle_ = value;
			}
		}
		private NPlot.Legend.BorderType borderStyle_;


		/// <summary>
		/// Whether or not to auto scale text in the legend according the physical
		/// dimensions of the plot surface.
		/// </summary>
		public bool AutoScaleText
		{
			get
			{
				return autoScaleText_;
			}
			set
			{
				autoScaleText_ = value;
			}
		}
		bool autoScaleText_;


		/// <summary>
		/// Setting this does two things. First of all, it sets the maximum number of 
		/// items in the legend vertically. Second of all, it makes the legend grow
		/// horizontally (as it must given this constraint).
		/// </summary>
		public int NumberItemsVertically
		{
			set
			{
				this.numberItemsVertically_ = value;
				this.numberItemsHorizontally_ = -1;
			}
		}
		int numberItemsVertically_ = -1;


		/// <summary>
		/// Setting this does two things. First of all, it sets the maximum number of 
		/// items in the legend horizontally. Second of all, it makes the legend grow
		/// vertically (as it must given this constraint).
		/// </summary>
		public int NumberItemsHorizontally
		{
			set
			{
				this.numberItemsHorizontally_ = value;
				this.numberItemsVertically_ = -1;
			}
		}
		int numberItemsHorizontally_ = 1;

	}

}
