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

namespace InstrumentationControls
{
	[ToolboxBitmap(typeof (CircularGauge))]
	public class CircularGauge : ValuePresenter
	{

		private int fCenterOrientation = 0;
		private bool fReverseScale = false;
		private Color dangerColor = Color.Red;
		private int dangerValue = 70;
		private Color dialColor = Color.White;
		private int largeTickFrequency = 10;
		private int numberFrequency = 10;
		private Color pointerColor = Color.Red;
		private Color fBorderColor = SystemColors.ControlDarkDark;
		private bool showGradient = false;
		private bool showNumbers = true;
		private int smallTickFrequency = 2;
		private int totalAngle = 270;
		private Font fTextFooterFont = new Font(FontFamily.GenericSansSerif,12);
		private string fTextFooter = "";
		private Color fTextFooterForeColor = Color.White;
		private int fTicksWidth = 1;


		protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs pevent)
		{
			
			base.OnPaintBackground(pevent);
		}
		protected override void OnResize(EventArgs e)
		{
			base.OnResize(e);
			SetRoundRegion();
		}
		private void SetRoundRegion()
		{
			GraphicsPath gp = new GraphicsPath();
			gp.FillMode = FillMode.Alternate;
			gp.AddEllipse(Center.X - Radius, Center.Y - Radius, Radius * 2, Radius * 2);
			Region rg = new Region(gp);
			this.Region  = rg;
		}

		protected override void Draw(Graphics g, Rectangle r)
		{
			g.SmoothingMode = SmoothingMode.AntiAlias;
			float Scale = Radius / 55f;
			float FontSize = (6 * Scale);
			if (FontSize < 6)
				FontSize = 6;
			float FooterFontSize = Scale * TextFooterFont.Size;
			base.Draw(g, r);
			Rectangle workRectangle = WorkRectangle;
			using (Pen pen = new Pen(ForeColor,fTicksWidth*Scale))
			{
				using (Pen pen2 = new Pen(dangerColor,fTicksWidth*Scale))
				{
					pen.EndCap = LineCap.Round;
					pen2.EndCap = LineCap.Round;
					Brush brush;
					if (ShowGradient)
					{
						brush = new LinearGradientBrush(workRectangle, GraphicsUtils.ScaleColor(dialColor, 0.5f), dialColor, 1);
					}
					else
					{
						brush = new SolidBrush(dialColor);
					}
					using (Brush brush2 = new SolidBrush(ForeColor))
					{
						Rectangle rect = WorkRectangle;
						if (rect.Width != rect.Height)
						{
							int smallest = rect.Width > rect.Height ? rect.Height : rect.Width;
							if (rect.Width > smallest)
								rect.Width -= (rect.Width - smallest);
							if (rect.Height > smallest)
								rect.Height -= (rect.Height - smallest);
						}
						g.FillEllipse(brush, Center.X - Radius, Center.Y - Radius, Radius * 2, Radius * 2);
						if(fBorderColor != dialColor)
						{
							using(Pen borderPen = new Pen(fBorderColor, 2*Scale))
							{
								g.DrawEllipse(borderPen, Center.X - Radius, Center.Y - Radius, Radius * 2, Radius * 2);
							}
						}
						Point center = Center;

						float AngleValuePercent = ((float) (base.Value - Minimum)) / (Maximum - Minimum);
						float angle = AngleValuePercent * totalAngle;
						Font font = new Font(Font.FontFamily, FontSize);
						
						if (Text != "")
						{
							g.DrawString(Text, font, brush2,
							             new Point(center.X - ((int) (g.MeasureString(Text, font).Width / 2f)),
							                       (center.Y - (int)(10*Scale)) - (font.Height / 2)));
						}
						if(!string.IsNullOrEmpty(fTextFooter))
							using (Brush FooterBrush = new SolidBrush(fTextFooterForeColor))
							{
								Font font2 = new Font(TextFooterFont.FontFamily, FooterFontSize);
								g.DrawString(fTextFooter, font2, FooterBrush,
														 new Point(center.X - ((int)(g.MeasureString(fTextFooter, font2).Width / 2f)),
																			 (center.Y + (int)(10 * Scale)) - (font2.Height / 2)));

							}
						int maxWidth = 0;
						for (int i = Minimum; i <= Maximum; i += smallTickFrequency)
						{
							string text = FormatValue(i);
							if (showNumbers && ((i % numberFrequency) == 0))
							{
								SizeF ef = g.MeasureString(text, font);
								if (maxWidth < ef.Width)
									maxWidth = (int) ef.Width;
							}
						}
						int TicksNumbersRadius = Radius - maxWidth / 2 - (int)(2 * Scale);
						int TicksScaleRadius = Radius - maxWidth - (int)(6 * Scale);
						int SmallTicksRadius = TicksScaleRadius + (int)(2*Scale);
						int LargeTicksRadius = TicksScaleRadius + (int)(4 * Scale);
						DrawPointer(g, center, angle, TicksNumbersRadius * 0.75f);
						for (int i = Minimum; i <= Maximum; i += smallTickFrequency)
						{
							AngleValuePercent = ((float)(i - Minimum)) / (Maximum - Minimum);
							angle = AngleValuePercent * totalAngle;

							float VisualAngle = (totalAngle / 2f) - angle + 360 - fCenterOrientation;
							if (fReverseScale)
								VisualAngle = 360 - VisualAngle;

							double TickXmin = center.X - (Math.Sin((VisualAngle / 180f) * Math.PI) * TicksScaleRadius);
							double TickYmin = center.Y - (Math.Cos((VisualAngle / 180f) * Math.PI) * TicksScaleRadius);
							double CurrTicksRadius = SmallTicksRadius;
							if ((i % largeTickFrequency) == 0)
							{
								CurrTicksRadius = LargeTicksRadius;
							}
							if (showNumbers && ((i % numberFrequency) == 0))
							{
								double NumberX = center.X - (Math.Sin((VisualAngle / 180f) * Math.PI) * TicksNumbersRadius);
								double NumberY = center.Y - (Math.Cos((VisualAngle / 180f) * Math.PI) * TicksNumbersRadius);
								string text = FormatValue(i);
								SizeF ef = g.MeasureString(text, font);
								NumberX -= ef.Width / 2f;
								NumberY -= ef.Height / 2f;
								g.DrawString(text, font, brush2,  new Point((int) NumberX, (int) NumberY));
							}
							double TickXmax = center.X - (Math.Sin((VisualAngle / 180f) * Math.PI) * CurrTicksRadius);
							double TickYmax = center.Y - (Math.Cos((VisualAngle / 180f) * Math.PI) * CurrTicksRadius);
							PointF TickMinPoint = new PointF((float) TickXmin, (float) TickYmin);
							PointF TickMaxPoint = new PointF((float) TickXmax, (float) TickYmax);
							if (i < dangerValue)
							{
								g.DrawLine(pen, TickMinPoint, TickMaxPoint);
							}
							else
							{
								g.DrawLine(pen2, TickMinPoint, TickMaxPoint);
							}
						}
						g.Flush();
					}
					brush.Dispose();
				}
			}
		}

		private void DrawPointer(Graphics g, Point p, float angle, float rad)
		{
			float Scale = Radius / 55f;
			float VisualAngle = ((totalAngle) / 2f) - angle - fCenterOrientation;
			if (fReverseScale)
				VisualAngle = 360 - VisualAngle;
			double num2 = p.X - (Math.Sin((VisualAngle / 180f) * Math.PI) * rad);
			double num3 = p.Y - (Math.Cos((VisualAngle / 180f) * Math.PI) * rad);
			Point point = new Point((int) num2, (int) num3);
			Pen pen = new Pen(pointerColor, 2 * Scale);
			pen.EndCap = LineCap.Round;
			pen.StartCap = LineCap.RoundAnchor;
			g.DrawLine(pen, p, point);
		}

		protected override void OnTextChanged(EventArgs e)
		{
			base.Invalidate();
			base.OnTextChanged(e);
		}

		private Point Center
		{
			get
			{
				Rectangle workRectangle = WorkRectangle;
				return new Point((workRectangle.Left + workRectangle.Right) / 2, (workRectangle.Bottom + workRectangle.Top) / 2);
			}
		}
		
		private int Radius
		{
			get
			{
				Rectangle workRectangle = WorkRectangle;
				return Math.Min((int)(workRectangle.Height / 2), (int)(workRectangle.Width / 2));
			}
		}

		protected override Size DefaultSize
		{
			get { return new Size(0xa8, 0xa8); }
		}


		[Description("Danger color"), DefaultValue(typeof (Color), "Red"), Category("Appearance")]
		public Color DangerColor
		{
			get { return dangerColor; }
			set
			{
				if (dangerColor != value)
				{
					dangerColor = value;
					base.Invalidate();
				}
			}
		}

		[Category("Behavior"), Description("Danger Value"), DefaultValue(70)]
		public int DangerValue
		{
			get { return dangerValue; }
			set
			{
				if (dangerValue != value)
				{
					dangerValue = value;
					base.Invalidate();
				}
			}
		}

		[DefaultValue(typeof (Color), "White"), Category("Appearance"), Description("Color of the dial")]
		public Color DialColor
		{
			get { return dialColor; }
			set
			{
				if (dialColor != value)
				{
					dialColor = value;
					base.Invalidate();
				}
			}
		}

		[Description("Define frequency of large ticks"), Category("Appearance"), DefaultValue(10)]
		public int LargeTickFrequency
		{
			get { return largeTickFrequency; }
			set
			{
				if (largeTickFrequency != value)
				{
					largeTickFrequency = value;
					base.Invalidate();
				}
			}
		}

		[DefaultValue(10), Description("Define frequency of numbers"), Category("Appearance")]
		public int NumberFrequency
		{
			get { return numberFrequency; }
			set
			{
				if (numberFrequency != value)
				{
					numberFrequency = value;
					base.Invalidate();
				}
			}
		}

		[Category("Appearance"), Description("Color of the pointer"), DefaultValue(typeof (Color), "Red")]
		public Color PointerColor
		{
			get { return pointerColor; }
			set
			{
				if (pointerColor != value)
				{
					pointerColor = value;
					base.Invalidate();
				}
			}
		}

		[Category("Appearance"), Description("Footer Text"), DefaultValue(typeof(string), "")]
		public string TextFooter
		{
			get
			{
				return fTextFooter;
			}
			set
			{
				if (fTextFooter != value)
				{
					fTextFooter = value;
					Invalidate();
				}
			}
		}

		[Category("Appearance"), Description("Footer Text foreground color "), DefaultValue(typeof(Color), "Black")]
		public Color TextFooterForeColor
		{
			get
			{
				return fTextFooterForeColor;
			}
			set
			{
				if(value != fTextFooterForeColor)
				{
					fTextFooterForeColor = value;
					if(!string.IsNullOrEmpty(fTextFooter))
						Invalidate();
				}
			}
		}

		[Category("Appearance"), Description("Footer Text Font ")]
		public Font TextFooterFont
		{
			get { return fTextFooterFont; }
			set
			{
				if(value != fTextFooterFont)
				{
					fTextFooterFont = value;
					Invalidate();
				}
			}
		}
		
		[Category("Appearance"), Description("Ticks width"), DefaultValue(1)]
		public int TicksWidth
		{
			get
			{
				return fTicksWidth;
			}
			set
			{
				if (value > -1 && Value <= 10)
				{
					if (value != fTicksWidth)
					{
						fTicksWidth = value;
						Invalidate();
					}
				}
			}
		}

		[Description("Gets or sets whether control should use gradient background"), DefaultValue(false),
		 Category("Appearance")]
		public bool ShowGradient
		{
			get { return showGradient; }
			set
			{
				if (showGradient != value)
				{
					showGradient = value;
					base.Invalidate();
				}
			}
		}

		[Category("Appearance"), DefaultValue(true), Description("Indicate if the control paints scale numbers")]
		public bool ShowNumbers
		{
			get { return showNumbers; }
			set
			{
				if (showNumbers != value)
				{
					showNumbers = value;
					base.Invalidate();
				}
			}
		}

		[DefaultValue(2), Category("Appearance"), Description("Define frequency of small ticks")]
		public int SmallTickFrequency
		{
			get { return smallTickFrequency; }
			set
			{
				if (smallTickFrequency != value)
				{
					smallTickFrequency = value;
					base.Invalidate();
				}
			}
		}

		[DefaultValue(270), Description("Angle behind minimum and maximum positions"), Category("Appearance")]
		public int TotalAngle
		{
			get { return totalAngle; }
			set
			{
				if (totalAngle != value)
				{
					totalAngle = value;
					base.Invalidate();
				}
			}
		}
		
		[Description("Orientation of center values scale"), Category("Appearance")]
		public int CenterOrientation
		{
			get { return fCenterOrientation; }
			set
			{
				if (fCenterOrientation != value)
				{
					fCenterOrientation = value;
					if (Math.Abs(fCenterOrientation) > 359)
						fCenterOrientation = fCenterOrientation % 360;
					base.Invalidate();
				}
			}
		}
		[Category("Appearance"), DefaultValue(false), Description("Direction of Scale, forward jr reverse")]
		public bool ReverseScale
		{
			get { return fReverseScale; }
			set
			{
				if (fReverseScale != value)
				{
					fReverseScale = value;
					base.Invalidate();
				}
			}
		}
		[Category("Appearance"), DefaultValue(typeof(Color), "SystemColors.ControlDarkDark"), Description("Color of border, circle")]
		public Color BorderColor
		{
			get { return fBorderColor; }
			set
			{
				if (fBorderColor != value)
				{
					fBorderColor = value;
					Invalidate();
				}
			}
		}
	}
}