﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace AC.Downloads.TextAdventure
{

	public class TextParser : MonoBehaviour, ITranslatable
	{

		#region Variables

		[SerializeField] private string menuName = "TextParser";
		[SerializeField] private string inputName = "Input";
		[SerializeField] private string submitButtonName = "Submit";
		[SerializeField] private bool autoTurnOnOff = true;
		public ActionListAsset actionListOnMisunderstand = null;
		public ActionListAsset actionListOnTooFar = null;
		[SerializeField] private List<Command> commands = new List<Command> ();
		[SerializeField] private List<Synonym> synonyms = new List<Synonym> ();
		private Menu menu;
		private MenuInput inputBox;
		private bool turnedOffThisFrame;

		#endregion


		#region UnityStandards

		private void OnEnable ()
		{
			EventManager.OnMenuElementClick += OnMenuElementClick;
			EventManager.OnExitGameState += OnExitGameState;
			EventManager.OnMenuTurnOn += OnMenuTurnOn;
		}


		private void OnDisable ()
		{
			EventManager.OnMenuElementClick -= OnMenuElementClick;
			EventManager.OnExitGameState -= OnExitGameState;
			EventManager.OnMenuTurnOn -= OnMenuTurnOn;
		}


		private void Start ()
		{
			menu = PlayerMenus.GetMenuWithName (menuName);
			inputBox = menu.GetElementWithName (inputName) as MenuInput;
		}


		private void Update ()
		{
			if (turnedOffThisFrame)
			{
				turnedOffThisFrame = false;
				return;
			}

			if (KickStarter.stateHandler.IsInGameplay ())
			{
				if (Input.anyKeyDown && !menu.IsOn () && autoTurnOnOff)
				{
					menu.TurnOn ();
					inputBox.OverrideLabel (Input.inputString);
					menu.Select (inputName, 0);
				}
			}
		}

		#endregion


		#region PublicFunctions

		public void OnSubmit ()
		{
			if (autoTurnOnOff)
			{
				turnedOffThisFrame = true;
				menu.TurnOff ();
			}

			string text = inputBox.GetContents ();
			if (string.IsNullOrEmpty (text)) return;

			for (int s = 0; s < synonyms.Count; s++)
			{
				text = synonyms[s].Process (text);
			}

			ParseText (text);
		}

		
		public void AddCommand (string name, ActionListAsset actionList)
		{
			commands.Add (new Command ()
			{
				name = name,
				lineID = -1,
				actionListAsset = actionList
			});
		}

		#endregion


		#region CustomEvents

		private void OnMenuElementClick (Menu menu, MenuElement element, int slot, int buttonPressed)
		{
			if (menu.title == menuName && element.title == submitButtonName)
			{
				OnSubmit ();
			}
		}


		private void OnExitGameState (GameState gameState)
		{
			if (autoTurnOnOff)
			{
				turnedOffThisFrame = true;
				PlayerMenus.GetMenuWithName (menuName).TurnOff ();
			}
		}


		private void OnMenuTurnOn (Menu menu, bool isInstant)
		{
			if (menu.title == menuName)
			{
				inputBox.OverrideLabel (string.Empty);
			}
		}

		#endregion


		#region ProtectedFunctions

		protected virtual bool ParseText (string text)
		{
			if (string.IsNullOrEmpty (text)) return false;

			text = text.ToLower ();
			int languageIndex = Options.GetLanguage ();

			// Commands
			foreach (var command in commands)
			{
				string commandName = KickStarter.runtimeLanguages.GetTranslation (command.name, command.lineID, languageIndex).ToLower ();
				if (text == commandName && command.actionListAsset)
				{
					command.actionListAsset.Interact ();
					return true;
				}
			}

			// Detect verb
			int verbID = -1;
			int verbIndex = -1;
			foreach (var icon in KickStarter.cursorManager.cursorIcons)
			{
				string iconLabel = icon.GetLabel (Options.GetLanguage ());
				int _verbIndex = text.IndexOf (iconLabel.ToLower ());

				if (_verbIndex >= 0)
				{
					verbIndex = _verbIndex;
					verbID = icon.id;
					break;
				}
			}
			
			// Detect inventory item
			string useItemPrefix = KickStarter.runtimeLanguages.GetTranslation (KickStarter.cursorManager.hotspotPrefix1.label, KickStarter.cursorManager.hotspotPrefix1.lineID, languageIndex).ToLower ();
			string giveItemPrefix = KickStarter.runtimeLanguages.GetTranslation (KickStarter.cursorManager.hotspotPrefix3.label, KickStarter.cursorManager.hotspotPrefix3.lineID, languageIndex).ToLower ();

			InvInstance invInstance = null;
			foreach (InvInstance _invInstance in KickStarter.runtimeInventory.PlayerInvCollection.InvInstances)
			{
				if (text.IndexOf (_invInstance.ItemLabel) > 0)
				{
					invInstance = _invInstance;
					break;
				}
			}

			// Detect Hotspot
			Hotspot hotspot = null;
			foreach (Hotspot _hotspot in KickStarter.stateHandler.Hotspots)
			{
				string hotspotLabel = _hotspot.GetName (Options.GetLanguage ());
				int hotspotIndex = text.IndexOf (hotspotLabel.ToLower ());

				if (hotspotIndex > 0 && HotspotIsValid (_hotspot))
				{
					if (HotspotIsCloseEnough (_hotspot))
					{
						hotspot = _hotspot;
						break;
					}
					else
					{
						if (actionListOnTooFar != null)
						{
							actionListOnTooFar.Interact ();
						}
						return true;
					}
				}
			}

			// Item / Hotspot combine
			if (InvInstance.IsValid (invInstance) && hotspot && (text.IndexOf (useItemPrefix) >= 0 || text.IndexOf (giveItemPrefix) >= 0))
			{
				var interaction = hotspot.GetInvButton (invInstance.ItemID);
				if (interaction != null && !interaction.isDisabled)
				{
					hotspot.RunInventoryInteraction (invInstance.ItemID);
					return true;
				}
			}

			// Item interaction
			if (InvInstance.IsValid (invInstance) && verbID >= 0)
			{
				foreach (var interaction in invInstance.Interactions)
				{
					if (interaction.icon.id == verbID && interaction.actionList)
					{
						interaction.actionList.Interact ();
						return true;
					}
				}
			}
			
			// Hotspot interaction
			if (hotspot && verbID >= 0)
			{
				var interaction = hotspot.GetUseButton (verbID);
				if (interaction != null && !interaction.isDisabled)
				{
					hotspot.RunUseInteraction (verbID);
					return true;
				}
			}

			// Unhandled
			if (actionListOnMisunderstand)
			{
				actionListOnMisunderstand.Interact ();
			}
			return false;
		}

		#endregion


		#region PrivateFunctions

		private bool HotspotIsValid (Hotspot hotspot)
		{
			if (hotspot == null || !hotspot.IsOn ())
			{
				return false;
			}
			return true;
		}


		private bool HotspotIsCloseEnough (Hotspot hotspot)
		{
			if (!hotspot.PlayerIsWithinBoundary ())
			{
				return false;
			}

			if (KickStarter.player && KickStarter.player.hotspotDetector && KickStarter.settingsManager.hotspotDetection == HotspotDetection.PlayerVicinity)
			{
				return KickStarter.player.hotspotDetector.IsHotspotInTrigger (hotspot);
			}

			return true;
		}

		#endregion


		#region ITranslatable

		public string GetTranslatableString (int index) => commands[index].name;

		public int GetTranslationID (int index) => commands[index].lineID;

		#if UNITY_EDITOR

		public void UpdateTranslatableString (int index, string updatedText)
		{
			commands[index].name = updatedText;
		}

		public int GetNumTranslatables () => commands.Count;

		public bool CanTranslate (int index) => !string.IsNullOrEmpty (commands[index].name);

		public bool HasExistingTranslation (int index) => commands[index].lineID >= 0;

		public void SetTranslationID (int index, int lineID)
		{
			commands[index].lineID = lineID;
		}

		public string GetOwner (int index) => null;

		public bool OwnerIsPlayer (int index) => false;

		public AC_TextType GetTranslationType (int index) => AC_TextType.Custom;

		#endif

		#endregion


		#region PrivateClasses

		[Serializable]
		private class Command
		{

			public string name;
			public int lineID;
			public ActionListAsset actionListAsset;

		}


		[Serializable]
		private class Synonym
		{

			public string original;
			public string[] synonyms;


			public string Process (string text)
			{
				text = text.ToLower ();

				foreach (string synonym in synonyms)
				{
					if (text.Contains (synonym.ToLower ()))
					{
						return text.Replace (synonym.ToLower (), original);
					}
				}
				return text;
			}

		}

		#endregion

	}

}