using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

using BizHawk.Client.Common;

namespace BizHawk.Client.EmuHawk
{
	public partial class VirtualPadForm : Form, IToolForm
	{
		// TODO: UpdateValues doesn't support all cores, and is probably wrong for gens, also done in an unsustainable way
		private List<IVirtualPad> Pads
		{
			get
			{
				return ControllerBox.Controls.OfType<IVirtualPad>().ToList();
			}
		}

		private int _defaultWidth;
		private int _defaultHeight;

		#region Public API

		public bool AskSave() { return true; }
		public bool UpdateBefore { get { return false; } }

		public void ClearVirtualPadHolds()
		{
			ControllerBox.Controls
				.OfType<IVirtualPad>()
				.ToList()
				.ForEach(pad => pad.Clear());
		}

		public void Restart()
		{
			if (!IsHandleCreated || IsDisposed)
			{
				return;
			}

			ControllerBox.Controls.Clear();
			LoadPads();
		}

		public void UpdateValues()
		{
			if (!IsHandleCreated || IsDisposed)
			{
				return;
			}

			if (Global.MovieSession.Movie.IsPlaying && !Global.MovieSession.Movie.IsFinished)
			{
				var str = Global.MovieSession.Movie.GetInput(Global.Emulator.Frame);
				if (Global.Config.VirtualPadsUpdatePads && str != string.Empty)
				{
					switch (Global.Emulator.SystemId)
					{
						case "NES":
							Pads[0].SetButtons(str.Substring(3, 8));
							Pads[1].SetButtons(str.Substring(12, 8));
							Pads[2].SetButtons(str[1].ToString());
							break;
						case "A26":
							Pads[0].SetButtons(str.Substring(4, 5));
							Pads[1].SetButtons(str.Substring(10, 5));
							Pads[2].SetButtons(str.Substring(1, 2));
							break;
						case "SMS":
						case "GG":
						case "SG":
							Pads[0].SetButtons(str.Substring(1, 6));
							Pads[1].SetButtons(str.Substring(8, 6));
							Pads[2].SetButtons(str.Substring(15, 2));
							break;
						case "PCE":
						case "SGX":
							Pads[0].SetButtons(str.Substring(3, 8));
							Pads[1].SetButtons(str.Substring(12, 8));
							Pads[2].SetButtons(str.Substring(21, 8));
							Pads[3].SetButtons(str.Substring(30, 8));
							break;
						case "TI83":
							Pads[0].SetButtons(str.Substring(2, 50));
							break;
						case "SNES":
							Pads[0].SetButtons(str.Substring(3, 12));
							Pads[1].SetButtons(str.Substring(16, 12));
							Pads[2].SetButtons(str.Substring(29, 12));
							Pads[3].SetButtons(str.Substring(42, 12));
							break;
						case "GEN":
							Pads[0].SetButtons(str.Substring(3, 8));
							Pads[1].SetButtons(str.Substring(12, 8));
							break;
						case "GB":
							Pads[0].SetButtons(str.Substring(3, 8));
							break;
						case "Coleco":
							Pads[0].SetButtons(str.Substring(1, 18));
							Pads[1].SetButtons(str.Substring(20, 18));
							break;
						case "C64":
							break;
						case "N64":
							Pads[0].SetButtons(str.Substring(3, 23));
							Pads[1].SetButtons(str.Substring(27, 23));
							Pads[2].SetButtons(str.Substring(51, 23));
							Pads[3].SetButtons(str.Substring(75, 23));
							break;
					}
				}
			}
			else if (!Global.Config.VirtualPadSticky)
			{
				Pads.ForEach(pad => pad.Clear());
			}
		}

		public VirtualPadForm()
		{
			InitializeComponent();
			Closing += (o, e) => SaveConfigSettings();
			TopMost = Global.Config.VirtualPadSettings.TopMost;
		}

		#endregion

		private void VirtualPadForm_Load(object sender, EventArgs e)
		{
			LoadConfigSettings();
			LoadPads();
		}

		private void LoadConfigSettings()
		{
			_defaultWidth = Size.Width;
			_defaultHeight = Size.Height;

			StickyBox.Checked = Global.Config.VirtualPadSticky;

			if (Global.Config.VirtualPadSettings.UseWindowPosition)
			{
				Location = Global.Config.VirtualPadSettings.WindowPosition;
			}

			if (Global.Config.VirtualPadSettings.UseWindowPosition)
			{
				Size = Global.Config.VirtualPadSettings.WindowSize;
			}
		}

		private void SaveConfigSettings()
		{
			Global.Config.VirtualPadSettings.Wndx = Location.X;
			Global.Config.VirtualPadSettings.Wndy = Location.Y;
			Global.Config.VirtualPadSettings.Width = Right - Left;
			Global.Config.VirtualPadSettings.Height = Bottom - Top;
		}

		private void LoadPads()
		{
			switch (Global.Emulator.SystemId)
			{
				case "A26":
					ControllerBox.Controls.Add(new VirtualPadA26 { Location = new Point(8, 19), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadA26 { Location = new Point(188, 19), Controller = "P2" });
					ControllerBox.Controls.Add(new VirtualPadA26Control { Location = new Point(8, 109) });
					break;
				case "A78":
					ControllerBox.Controls.Add(new VirtualPadA78 { Location = new Point(8, 19), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadA78 { Location = new Point(150, 19), Controller = "P2" });
					ControllerBox.Controls.Add(new VirtualPadA78Control { Location = new Point(8, 125) });
					break;
				case "NES":
					ControllerBox.Controls.Add(new VirtualPadNES { Location = new Point(8, 19), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadNES { Location = new Point(188, 19), Controller = "P2" });
					ControllerBox.Controls.Add(new VirtualPadNESControl { Location = new Point(8, 109) });
					break;
				case "N64":
					ControllerBox.Controls.Add(new VirtualPadN64 { Location = new Point(8, 19), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadN64 { Location = new Point(208, 19), Controller = "P2" });
					ControllerBox.Controls.Add(new VirtualPadN64 { Location = new Point(408, 19), Controller = "P3" });
					ControllerBox.Controls.Add(new VirtualPadN64 { Location = new Point(608, 19), Controller = "P4" });
					ControllerBox.Controls.Add(new VirtualPadN64Control { Location = new Point(8, 350) });
					break;
				case "SMS":
				case "SG":
				case "GG":
					ControllerBox.Controls.Add(new VirtualPadSMS { Location = new Point(8, 19), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadSMS { Location = new Point(188, 19), Controller = "P2" });
					ControllerBox.Controls.Add(new VirtualPadSMSControl { Location = new Point(8, 109) });
					break;
				case "PCE":
				case "PCECD":
				case "SGX":
					ControllerBox.Controls.Add(new VirtualPadPCE { Location = new Point(8, 19), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadPCE { Location = new Point(188, 19), Controller = "P2" });
					ControllerBox.Controls.Add(new VirtualPadPCE { Location = new Point(8, 109), Controller = "P3" });
					ControllerBox.Controls.Add(new VirtualPadPCE { Location = new Point(188, 109), Controller = "P4" });
					break;
				case "SNES":
					ControllerBox.Controls.Add(new VirtualPadSNES { Location = new Point(8, 19), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadSNES { Location = new Point(188, 19), Controller = "P2" });
					ControllerBox.Controls.Add(new VirtualPadSNES { Location = new Point(8, 95), Controller = "P3" });
					ControllerBox.Controls.Add(new VirtualPadSNES { Location = new Point(188, 95), Controller = "P4" });
					ControllerBox.Controls.Add(new VirtualPadSNESControl { Location = new Point(8, 170) });
					break;
				case "GB":
				case "GBC":
					ControllerBox.Controls.Add(new VirtualPadGB { Location = new Point(8, 19), Controller = string.Empty });
					ControllerBox.Controls.Add(new VirtualPadGBControl { Location = new Point(8, 109) });
					break;
				case "GBA":
					ControllerBox.Controls.Add(new VirtualPadGBA { Location = new Point(8, 19), Controller = string.Empty });
					break;
				case "GEN":
					ControllerBox.Controls.Add(new VirtualPadGen6Button { Location = new Point(8, 19), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadGen6Button { Location = new Point(195, 19), Controller = "P2" });
					ControllerBox.Controls.Add(new VirtualPadNESControl { Location = new Point(8, 105) });
					break;
				case "Coleco":
					var coleco1 = new VirtualPadColeco { Location = new Point(8, 19), Controller = "P1" };
					var coleco2 = new VirtualPadColeco { Location = new Point(130, 19), Controller = "P2" };
					ControllerBox.Controls.Add(coleco1);
					ControllerBox.Controls.Add(coleco2);
					break;
				case "C64":
					ControllerBox.Controls.Add(new VirtualPadC64Keyboard { Location = new Point(8, 19) });
					ControllerBox.Controls.Add(new VirtualPadA26 { Location = new Point(8, 159), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadA26 { Location = new Point(218, 159), Controller = "P2" });
					break;
				case "SAT":
					ControllerBox.Controls.Add(new VirtualPadSaturn { Location = new Point(8, 19), Controller = "P1" });
					ControllerBox.Controls.Add(new VirtualPadSaturn { Location = new Point(213, 19), Controller = "P2" });
					ControllerBox.Controls.Add(new VirtualPadSaturnControl { Location = new Point(8, 125) });
					break;
			}

			// Hack for now
			if (Global.Emulator.SystemId == "C64")
			{
				if (Width < 505)
				{
					Width = 505;
					ControllerBox.Width = Width - 37;
				}
			}
		}

		public void BumpAnalogValue(int? dx, int? dy) // TODO: multi-player
		{
			// TODO: make an analog flag in virtualpads that have it, and check the virtualpads loaded, instead of doing this hardcoded
			if (Pads[0] is VirtualPadN64)
			{
				(Pads[0] as VirtualPadN64).FudgeAnalog(dx, dy);

				UpdateValues();
			}
		}

		private void RefreshFloatingWindowControl()
		{
			Owner = Global.Config.VirtualPadSettings.FloatingWindow ? null : GlobalWin.MainForm;
		}

		#region Events

		#region Menu

		private void OptionsSubMenu_DropDownOpened(object sender, EventArgs e)
		{
			AutoloadMenuItem.Checked = Global.Config.AutoloadVirtualPad;
			SaveWindowPositionMenuItem.Checked = Global.Config.VirtualPadSettings.SaveWindowPosition;
			AlwaysOnTopMenuItem.Checked = Global.Config.VirtualPadSettings.TopMost;
			FloatingWindowMenuItem.Checked = Global.Config.VirtualPadSettings.FloatingWindow;
		}

		private void AutoloadMenuItem_Click(object sender, EventArgs e)
		{
			Global.Config.AutoloadVirtualPad ^= true;
		}

		private void SaveWindowPositionMenuItem_Click(object sender, EventArgs e)
		{
			Global.Config.VirtualPadSettings.SaveWindowPosition ^= true;
		}

		private void AlwaysOnTopMenuItem_Click(object sender, EventArgs e)
		{
			Global.Config.VirtualPadSettings.TopMost ^= true;
			TopMost = Global.Config.VirtualPadSettings.TopMost;
		}

		private void FloatingWindowMenuItem_Click(object sender, EventArgs e)
		{
			Global.Config.VirtualPadSettings.FloatingWindow ^= true;
			RefreshFloatingWindowControl();
		}

		private void RestoreDefaultSettingsMenuItem_Click(object sender, EventArgs e)
		{
			Size = new Size(_defaultWidth, _defaultHeight);

			Global.Config.VirtualPadSettings.SaveWindowPosition = true;
			Global.Config.VirtualPadSettings.TopMost = TopMost = false;
			Global.Config.VirtualPadSettings.FloatingWindow = false;
		}

		private void ExitMenuItem_Click(object sender, EventArgs e)
		{
			Close();
		}

		#endregion

		#region Dialog, Controls, Context Menu

		protected override void OnClosed(EventArgs e)
		{
			Global.StickyXORAdapter.ClearStickies();
			Global.StickyXORAdapter.ClearStickyFloats();
		}

		private void ClearMenuItem_Click(object sender, EventArgs e)
		{
			ClearVirtualPadHolds();
		}

		private void StickyBox_CheckedChanged(object sender, EventArgs e)
		{
			Global.Config.VirtualPadSticky = StickyBox.Checked;
		}

		protected override void OnShown(EventArgs e)
		{
			RefreshFloatingWindowControl();
			base.OnShown(e);
		}

		#endregion

		#endregion
	}
}
