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

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

namespace BizHawk.Client.EmuHawk
{
	public partial class ControllerConfig : Form
	{
		static readonly Dictionary<string, Bitmap> ControllerImages = new Dictionary<string, Bitmap>();
		static ControllerConfig()
		{
			ControllerImages.Add("NES Controller", Properties.Resources.NES_Controller);
			ControllerImages.Add("SNES Controller", Properties.Resources.SNES_Controller);
			ControllerImages.Add("Nintento 64 Controller", Properties.Resources.N64);
			ControllerImages.Add("Gameboy Controller", Properties.Resources.GBController);
			ControllerImages.Add("GBA Controller", Properties.Resources.GBA_Controller);
			ControllerImages.Add("Dual Gameboy Controller", Properties.Resources.GBController);

			ControllerImages.Add("SMS Controller", Properties.Resources.SMSController);
			ControllerImages.Add("Genesis 3-Button Controller", Properties.Resources.GENController);
			ControllerImages.Add("GPGX Genesis Controller", Properties.Resources.GENController);
			ControllerImages.Add("Saturn Controller", Properties.Resources.SaturnController);

			ControllerImages.Add("Intellivision Controller", Properties.Resources.IntVController);
			ControllerImages.Add("ColecoVision Basic Controller", Properties.Resources.colecovisioncontroller);
			ControllerImages.Add("Atari 2600 Basic Controller", Properties.Resources.atari_controller);
			ControllerImages.Add("Atari 7800 ProLine Joystick Controller", Properties.Resources.A78Joystick);

			ControllerImages.Add("PC Engine Controller", Properties.Resources.PCEngineController);
			ControllerImages.Add("Commodore 64 Controller", Properties.Resources.C64Joystick);
			ControllerImages.Add("TI83 Controller", Properties.Resources.TI83_Controller);

			//ControllerImages.Add("PSP Controller", Properties.Resources); //TODO
		}

		const int MAXPLAYERS = 8;

		private ControllerConfig()
		{
			InitializeComponent();
		}

		delegate Control PanelCreator<T>(Dictionary<string, T> settings, List<string> buttons, Size size);

		private Control CreateNormalPanel(Dictionary<string, string> settings, List<string> buttons, Size size)
		{
			var cp = new ControllerConfigPanel { Dock = DockStyle.Fill };
			cp.LoadSettings(settings, checkBoxAutoTab.Checked, buttons, size.Width, size.Height);
			return cp;
		}

		private static Control CreateAnalogPanel(Dictionary<string, Config.AnalogBind> settings, List<string> buttons, Size size)
		{
			return new AnalogBindPanel(settings, buttons) { Dock = DockStyle.Fill };
		}

		static void LoadToPanel<T>(Control dest, string controllerName, IEnumerable<string> controllerButtons, IDictionary<string, Dictionary<string, T>> settingsblock, T defaultvalue, PanelCreator<T> createpanel)
		{
			Dictionary<string, T> settings;
			if (!settingsblock.TryGetValue(controllerName, out settings))
			{
				settings = new Dictionary<string, T>();
				settingsblock[controllerName] = settings;
			}
			// check to make sure that the settings object has all of the appropriate boolbuttons
			foreach (var button in controllerButtons)
			{
				if (!settings.Keys.Contains(button))
					settings[button] = defaultvalue;
			}

			if (settings.Keys.Count == 0)
			{
				return;
			}

			// split the list of all settings into buckets by player number
			var buckets = new List<string>[MAXPLAYERS + 1];
			for (int i = 0; i < buckets.Length; i++)
			{
				buckets[i] = new List<string>();
			}

			foreach (var button in settings.Keys)
			{
				int i;
				for (i = 1; i <= MAXPLAYERS; i++)
				{
					if (button.StartsWith("P" + i))
						break;
				}
				if (i > MAXPLAYERS) // couldn't find
					i = 0;
				buckets[i].Add(button);
			}

			if (buckets[0].Count == settings.Keys.Count)
			{
				// everything went into bucket 0, so make no tabs at all
				dest.Controls.Add(createpanel(settings, null, dest.Size));
			}
			else
			{
				// create multiple player tabs
				var tt = new TabControl { Dock = DockStyle.Fill };
				dest.Controls.Add(tt);
				int pageidx = 0;
				for (int i = 1; i <= MAXPLAYERS; i++)
				{
					if (buckets[i].Count > 0)
					{
						tt.TabPages.Add("Player " + i);
						tt.TabPages[pageidx].Controls.Add(createpanel(settings, buckets[i], tt.Size));
						pageidx++;
					}
				}
				if (buckets[0].Count > 0)
				{
					tt.TabPages.Add(Global.Emulator.SystemId == "C64" ? "Keyboard" : "Console");
					tt.TabPages[pageidx].Controls.Add(createpanel(settings, buckets[0], tt.Size));
				}
			}
		}

		private readonly ControllerDefinition _theDefinition;

		public ControllerConfig(ControllerDefinition def)
			: this()
		{
			_theDefinition = def;
			SuspendLayout();
			LoadPanels(Global.Config);

			checkBoxUDLR.Checked = Global.Config.AllowUD_LR;
			checkBoxAutoTab.Checked = Global.Config.InputConfigAutoTab;

			SetControllerPicture(def.Name);

			if (!VersionInfo.INTERIM)
			{
				buttonSaveDefaults.Hide();
			}

			ResumeLayout();
		}

		private void LoadPanels(IDictionary<string, Dictionary<string, string>> normal,
			IDictionary<string, Dictionary<string, string>> autofire,
			IDictionary<string, Dictionary<string, Config.AnalogBind>> analog)
		{
			LoadToPanel(tabPage1, _theDefinition.Name, _theDefinition.BoolButtons, normal, "", CreateNormalPanel);
			LoadToPanel(tabPage2, _theDefinition.Name, _theDefinition.BoolButtons, autofire, "", CreateNormalPanel);
			LoadToPanel(tabPage3, _theDefinition.Name, _theDefinition.FloatControls, analog, new Config.AnalogBind("", 1.0f, 0.1f), CreateAnalogPanel);

			if (tabPage3.Controls.Count == 0)
			{
				tabControl1.TabPages.Remove(tabPage3);
			}
		}

		private void LoadPanels(ControlDefaults cd)
		{
			LoadPanels(cd.AllTrollers, cd.AllTrollersAutoFire, cd.AllTrollersAnalog);
		}

		private void LoadPanels(Config c)
		{
			LoadPanels(c.AllTrollers, c.AllTrollersAutoFire, c.AllTrollersAnalog);
		}

		void SetControllerPicture(string controlName)
		{
			Bitmap bmp;
			if (!ControllerImages.TryGetValue(controlName, out bmp))
			{
				bmp = Properties.Resources.Help;
			}

			pictureBox1.Image = bmp;
			pictureBox1.Size = bmp.Size;
			tableLayoutPanel1.ColumnStyles[1].Width = bmp.Width;

			//Uberhack
			if (controlName == "Commodore 64 Controller")
			{
				var pictureBox2 = new PictureBox
					{
						Image = Properties.Resources.C64Keyboard,
						Size = Properties.Resources.C64Keyboard.Size
					};
				tableLayoutPanel1.ColumnStyles[1].Width = Properties.Resources.C64Keyboard.Width;
				pictureBox1.Height /= 2;
				pictureBox1.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
				pictureBox1.Dock = DockStyle.Top;
				pictureBox2.Location = new Point(pictureBox1.Location.X, pictureBox1.Location.Y + pictureBox1.Size.Height + 10);
				tableLayoutPanel1.Controls.Add(pictureBox2, 1, 0);


				pictureBox2.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
			}
		}

		// lazy methods, but they're not called often and actually
		// tracking all of the ControllerConfigPanels wouldn't be simpler
		static void SetAutoTab(Control c, bool value)
		{
			if (c is ControllerConfigPanel)
			{
				(c as ControllerConfigPanel).SetAutoTab(value);
			}
			else if (c is AnalogBindPanel)
			{
				// TODO
			}
			else if (c.HasChildren)
			{
				foreach (Control cc in c.Controls)
				{
					SetAutoTab(cc, value);
				}
			}
		}

		void Save()
		{
			ActOnControlCollection<ControllerConfigPanel>(tabPage1, c => c.Save(Global.Config.AllTrollers[_theDefinition.Name]));
			ActOnControlCollection<ControllerConfigPanel>(tabPage2, c => c.Save(Global.Config.AllTrollersAutoFire[_theDefinition.Name]));
			ActOnControlCollection<AnalogBindPanel>(tabPage3, c => c.Save(Global.Config.AllTrollersAnalog[_theDefinition.Name]));
		}
		void SaveToDefaults(ControlDefaults cd)
		{
			ActOnControlCollection<ControllerConfigPanel>(tabPage1, c => c.Save(cd.AllTrollers[_theDefinition.Name]));
			ActOnControlCollection<ControllerConfigPanel>(tabPage2, c => c.Save(cd.AllTrollersAutoFire[_theDefinition.Name]));
			ActOnControlCollection<AnalogBindPanel>(tabPage3, c => c.Save(cd.AllTrollersAnalog[_theDefinition.Name]));
		}

		static void ActOnControlCollection<T>(Control c, Action<T> proc)
			where T : Control
		{
			if (c is T)
				proc(c as T);
			else if (c.HasChildren)
				foreach (Control cc in c.Controls)
					ActOnControlCollection(cc, proc);
		}

		private void checkBoxAutoTab_CheckedChanged(object sender, EventArgs e)
		{
			SetAutoTab(this, checkBoxAutoTab.Checked);
		}

		private void checkBoxUDLR_CheckedChanged(object sender, EventArgs e)
		{
		}

		private void buttonOK_Click(object sender, EventArgs e)
		{
			Global.Config.AllowUD_LR = checkBoxUDLR.Checked;
			Global.Config.InputConfigAutoTab = checkBoxAutoTab.Checked;

			Save();

			GlobalWin.OSD.AddMessage("Controller settings saved");
			DialogResult = DialogResult.OK;
			Close();
		}

		private void buttonCancel_Click(object sender, EventArgs e)
		{
			GlobalWin.OSD.AddMessage("Controller config aborted");
			Close();
		}

		private void NewControllerConfig_Load(object sender, EventArgs e)
		{
			Text = _theDefinition.Name + " Configuration";
		}

		private static TabControl GetTabControl(IEnumerable controls)
		{
			if (controls != null)
			{
				return controls
					.OfType<TabControl>()
					.Select(c => c)
					.FirstOrDefault();
			}
			return null;
		}

		private void buttonLoadDefaults_Click(object sender, EventArgs e)
		{
			tabControl1.SuspendLayout();

			var wasTabbedMain = tabControl1.SelectedTab.Name;
			var tb1 = GetTabControl(tabPage1.Controls);
			var tb2 = GetTabControl(tabPage2.Controls);
			var tb3 = GetTabControl(tabPage3.Controls);
			int? wasTabbedPage1 = null;
			int? wasTabbedPage2 = null;
			int? wasTabbedPage3 = null;

			if (tb1 != null && tb1.SelectedTab != null) { wasTabbedPage1 = tb1.SelectedIndex; }
			if (tb2 != null && tb2.SelectedTab != null) { wasTabbedPage2 = tb2.SelectedIndex; }
			if (tb3 != null && tb3.SelectedTab != null) { wasTabbedPage3 = tb3.SelectedIndex; }

			tabPage1.Controls.Clear();
			tabPage2.Controls.Clear();
			tabPage3.Controls.Clear();

			// load panels directly from the default config.
			// this means that the changes are NOT committed.  so "Cancel" works right and you
			// still have to hit OK at the end.
			var cd =  ConfigService.Load<ControlDefaults>(Config.ControlDefaultPath);
			LoadPanels(cd);

			tabControl1.SelectTab(wasTabbedMain);

			if (wasTabbedPage1.HasValue)
			{
				var newTb1 = GetTabControl(tabPage1.Controls);
				if (newTb1 != null)
				{
					newTb1.SelectTab(wasTabbedPage1.Value);
				}
			}

			if (wasTabbedPage2.HasValue)
			{
				var newTb2 = GetTabControl(tabPage2.Controls);
				if (newTb2 != null)
				{
					newTb2.SelectTab(wasTabbedPage2.Value);
				}
			}

			if (wasTabbedPage3.HasValue)
			{
				var newTb3 = GetTabControl(tabPage3.Controls);
				if (newTb3 != null)
				{
					newTb3.SelectTab(wasTabbedPage3.Value);
				}
			}

			tabControl1.ResumeLayout();
		}

		private void buttonSaveDefaults_Click(object sender, EventArgs e)
		{
			var result = MessageBox.Show(this, "OK to overwrite defaults for current control scheme?", "Save Defaults", MessageBoxButtons.YesNo);
			if (result == DialogResult.Yes)
			{
				var cd = ConfigService.Load<ControlDefaults>(Config.ControlDefaultPath);
				cd.AllTrollers[_theDefinition.Name] = new Dictionary<string, string>();
				cd.AllTrollersAutoFire[_theDefinition.Name] = new Dictionary<string, string>();
				cd.AllTrollersAnalog[_theDefinition.Name] = new Dictionary<string, Config.AnalogBind>();

				SaveToDefaults(cd);

				ConfigService.Save(Config.ControlDefaultPath, cd);
			}
		}
	}
}
