using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Diagnostics;

namespace Hanna.Controls
{
	/// <summary>
	/// Summary description for Shape.
	/// </summary>
	public class Shape : System.Windows.Forms.UserControl
	{
		#region shape enums

		public enum PrimitiveShapes
		{
			Ellipse,
			Rectangle
		};
		public enum BorderStyles
		{
			None,
			Solid,
			Engraved,
			Embossed,
			Forged,
			Stamped
		};
		public enum FillStyles
		{
			Flat,
			Raised,
			Sunken
		}

		#endregion

		#region Declarations

		private bool			_initializing		= true;
		private int				_borderWidth		= 0; 
		private BorderStyles	_borderStyle		= BorderStyles.None;
		private FillStyles		_fillStyle			= FillStyles.Flat;
		private Color			_borderDark			= Color.Black;
		private Color			_borderLight		= Color.White;
		private Color			_backColorLight		= Color.White;
		private SmoothingMode	_smoothMode			= SmoothingMode.Default;
		private PrimitiveShapes _shape				= PrimitiveShapes.Rectangle;
		private Brush			_lineBrush			= new SolidBrush(SystemColors.ControlText);
		private Brush			_fillBrush			= new SolidBrush(SystemColors.Control);
		private Brush			_borderBrushDark	= new SolidBrush(Color.Black);
		private Brush			_borderBrushLight	= new SolidBrush(Color.White);
		protected Region		FillRegion			= null;
		protected Region		ShadowRegionUp		= null;
		protected Region		ShadowRegionDown	= null;

		#endregion

		#region construct/dispose/init

		public Shape() 
		{
			InitializeComponent();
		}
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			if ( this._lineBrush		!= null ) this._lineBrush.Dispose();
			if ( this._fillBrush		!= null ) this._fillBrush.Dispose();
			if ( this._borderBrushDark	!= null ) this._borderBrushDark.Dispose();
			if ( this._borderBrushLight	!= null ) this._borderBrushLight.Dispose();
			if ( this.FillRegion		!= null ) this.FillRegion.Dispose();
			if ( this.ShadowRegionUp	!= null ) this.ShadowRegionUp.Dispose();
			if ( this.ShadowRegionDown	!= null ) this.ShadowRegionDown.Dispose();
			base.Dispose( disposing );
		}
		private void Shape_Load(object sender, EventArgs e)
		{
			_initializing = false;
			SetRegion();
			this.Invalidate();
		}

		#endregion

		#region Component Designer generated code
		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.SuspendLayout();
			// 
			// Shape
			// 
			this.Name = "Shape";
			this.Size = new System.Drawing.Size(64, 64);
			this.Load += new System.EventHandler(this.Shape_Load);
			this.Resize += new System.EventHandler(this.Shape_Resize);
			this.ForeColorChanged += new System.EventHandler(this.Shape_ForeColorChanged);
			this.BackColorChanged += new System.EventHandler(this.Shape_BackColorChanged);
			this.Paint += new System.Windows.Forms.PaintEventHandler(this.Shape_Paint);
			this.ResumeLayout(false);

		}
		#endregion

		#region Overridable members

		protected virtual Brush	LineBrush	
		{
			get
			{
				return this._lineBrush;
			}
			set
			{
				if (value != null)
					if (!value.Equals(this._lineBrush))
					{
						this._lineBrush = value;
						this.Invalidate();
					}
			}
		}
		protected virtual Brush	FillBrush	
		{
			get
			{
				return this._fillBrush;
			}
			set
			{
				if ( ! value.Equals ( this._fillBrush ) )
				{
					this._fillBrush=value;
					this.Invalidate();
				}
			}
		}

		protected virtual GraphicsPath GetShapePath(Rectangle rectangle)
		{
			GraphicsPath path = new GraphicsPath();
			//	set control Region
			switch ( this._shape )
			{
				case PrimitiveShapes.Ellipse:
					path.AddEllipse   ( rectangle );
					break;
				case PrimitiveShapes.Rectangle:
					path.AddRectangle ( rectangle );
					break;
			}		
			return path;
		}
		protected virtual void SetRegion()
		{
			if (!_initializing)
			{
				this.SetShapeRegion();
				this.SetFillRegion();
				this.SetShadowRegion();
				ResetBrushes();
			}
		}
		protected virtual void SetShapeRegion()
		{
			if (!_initializing)
			{
				Rectangle rect = this.ClientRectangle;
				using (GraphicsPath path = GetShapePath(rect))
				{
					if (this.Region != null)
						this.Region.Dispose();
					this.Region = new Region(path);
				}
			}
		}
		protected virtual void SetFillRegion()
		{
			if (!_initializing)
			{
				Rectangle rect = this.ClientRectangle;
				if (this.BorderStyle != BorderStyles.None)
					rect.Inflate(-(this.BorderWidth * 2), -(this.BorderWidth * 2));
				using (GraphicsPath path = GetShapePath(rect))
				{
					if (this.FillRegion != null)
						this.FillRegion.Dispose();
					this.FillRegion = new Region(path);
				}
			}
		}
		protected virtual void SetShadowRegion()
		{
			if (!_initializing)
			{

				Rectangle rect = this.ClientRectangle;
				if ((this.BorderStyle != BorderStyles.None) &&
					 (this.BorderWidth > 0))
				{
					rect.Inflate(-(this.BorderWidth * 2), -(this.BorderWidth * 2));
					if ((this.BorderStyle == BorderStyles.Embossed) ||
						 (this.BorderStyle == BorderStyles.Engraved))
					{
						// offset rectangle up and left to create ULeft shadow
						rect.Offset(-this.BorderWidth, -this.BorderWidth);
						using (GraphicsPath path = GetShapePath(rect))
						{
							if (this.ShadowRegionUp != null)
								this.ShadowRegionUp.Dispose();
							this.ShadowRegionUp = new Region(path);
						}
						// offset rectangle down and right to create ULeft shadow
						rect.Offset(this.BorderWidth * 2, this.BorderWidth * 2);
						using (GraphicsPath path = GetShapePath(rect))
						{
							if (this.ShadowRegionDown != null)
								this.ShadowRegionDown.Dispose();
							this.ShadowRegionDown = new Region(path);
						}
					}
				}
			}
		}

		#endregion

		#region public members

		public SmoothingMode ShapeSmoothingMode 
		{
			get
			{
				return this._smoothMode;
			}
			set 
			{
				if ( this._smoothMode != value )
				{
					this._smoothMode = value;
					this.Invalidate();
				}
			}
		}
		public PrimitiveShapes PrimitiveShape 
		{
			get
			{
				return this._shape;
			}
			set 
			{
				if ( this._shape != value )
				{
					this._shape = value;
					this.SetRegion();
					this.Invalidate();
				}
			}
		}
		public FillStyles FillStyle
		{
			get
			{
				return this._fillStyle;
			}
			set 
			{
				if ( this._fillStyle != value )
				{
					this._fillStyle = value;
					this.ResetBrushes();
					this.Invalidate();
				}
			}
		}
		public new BorderStyles BorderStyle
		{
			get
			{
				return this._borderStyle;
			}
			set 
			{
				if ( this._borderStyle != value )
				{
					this._borderStyle = value;
					this.SetRegion();
					this.ResetBrushes();
					this.Invalidate();
				}
			}
		}
		public virtual int BorderWidth 
		{
			get
			{	
				return this._borderWidth;
			}
			set
			{
				if ( this._borderWidth != value )
				{
					this._borderWidth = value;
					this.SetRegion();
					this.Invalidate();
				}
			}
		}
		public Color BorderColorDark
		{
			get { return this._borderDark ; }
			set 
			{
				if ( ! this._borderDark.Equals(	value ) )
				{
					this._borderDark = value;
					ResetBrushes();
					this.Invalidate();
				}
			}
		}
		public Color BorderColorLight
		{
			get { return this._borderLight ; }
			set 
			{
				if ( ! this._borderLight.Equals(	value ) )
				{
					this._borderLight = value;
					ResetBrushes();
					this.Invalidate();
				}
			}
		}
		public Color BackColorLight
		{
			get { return this._backColorLight ; }
			set 
			{
				if ( ! this._backColorLight.Equals(	value ) )
				{
					this._backColorLight = value;
					ResetBrushes();
					this.Invalidate();
				}
			}
		}

      #endregion	

		#region Private drawing helpers

		protected virtual void Shape_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
		{
			if ( this.Region != null )
			{
				if ( this.FillRegion != null )
				{
					try
					{
						e.Graphics.SmoothingMode = this._smoothMode;
						e.Graphics.FillRegion(this.LineBrush, this.Region);

						switch (this.BorderStyle)
						{
							case BorderStyles.Embossed:
								e.Graphics.FillRegion(this._borderBrushLight, this.ShadowRegionUp);
								e.Graphics.FillRegion(this._borderBrushDark, this.ShadowRegionDown);
								break;
							case BorderStyles.Engraved:
								e.Graphics.FillRegion(this._borderBrushDark, this.ShadowRegionUp);
								e.Graphics.FillRegion(this._borderBrushLight, this.ShadowRegionDown);
								break;
						}
						e.Graphics.FillRegion(this.FillBrush, this.FillRegion);
					}
					catch (Exception ex)
					{
						MessageBox.Show("Exception in shape (" + this.GetType().Name + " paint: " + ex.ToString());
					}
				}
			}
		}	
		private void Shape_Resize(object sender, System.EventArgs e)
		{
			this.SetRegion();
			this.Invalidate();
		}      

		private void Shape_ForeColorChanged(object sender, System.EventArgs e)
		{
			ResetBrushes();
			this.Invalidate();
		}
		private void Shape_BackColorChanged(object sender, System.EventArgs e)
		{
			ResetBrushes();
			this.Invalidate();
		}
		private Color AverageColor( Color Color1, Color Color2 )
		{
			int R = (int)( Color1.R + Color2.R ) / 2;
			int G = (int)( Color1.G + Color2.G ) / 2;
			int B = (int)( Color1.B + Color2.B ) / 2;
			return Color.FromArgb(R,G,B);
		}

		private void ResetBrushes()
		{
			if (!_initializing)
			{
				try
				{
					ResetLineBrush();
					ResetFillBrush();
				}
				catch (Exception ex)
				{
					MessageBox.Show("Error in ShapePaint: " + ex.ToString());
				}
			}
		}
		private void ResetLineBrush()
		{
			if (!_initializing)
			{

				if (this._lineBrush != null)
					this._lineBrush.Dispose();

				switch (this._borderStyle)
				{
					case BorderStyles.None:
						this.LineBrush = new SolidBrush(this.BackColor);
						break;
					case BorderStyles.Solid:
						this.LineBrush = new SolidBrush(this.ForeColor);
						break;
					case BorderStyles.Forged:
						this.LineBrush = GetShadowingBrush(this.ClientRectangle, this._borderLight, this._borderDark);
						break;
					case BorderStyles.Stamped:
						this.LineBrush = GetShadowingBrush(this.ClientRectangle, this._borderDark, this._borderLight);
						break;
					case BorderStyles.Embossed:
					case BorderStyles.Engraved:
						if (this._borderBrushDark != null) this._borderBrushDark.Dispose();
						if (this._borderBrushLight != null) this._borderBrushLight.Dispose();
						this._lineBrush = new SolidBrush(AverageColor(this._borderDark, this._borderLight));
						this._borderBrushDark = new SolidBrush(this._borderDark);
						this._borderBrushLight = new SolidBrush(this._borderLight);
						break;
				}
			}
		}
		private void ResetFillBrush()
		{
			if (!_initializing)
			{
				if (this._fillBrush != null)
					this._fillBrush.Dispose();

				Rectangle rect = this.ClientRectangle;
				if (this.BorderStyle != BorderStyles.None)
				{
					rect.Inflate(-(this.BorderWidth), -(this.BorderWidth));
				}

				switch (this._fillStyle)
				{
					case FillStyles.Flat:
						this._fillBrush = new SolidBrush(this.BackColor);
						break;
					case FillStyles.Raised:
						this._fillBrush = GetShadowingBrush(rect, this._backColorLight, this.BackColor);
						break;
					case FillStyles.Sunken:
						this._fillBrush = GetShadowingBrush(rect, this.BackColor, this._backColorLight);
						break;
				}
			}
		}
		private PathGradientBrush GetShadowingBrush(Rectangle rect, Color light, Color dark)
		{
			PointF[] pts = { 
								   rect.Location,
								   new PointF(rect.Left + rect.Width, rect.Top),	
								   new PointF(rect.Left + rect.Width, rect.Top + rect.Height),	
								   new PointF(rect.Left, rect.Top + rect.Height)
							   };
			Color avg = this.AverageColor(light, dark);
			Color[] colors = new Color[] {
												 light,
												 avg,  
												 dark,
												 avg
											 };
			if (rect.IsEmpty || rect.Width == 0 || rect.Height == 0)
				return null;
			else
				using (PathGradientBrush pgb = new PathGradientBrush(pts))
				{
					pgb.CenterColor = avg;
					pgb.SurroundColors = colors;
					pgb.WrapMode = WrapMode.Clamp;
					return (PathGradientBrush)pgb.Clone();
				}
		}

      #endregion

	}
}
