using System;
using GDI = System.Drawing;
using GDI2D = System.Drawing.Drawing2D;

namespace Icarus.Utilities.Graphics
{
	/// <summary>
	/// This class provides additional functionality for drawing such things as rounded rectangles through a Graphics object.
	/// </summary>
	/// The code is mostly adapted from this CodeProject article:
	/// http://www.codeproject.com/cs/media/ExtendedGraphics.asp
	public class ExtendedGraphics
	{
		private GDI.Graphics mGraphics; 

		/// <summary>
		/// The graphics object used for drawing.
		/// </summary>
		public GDI.Graphics Graphics 
		{ 
			get{ return this.mGraphics; } 
			set{ this.mGraphics = value; } 
		} 

		/// <summary>
		/// Creates a new extended graphics object.
		/// </summary>
		/// <param name="g">The graphics object used for drawing.</param>
		public ExtendedGraphics(GDI.Graphics g)
		{
			this.Graphics = g;
		}

		#region Fill/DrawRoundedRectangle
		/// <summary>
		/// Fills a rounded rectangle.
		/// </summary>
		/// <param name="brush"></param>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="radius"></param>
		public void FillRoundedRectangle(GDI.Brush brush, int x, int y, int width, int height, int radius) 
		{ 
			float fx = Convert.ToSingle(x);
			float fy = Convert.ToSingle(y); 
			float fwidth = Convert.ToSingle(width);
			float fheight = Convert.ToSingle(height); 
			float fradius = Convert.ToSingle(radius); 
			this.FillRoundedRectangle(brush, fx, fy, fwidth, fheight, fradius); 
		}

		/// <summary>
		/// Fills a rounded rectangle.
		/// </summary>
		/// <param name="brush"></param>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="radius"></param>
		public void FillRoundedRectangle(GDI.Brush brush, float x, float y, float width, float height, float radius)
		{
			GDI.RectangleF rectangle = new GDI.RectangleF(x, y, width, height);
			GDI2D.GraphicsPath path = this.GetRoundedRect(rectangle, radius);
			this.Graphics.FillPath(brush, path);
		}

		/// <summary>
		/// Draws a Rounded Rectangle.
		/// </summary>
		/// <param name="pen"></param>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="radius"></param>
		public void DrawRoundedRectangle(GDI.Pen pen, int x, int y, int width, int height, int radius) 
		{ 
			float fx = Convert.ToSingle(x);
			float fy = Convert.ToSingle(y); 
			float fwidth = Convert.ToSingle(width);
			float fheight = Convert.ToSingle(height); 
			float fradius = Convert.ToSingle(radius); 
			this.DrawRoundedRectangle(pen, fx, fy, fwidth, fheight, fradius); 
		}

		/// <summary>
		/// Draws a Rounded Rectangle.
		/// </summary>
		/// <param name="pen"></param>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="radius"></param>
		public void DrawRoundedRectangle(GDI.Pen pen, float x, float y, float width, float height, float radius) 
		{ 
			GDI.RectangleF rectangle = new GDI.RectangleF(x, y, width, height); 
			GDI2D.GraphicsPath path = this.GetRoundedRect(rectangle, radius); 
			this.Graphics.DrawPath(pen, path); 
		} 
		#endregion

		#region Rounded Rectangle Private Parts
		/// <summary>
		/// Returns a graphics path for a rounded rectangle.
		/// </summary>
		/// <param name="baseRect"></param>
		/// <param name="radius"></param>
		/// <returns></returns>
		private GDI2D.GraphicsPath GetRoundedRect(GDI.RectangleF baseRect, float radius) 
		{
			// if corner radius is less than or equal to zero, 
			// return the original rectangle 
			if(radius <= 0.0f)
			{ 
				GDI2D.GraphicsPath mPath = new GDI2D.GraphicsPath(); 
				mPath.AddRectangle(baseRect); 
				mPath.CloseFigure(); 
				return mPath;
			}

			// if the corner radius is greater than or equal to 
			// half the width, or height (whichever is shorter) 
			// then return a capsule instead of a lozenge 
			if(radius >= (Math.Min(baseRect.Width, baseRect.Height)) / 2.0f) 
			{
				return GetCapsule(baseRect); 
			}

			// create the arc for the rectangle sides and declare 
			// a graphics path object for the drawing 
			float diameter = radius * 2.0f; 
			GDI.SizeF sizeF = new GDI.SizeF(diameter, diameter);
			GDI.RectangleF arc = new GDI.RectangleF(baseRect.Location, sizeF); 
			GDI2D.GraphicsPath path = new GDI2D.GraphicsPath(); 

			// top left arc 
			path.AddArc(arc, 180, 90); 

			// top right arc 
			arc.X = baseRect.Right - diameter; 
			path.AddArc(arc, 270, 90); 

			// bottom right arc 
			arc.Y = baseRect.Bottom - diameter; 
			path.AddArc(arc, 0, 90); 

			// bottom left arc
			arc.X = baseRect.Left;     
			path.AddArc(arc, 90, 90);

			path.CloseFigure(); 
			return path; 
		}

		/// <summary>
		/// Returns a capsular path for the base rectangle.
		/// </summary>
		/// <param name="baseRect"></param>
		/// <returns></returns>
		private GDI2D.GraphicsPath GetCapsule(GDI.RectangleF baseRect) 
		{ 
			float diameter; 
			GDI.RectangleF arc; 
			GDI2D.GraphicsPath path = new GDI2D.GraphicsPath(); 

			try 
			{ 
				if(baseRect.Width > baseRect.Height) 
				{ 
					// return horizontal capsule 
					diameter = baseRect.Height; 
					GDI.SizeF sizeF = new GDI.SizeF(diameter, diameter);
					arc = new GDI.RectangleF(baseRect.Location, sizeF); 
					path.AddArc(arc, 90, 180); 
					arc.X = baseRect.Right - diameter; 
					path.AddArc(arc, 270, 180); 
				} 
				else if(baseRect.Width < baseRect.Height) 
				{ 
					// return vertical capsule 
					diameter = baseRect.Width;
					GDI.SizeF sizeF = new GDI.SizeF(diameter, diameter);
					arc = new GDI.RectangleF(baseRect.Location, sizeF); 
					path.AddArc(arc, 180, 180); 
					arc.Y = baseRect.Bottom - diameter; 
					path.AddArc(arc, 0, 180); 
				} 
				else
				{ 
					// return circle 
					path.AddEllipse(baseRect); 
				}
			} 
			catch(Exception)
			{
				path.AddEllipse(baseRect); 
			} 
			finally 
			{ 
				path.CloseFigure(); 
			} 
			return path; 
		} 
		#endregion 
	}
}
