using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using ImGuiNET;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.Gps;
using skyscraper5.Skyscraper.Scraper.Storage;
using static SDL2Demo.Forms.ConfigureDataStorageWindow;

namespace SDL2Demo.Forms
{
	internal class ConfigureGpsWindow : IRenderable
	{
		public Ini Ini { get; }
		public bool IsOpen;
		public bool RequireRestart { get; private set; }

		private int selectedGpsReceiver;
		private List<Setting> settings;
		private ReadOnlyDictionary<int, IGpsReceiverFactory> gpsReceivers;
		private bool HasChanges;

		public ConfigureGpsWindow(Ini ini)
		{
			Ini = ini;
			IsOpen = true;
			selectedGpsReceiver = ini.ReadValue("startup", "gps", 0);
			settings = new List<Setting>();
			gpsReceivers = GpsManager.GetGpsTunerFactories();

			foreach (KeyValuePair<int, IGpsReceiverFactory> keyValuePair in gpsReceivers)
			{
				PropertyInfo[] propertyInfos = keyValuePair.Value.GetType().GetProperties();
				foreach (PropertyInfo propertyInfo in propertyInfos)
				{
					Setting s = new Setting();
					s.IniKey = keyValuePair.Key;
					s.SettingName = propertyInfo.Name;
					s.SettingControlUuid = Guid.NewGuid().ToString();
					switch (propertyInfo.PropertyType.Name)
					{
						case nameof(String):
							s.SettingMode = 1;
							s.StringValue = ini.ReadValue(String.Format("gps{0}", s.IniKey), s.SettingName, "");
							break;
						case nameof(Boolean):
							s.SettingMode = 2;
							s.BooleanValue = ini.ReadValue(String.Format("gps{0}", s.IniKey), s.SettingName, false);
							break;
						case nameof(UInt16):
							s.SettingMode = 3;
							s.UshortValue = (ushort)ini.ReadValue(String.Format("gps{0}", s.IniKey), s.SettingName, ushort.MinValue);
							break;
						case nameof(Single):
							s.SettingMode = 4;
							s.FloatValue = (float)ini.ReadValue(String.Format("gps{0}", s.IniKey), s.SettingName, 0.0f);
							break;
						case nameof(Int32):
							s.SettingMode = 5;
							s.IntegerValue = (int)ini.ReadValue(String.Format("gps{0}", s.IniKey), s.SettingName, 0);
							break;
						default:
							throw new NotImplementedException(propertyInfo.PropertyType.Name);
					}

					settings.Add(s);
				}
			}

		}

		private void Save()
		{
			Ini.WriteValue("startup", "gps", selectedGpsReceiver);
			foreach (Setting setting in settings)
			{
				switch (setting.SettingMode)
				{
					case 1:
						Ini.WriteValue(String.Format("gps{0}", setting.IniKey), setting.SettingName, setting.StringValue);
						break;
					case 2:
						Ini.WriteValue(String.Format("gps{0}", setting.IniKey), setting.SettingName, setting.BooleanValue);
						break;
					case 3:
						Ini.WriteValue(String.Format("gps{0}", setting.IniKey), setting.SettingName, setting.UshortValue);
						break;
					case 4:
						Ini.WriteValue(String.Format("gps{0}", setting.IniKey), setting.SettingName, setting.FloatValue);
						break;
					case 5:
						Ini.WriteValue(String.Format("gps{0}", setting.IniKey), setting.SettingName, setting.IntegerValue);
						break;
					default:
						throw new NotImplementedException(String.Format("Setting mode {0}", setting.SettingMode));
				}
			}

			Ini.Export(new FileInfo("skyscraper5.ini"));

			if (HasChanges)
				RequireRestart = true;
			HasChanges = false;

		}

		private Dictionary<long, string> keyUuids;
		public void Render()
		{
			float itemWidth = 100.0f;
			ImGuiWindowFlags flags = ImGuiWindowFlags.None;
			if (HasChanges)
				flags |= ImGuiWindowFlags.UnsavedDocument;

			ImGui.Begin("Configure GPS Receiver", ref IsOpen, flags);

			foreach (KeyValuePair<int, IGpsReceiverFactory> keyValuePair in gpsReceivers)
			{
				if (ImGui.RadioButton(keyValuePair.Value.GetType().Name, ref selectedGpsReceiver, keyValuePair.Key))
				{
					HasChanges = true;
				}

				if (keyUuids == null)
					keyUuids = new Dictionary<long, string>();
				if (!keyUuids.ContainsKey(keyValuePair.Key))
					keyUuids.Add(keyValuePair.Key, Guid.NewGuid().ToString());

				ImGui.BeginTable(keyUuids[keyValuePair.Key], 2);

				foreach (Setting setting in settings.Where(x => x.IniKey == keyValuePair.Key))
				{
					ImGui.TableNextRow();
					ImGui.TableSetColumnIndex(0);
					ImGui.Text(setting.SettingName);
					ImGui.TableSetColumnIndex(1);
					ImGui.PushID(setting.SettingControlUuid);
					switch (setting.SettingMode)
					{
						case 1:
							ImGui.SetNextItemWidth(itemWidth);
							if (ImGui.InputText("", ref setting.StringValue, 255))
								HasChanges = true;
							break;
						case 2:
							if (ImGui.Checkbox("", ref setting.BooleanValue))
								HasChanges = true;
							break;
						case 3:
							if (setting.UshortValue < ushort.MinValue)
								setting.UshortValue = ushort.MinValue;
							if (setting.UshortValue > ushort.MaxValue)
								setting.UshortValue = ushort.MaxValue;
							ImGui.SetNextItemWidth(itemWidth);
							if (ImGui.InputInt("", ref setting.UshortValue))
								HasChanges = true;
							break;
						case 4:
							ImGui.SetNextItemWidth(itemWidth);
							if (ImGui.InputFloat("", ref setting.FloatValue, 0.01f, 0.1f))
								HasChanges = true;
							break;
						case 5:
							ImGui.SetNextItemWidth(itemWidth);
							if (ImGui.InputInt("", ref setting.IntegerValue, 1, 10))
								HasChanges = true;
							break;
						default:
							throw new NotImplementedException(String.Format("Setting mode {0}", setting.SettingMode));
					}
				}
				ImGui.PopID();
				ImGui.EndTable();
			}

			bool canApply = CanApply();
			ImGui.BeginDisabled(!canApply);
			if (ImGui.Button("OK"))
			{
				Save();
				IsOpen = false;
			}
			ImGui.EndDisabled();

			ImGui.SameLine();
			if (ImGui.Button("Cancel"))
			{
				IsOpen = false;
			}

			ImGui.SameLine();
			ImGui.BeginDisabled(!canApply);
			if (ImGui.Button("Apply"))
			{
				Save();
			}
			ImGui.EndDisabled();
			ImGui.End();
		}

		private bool CanApply()
		{
			foreach (Setting setting in settings.Where(x => x.IniKey == selectedGpsReceiver))
			{
				if (!setting.Validate())
				{
					return false;
				}
			}

			return true;
		}
	}
}
