using System;
using System.Drawing;
using System.Windows.Forms;

using BizHawk.Client.Common;
using BizHawk.Emulation.Common;

namespace BizHawk.Client.EmuHawk
{
	public partial class VirtualPadTargetScreen : UserControl, IVirtualPadControl
	{
		private readonly Pen BlackPen = new Pen(Brushes.Black, 2);
		private readonly Pen WhitePen = new Pen(Brushes.White, 2);
		private readonly Pen GrayPen = new Pen(Brushes.Gray, 2);
		private readonly Pen RedPen = new Pen(Brushes.Red, 2);

		private bool _isProgrammicallyChangingNumerics;
		private bool _isDragging;
		private bool _readonly;
		private bool _isSet; // The tool has to keep track of this because there is currently no way to know if a float button is being autoheld or just held
		
		private int? _overrideX = null;
		private int? _overrideY = null;

		public VirtualPadTargetScreen()
		{
			InitializeComponent();
		}

		private void VirtualPadTargetScreen_Load(object sender, EventArgs e)
		{
			XNumeric.Maximum = TargetPanel.Width - 1;
			YNumeric.Maximum = TargetPanel.Height - 1;
		}

		#region IVirtualPadControl Implementation

		public void UpdateValues()
		{
			TargetPanel.Refresh();
		}

		public void Clear()
		{
			Global.StickyXORAdapter.Unset(XName);
			Global.StickyXORAdapter.Unset(YName);
			_overrideX = null;
			_overrideY = null;
			_isSet = false;
			Refresh();
		}

		public void Set(IController controller)
		{
			var newX = controller.GetFloat(XName) / MultiplierX;
			var newY = controller.GetFloat(YName) / MultiplierY;

			var oldX = X / MultiplierX;
			var oldY = Y / MultiplierY;

			var changed = newX != oldX && newY != oldY;

			XNumeric.Value = (int)newX;
			YNumeric.Value = (int)newY;

			_overrideX = (int)newX;
			_overrideY = (int)newY;

			if (changed)
			{
				Refresh();
			}

			_isSet = true;
		}

		public bool ReadOnly
		{
			get
			{
				return _readonly;
			}

			set
			{
				if (_readonly != value)
				{
					XNumeric.Enabled =
						XLabel.Enabled =
						YNumeric.Enabled =
						YLabel.Enabled =
						!value;

					_readonly = value;
					Refresh();
				}

				if (!_readonly)
				{
					_overrideX = null;
					_overrideY = null;
				}
			}
		}

		#endregion

		// Size of the extra controls to the right / bottom of the target panel at 96 DPI
		private Size PaddingSize
		{
			get { return new Size(0, 30); }
		}

		public Size TargetSize
		{
			get { return TargetPanel.Size; }
			set
			{
				TargetPanel.Size = value;
				Size = UIHelper.Scale(value + PaddingSize);
			}
		}

		// These are the value that a maximum x or y actually represent, used to translate from control X,Y to values the core expects
		public int RangeX { get; set; }
		public int RangeY { get; set; }

		public float MultiplierX
		{
			get
			{
				if (RangeX > 0)
				{
					return RangeX / (float)TargetPanel.Width;
				}

				return 1;
			}
		}

		public float MultiplierY
		{
			get
			{
				if (RangeY > 0)
				{
					return RangeY / (float)TargetPanel.Height;
				}

				return 1;
			}
		}

		public void Bump(int? x, int? y)
		{
			if (x.HasValue)
			{
				X = X + x.Value;
			}

			if (y.HasValue)
			{
				Y = Y + y.Value;
			}

			Refresh();
		}

		public string XName { get; set; }
		public string YName { get; set; }

		public int X
		{
			get
			{
				return _overrideX ?? (int)(Global.StickyXORAdapter.GetFloat(XName) / MultiplierX);
			}

			set
			{
				if (value < 0)
				{
					XNumeric.Value = 0;
					
				}
				else if (value <= XNumeric.Maximum)
				{
					XNumeric.Value = value;
				}
				else
				{
					XNumeric.Value = XNumeric.Maximum;
				}

				Global.StickyXORAdapter.SetFloat(XName, (int)((float)XNumeric.Value * MultiplierX));
				_isSet = true;
			}
		}
		public int Y
		{
			get
			{
				return _overrideY ?? (int)(Global.StickyXORAdapter.GetFloat(YName) / MultiplierY);
			}

			set
			{
				if (value < 0)
				{
					YNumeric.Value = 0;
				}
				else if (value <= YNumeric.Maximum)
				{
					YNumeric.Value = value;
				}
				else
				{
					YNumeric.Value = YNumeric.Maximum;
				}

				Global.StickyXORAdapter.SetFloat(YName, (int)((float)YNumeric.Value * MultiplierY));
				_isSet = true;
			}
		}

		private void UpdatePanelFromNumeric()
		{
			if (!_isProgrammicallyChangingNumerics)
			{
				TargetPanel.Refresh();
			}
		}

		private void TargetPanel_Paint(object sender, PaintEventArgs e)
		{
			e.Graphics.DrawEllipse(
				ReadOnly ? GrayPen : _isSet ? RedPen : BlackPen,
				X - 10,
				Y - 10,
				21,
				21);

			e.Graphics.DrawLine(
				ReadOnly ? GrayPen : BlackPen,
				new Point(X, Y - 10),
				new Point(X, Y + 10));

			e.Graphics.DrawLine(
				ReadOnly ? GrayPen : BlackPen,
				new Point(X - 10, Y),
				new Point(X + 10, Y));

			e.Graphics.FillEllipse(
				ReadOnly ? Brushes.Gray : Brushes.Red,
				new Rectangle(X - 2, Y - 2, 4, 4));
		}

		private void XNumeric_ValueChanged(object sender, EventArgs e)
		{
			UpdatePanelFromNumeric();
		}

		private void XNumeric_KeyUp(object sender, KeyEventArgs e)
		{
			UpdatePanelFromNumeric();
		}

		private void YNumeric_ValueChanged(object sender, EventArgs e)
		{
			UpdatePanelFromNumeric();
		}

		private void YNumeric_KeyUp(object sender, KeyEventArgs e)
		{
			UpdatePanelFromNumeric();
		}

		private void TargetPanel_MouseDown(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Left && !ReadOnly)
			{
				_isDragging = true;
				X = e.X;
				Y = e.Y;
				TargetPanel.Refresh();
			}
		}

		private void TargetPanel_MouseMove(object sender, MouseEventArgs e)
		{
			if (_isDragging && !ReadOnly)
			{
				_isProgrammicallyChangingNumerics = true;
				X = e.X;
				Y = e.Y;
				_isProgrammicallyChangingNumerics = false;
				TargetPanel.Refresh();
			}
		}

		private void TargetPanel_MouseUp(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Left)
			{
				_isDragging = false;
				TargetPanel.Refresh();
			}
		}
	}
}
