using Cryville.Common;
using Cryville.Common.Unity;
using Cryville.Crtr.Ruleset;
using Cryville.Input;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace Cryville.Crtr.Config.UI {
	public class InputConfigPanel : MonoBehaviour {
		[SerializeField]
		ConfigPanelMaster m_configScene;

		[SerializeField]
		GameObject m_inputDialog;

		[SerializeField]
		Transform m_deviceList;

		[SerializeField]
		GameObject m_prefabListItem;

		[SerializeField]
		Transform m_entryList;

		[SerializeField]
		GameObject m_prefabInputConfigEntry;

		SimpleInputConsumer _consumer;
		public InputProxy proxy;
		readonly Dictionary<Identifier, InputConfigPanelEntry> _entries = new Dictionary<Identifier, InputConfigPanelEntry>();

		int _applicableEntries;
		Identifier _sel;
		int _targetDim;
		PhysicalDimension? _targetPDim;
		bool _targetNotNull;
		public void OpenDialog(Identifier entry) {
			_sel = entry;
			var def = m_configScene.ruleset.Root.inputs[_sel];
			_targetDim = def.dim;
			if (def.pdim != null) _targetPDim = ParsePhysicalDimension(def.pdim);
			else _targetPDim = null;
			_targetNotNull = def.notnull;
			m_inputDialog.SetActive(true);
			CallHelper.Purge(m_deviceList);
			_consumer.EnumerateEvents(ev => { });
			_recvsrcs.Clear();
			_applicableEntries = 1;
			AddSourceItem(null);
		}

		static PhysicalDimension ParsePhysicalDimension(string str) {
			var comps = str.Split(' ', StringSplitOptions.RemoveEmptyEntries);
			var result = new PhysicalDimension();
			foreach (var comp in comps) {
				int dim = 1;
				if (comp.Length > 1) dim = int.Parse(comp.Substring(1));
				switch (comp[0]) {
					case 'T': result.Time += dim; break;
					case 'L': result.Length += dim; break;
					case 'M': result.Mass += dim; break;
					case 'I': result.ElectricCurrent += dim; break;
					case '\x0398':
					case 'H': result.ThermodynamicTemperature += dim; break;
					case 'N': result.AmountOfSubstance += dim; break;
					case 'J': result.LuminousIntensity += dim; break;
					default: throw new ArgumentException(string.Format("Invalid dimension symbol {0}", comp[0]));
				}
			}
			return result;
		}

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

		public void CloseDialog(InputSource? src) {
			proxy.Set(new InputProxyEntry {
				Target = _sel,
				Source = src,
			});
			CloseDialog();
		}

		public void OnConfigEnable() {
			CallHelper.Purge(m_entryList);
			_entries.Clear();
			_consumer = new SimpleInputConsumer(Game.InputManager);
			_consumer.Activate();
			foreach (var i in m_configScene.ruleset.Root.inputs) {
				var e = GameObject.Instantiate(m_prefabInputConfigEntry, m_entryList.transform).GetComponent<InputConfigPanelEntry>();
				_entries.Add(i.Key, e);
				e.SetKey(this, i.Key);
				OnProxyChanged(this, proxy[i.Key]);
			}
			proxy.ProxyChanged += OnProxyChanged;
		}

		public void OnConfigDisable() {
			_consumer.Deactivate();
		}

		void OnProxyChanged(object sender, ProxyChangedEventArgs e) {
			_entries[e.Name].OnProxyChanged(e);
		}

		void Start() {
			_d_HandleInputEvent = HandleInputEvent;
		}

		readonly List<InputSource?> _recvsrcs = new List<InputSource?>();
		void Update() {
			if (m_inputDialog.activeSelf) {
				_consumer.EnumerateEvents(_d_HandleInputEvent);
			}
		}

		Action<InputEvent> _d_HandleInputEvent;
		void HandleInputEvent(InputEvent ev) {
			AddSourceItem(ev.Identifier.Source);
		}

		void AddSourceItem(InputSource? src) {
			if (_recvsrcs.Contains(src)) return;
			_recvsrcs.Add(src);
			var obj = Instantiate(m_prefabListItem, m_deviceList);
			var text = obj.transform.Find("Text").GetComponent<Text>();
			text.text = src == null ? "(None)" : src.Value.Handler.GetTypeName(src.Value.Type);
			var btn = obj.GetComponent<Button>();
			if (src != null) {
				var tsrc = src.Value;
				bool flag = false;
				if (proxy.IsUsed(tsrc)) {
					text.text += " <size=9>(Used)</size>";
				}
				else if (tsrc.Handler.Dimension < _targetDim) {
					text.text += " <size=9>(Not Applicable)</size>";
				}
				else if (_targetPDim != null && tsrc.Handler.ReferenceCue.PhysicalDimension != _targetPDim) {
					text.text += " <size=9>(Not Applicable)</size>";
				}
				else if (!_targetNotNull && !tsrc.Handler.IsNullable) {
					text.text += " <size=9>(Not Applicable)</size>";
				}
				else flag = true;
				btn.interactable = flag;
				obj.transform.SetSiblingIndex(flag ? _applicableEntries++ : m_deviceList.childCount - 1);
			}
			else {
				obj.transform.SetSiblingIndex(0);
			}
			btn.onClick.AddListener(() => {
				CloseDialog(src);
			});
		}
	}
}
