using Cryville.Common.Unity;
using Cryville.Common.Unity.Input;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEngine.UI;

namespace Cryville.Crtr.Config {
	public class InputConfig : MonoBehaviour {
		[SerializeField]
		GameObject m_inputDialog;

		[SerializeField]
		Transform m_deviceList;

		[SerializeField]
		GameObject m_prefabListItem;

		[SerializeField]
		Transform m_entryList;

		[SerializeField]
		GameObject m_prefabInputConfigEntry;

		InputProxy _proxy;
		Dictionary<string, InputConfigEntry> _entries = new Dictionary<string, InputConfigEntry>();

		string _sel;
		public void OpenDialog(string entry) {
			_sel = entry;
			m_inputDialog.SetActive(true);
			Game.InputManager.Activate();
			CallHelper.Purge(m_deviceList);
			_recvsrcs.Clear();
			AddSourceItem(null);
		}

		public void CloseDialog() {
			m_inputDialog.SetActive(false);
			Game.InputManager.Deactivate();
		}

		public void CloseDialog(InputSource? src) {
			_proxy.Set(new InputProxyEntry {
				Target = _sel,
				Source = src,
			});
			m_inputDialog.SetActive(false);
			Game.InputManager.Deactivate();
		}

		void Start() {
			ChartPlayer.etor = new PdtEvaluator();
			FileInfo file = new FileInfo(
				Game.GameDataPath + "/rulesets/" + Settings.Default.LoadRuleset
			);
			DirectoryInfo dir = file.Directory;
			using (StreamReader reader = new StreamReader(file.FullName, Encoding.UTF8)) {
				var ruleset = JsonConvert.DeserializeObject<Ruleset>(reader.ReadToEnd(), new JsonSerializerSettings() {
					MissingMemberHandling = MissingMemberHandling.Error
				});
				if (ruleset.format != 1) throw new FormatException("Invalid ruleset file version");
				ruleset.LoadPdt(dir);
				_proxy = new InputProxy(ruleset.Root, null);
				foreach (var i in ruleset.Root.inputs) {
					var e = GameObject.Instantiate(m_prefabInputConfigEntry, m_entryList.transform).GetComponent<InputConfigEntry>();
					_entries.Add(i.Key, e);
					e.SetKey(this, i.Key);
				}
				_proxy.ProxyChanged += OnProxyChanged;
			}
		}

		void OnProxyChanged(object sender, ProxyChangedEventArgs e) {
			_entries[e.Name].SetEnabled(!e.Used);
			_entries[e.Name].SetValue(e.Proxy == null ? "None" : e.Proxy.Value.Handler.GetTypeName(e.Proxy.Value.Type));
		}

		readonly List<InputSource?> _recvsrcs = new List<InputSource?>();
		void Update() {
			if (m_inputDialog.activeSelf) {
				Game.InputManager.EnumerateEvents(ev => {
					AddSourceItem(ev.Id.Source);
				});
			}
		}

		void AddSourceItem(InputSource? src) {
			if (_recvsrcs.Contains(src)) return;
			_recvsrcs.Add(src);
			var obj = Instantiate(m_prefabListItem, m_deviceList);
			obj.transform.Find("Text").GetComponent<Text>().text = src == null ? "None" : src.Value.Handler.GetTypeName(src.Value.Type);
			var btn = obj.GetComponent<Button>();
			if (src != null) btn.interactable = !_proxy.IsUsed(src.Value);
			btn.onClick.AddListener(() => {
				CloseDialog(src);
			});
		}
	}
}
