using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Resources;

namespace WheelCtrl
{
	
	public class WheelEventHandlerArgs : EventArgs
	{
		private int nX;
		private int nY;

		public WheelEventHandlerArgs(int x, int y)
		{
			this.nX = x;
			this.nY = y;
		}

		public int X
		{
			get
			{
				return nX;
			}
		}
		
		public int Y
		{
			get
			{
				return nY;
			}
		}
	}
	
	public class WheelCtrl : System.Windows.Forms.UserControl
	{
		public enum	Orientation { XAxe, YAxe };
		public delegate void WheelEventHandler(object sender, WheelEventHandlerArgs e);
		public event WheelEventHandler WheelDrag;
		private System.ComponentModel.IContainer components;
		private Image aImage;
		private double	dSensitivity;
		private double	dMin;
		private double	dMax;
		private double	dValue;
		private bool	bLoop;
		private Orientation wheelAxe;
		private int		nPos;
		private int		lastX;
		private int		lastY;
		private bool	bCaptured;

		// Properties
		public double Sensitivity
		{
			get
			{
				return dSensitivity;
			}
			set
			{
				dSensitivity = Math.Abs(value);
			}
		}
		public double Min
		{
			get
			{
				return dMin;
			}
			set
			{
				dMin = value;
			}
		}
		public double Max
		{
			get
			{
				return dMax;
			}
			set
			{
				dMax = value;
			}
		}
		public double Value
		{
			get
			{
				return dValue;
			}
			set
			{
				if( value < dMin ) dValue = dMin;
				else if( value > dMax) dValue = dMax;
				else dValue = value;
			}
		}
		public bool Loop
		{
			get
			{
				return bLoop;
			}
			set
			{
				bLoop = value;
			}
		}
		public Orientation WheelAxe
		{
			get
			{
				return wheelAxe;
			}
			set
			{
				wheelAxe = value;
				RotateComponent();
			}
		}		
		
		public WheelCtrl()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			
			// Set Defaults
			bCaptured = false;
			dSensitivity = 1.0;
			dMin = 0.0;
			dMax = 1.0;
			dValue = (dMin + dMax) / 2.0;
			bLoop = false;
			wheelAxe = Orientation.XAxe;

			nPos = 4;
			lastX = lastY = 0;
			
			RotateComponent();
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if( components != null )
					components.Dispose();
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			// 
			// WheelCtrl
			// 
			this.Name = "WheelCtrl";
			this.Size = new System.Drawing.Size(56, 8);

		}
		#endregion

		protected override void OnPaint(PaintEventArgs e)
		{
			Graphics grfx = e.Graphics;
			Rectangle destRect;
			Rectangle srcRect;
			
			if(wheelAxe == Orientation.YAxe)
			{
				destRect = new Rectangle(0, 0, 8, aImage.Height);
				srcRect = new Rectangle(nPos*(8 - 1), 0, 8 , aImage.Height);
			}
			else
			{
				destRect = new Rectangle(0, 0, aImage.Width, 8);
				srcRect = new Rectangle(0, nPos*(8 - 1), aImage.Width, 8);
			}
			
			grfx.DrawImage(aImage, destRect, srcRect, GraphicsUnit.Pixel);
		}

		protected override void OnResize(EventArgs e)
		{
			if(this.DesignMode)
			{
				if(wheelAxe == Orientation.YAxe)
				{
					this.Size = new Size(8, aImage.Height);
				}
				else
				{
					this.Size = new Size(aImage.Width, 8);
				}
			}
		}

		protected override void OnMouseDown(MouseEventArgs e)
		{
			if(e.Button == MouseButtons.Left)
			{
				bCaptured = true;
			}
		}
		
		protected override void OnMouseUp(MouseEventArgs e)
		{
			if(bCaptured && (e.Button & MouseButtons.Left) != 0)
			{
				bCaptured = false;
			}
		}
		
		protected override void OnMouseMove(MouseEventArgs e)
		{
			if(bCaptured)
			{
				int delta;
				if(wheelAxe == Orientation.YAxe)
				{
					delta = e.Y - lastY;
				}
				else
				{
					delta = e.X - lastX;
				}

				DragWheel(delta);				
				this.Invalidate();
				
				// fire WheelDrag event
				WheelEventHandlerArgs eWheel = new WheelEventHandlerArgs(e.X, e.Y);
				if (WheelDrag != null)
				{
					WheelDrag(this, eWheel);
				}

				lastX = e.X;
				lastY = e.Y;
			}
		}
		
		private void DragWheel(int dx)
		{
			double ddx = (dx != 0) ? Math.Abs(dx) : 1.0;
			double xx  = dx / ddx * Math.Log(Math.Abs(dx) + 2.0);

			nPos = (int) ((xx != 0 && (nPos+xx <= 0 || nPos+xx >= 8 )) ? 4 : nPos + xx);
			ddx = dValue + xx + ((xx - xx/ddx) * dSensitivity);
			
			if(bLoop)
			{
				if(ddx > dMax)
				{
					dValue = (dMin - dMax) + ddx;
				}
				else if(ddx < dMin)
				{
					dValue = - (dMin - dMax) + ddx;
				}
				else
				{
					dValue = ddx;
				}
			}
			else
			{
				dValue = Math.Max( Math.Min(ddx, dMax), dMin );
			}
		}

		private void RotateComponent()
		{
			aImage = new Bitmap(GetType(), "wheelh.bmp");
			
			if(wheelAxe == Orientation.YAxe)
			{
				aImage.RotateFlip(RotateFlipType.Rotate90FlipX);
				this.Size = new Size(8, aImage.Height);
			}
			else
			{
				this.Size = new Size(aImage.Width, 8);
			}

			this.Invalidate();
		}
	}
}
