using System.Collections.Generic;
using UnityEngine;
using Yarn.Unity;

namespace AC.Downloads.YarnIntegration
{

	public class ACYarnVariables : VariableStorageBehaviour
	{

		#region Variables

		[SerializeField] private string nodesVisitedStringVariable = "NodesVisited";
		[SerializeField] private string onceLinesStringVariable = "OnceLines";

		#endregion


		#region PublicFunctions

		public void OnNodeComplete(string node)
		{
			SetVisited(node, GetVisited(node) + 1);
		}


		public override void SetValue(string variableName, string stringValue)
		{
			GVar variable = GetVariable(variableName);
			if (variable != null)
			{
				variable.TextValue = stringValue;
			}
		}


		public override void SetValue(string variableName, float floatValue)
		{
			GVar variable = GetVariable(variableName);
			if (variable != null)
			{
				variable.IntegerValue = (int)floatValue;
				variable.FloatValue = floatValue;
			}
		}


		public override void SetValue(string variableName, bool boolValue)
		{
			if (variableName.StartsWith("$Yarn.Internal.Once"))
			{
				string lineID = variableName.Substring("$Yarn.Internal.Once".Length + 1);
				if (boolValue)
				{
					SetOnce(lineID);
				}
				else
				{
					RemoveOnce(lineID);
				}
				return;
			}
			
			GVar variable = GetVariable(variableName);
			if (variable != null)
			{
				variable.BooleanValue = boolValue;
			}
		}


		public override bool TryGetValue<T>(string variableName, out T result)
		{
			if (variableName.StartsWith("$Yarn.Internal.Visiting"))
			{
				string node = variableName.Substring("$Yarn.Internal.Visiting".Length + 1);
				result = (T)(object)(float)GetVisited(node);
				return true;
			}
			
			if (variableName.StartsWith("$Yarn.Internal.Once"))
			{
				string lineID = variableName.Substring("$Yarn.Internal.Once".Length + 1);
				if (GetOnce (lineID))
				{
					result = (T) (object) true;
					return true;
				}
				result = (T) (object) false;
				return false;
			}

			GVar variable = GetVariable(variableName);
			if (variable != null)
			{
				switch (variable.type)
				{
					case VariableType.Boolean:
						result = (T)(object)variable.BooleanValue;
						return true;

					case VariableType.Integer:
					case VariableType.PopUp:
						result = (T)(object)variable.IntegerValue;
						return true;

					case VariableType.Float:
						result = (T)(object)variable.FloatValue;
						return true;

					case VariableType.String:
						result = (T)(object)variable.TextValue;
						return true;

					default:
						break;
				}
			}
			result = default(T);
			return false;
		}


		public override bool Contains(string variableName)
		{
			GVar variable = GetVariable(variableName);
			return variable != null;
		}


		public override void Clear() { }


		public override void SetAllVariables(Dictionary<string, float> floats, Dictionary<string, string> strings, Dictionary<string, bool> bools, bool clear = true)
		{
			foreach (var key in floats.Keys)
			{
				SetValue(key, floats[key]);
			}

			foreach (var key in strings.Keys)
			{
				SetValue(key, strings[key]);
			}

			foreach (var key in bools.Keys)
			{
				SetValue(key, bools[key]);
			}
		}


		public override (Dictionary<string, float> FloatVariables, Dictionary<string, string> StringVariables, Dictionary<string, bool> BoolVariables) GetAllVariables()
		{
			var floats = new Dictionary<string, float>();
			var strings = new Dictionary<string, string>();
			var bools = new Dictionary<string, bool>();

			return (floats, strings, bools);
		}

		#endregion


		#region PrivateFunctions

		private GVar GetVariable(string variableName)
		{
			string varName = variableName;
			if (varName.StartsWith("$"))
			{
				varName = varName.Substring(1);
			}

			if (!varName.StartsWith("l_"))
			{
				GVar variable = GlobalVariables.GetVariable(varName);
				if (variable != null)
				{
					return variable;
				}
			}

			if (!varName.StartsWith("g_"))
			{
				GVar variable = LocalVariables.GetVariable(varName);
				if (variable != null)
				{
					return variable;
				}
			}

			ACDebug.LogWarning("Cannot find AC Variable of the name " + varName, this);
			return null;
		}


		private void SetVisited(string node, int timesVisited)
		{
			var variable = GlobalVariables.GetVariable(nodesVisitedStringVariable);
			if (variable == null || variable.type != VariableType.String) return;

			string allData = variable.TextValue;

			if (string.IsNullOrEmpty(allData))
			{
				variable.TextValue = ";" + node + ":" + timesVisited + ";";
				return;
			}

			string[] chunks = allData.Split(";"[0]);
			string newData = ";";
			foreach (var chunk in chunks)
			{
				if (!string.IsNullOrEmpty(chunk) && !chunk.StartsWith(node + ":"))
				{
					newData += chunk + ";";
				}
			}

			newData += node + ":" + timesVisited;
			variable.TextValue = newData;
		}


		private int GetVisited(string node)
		{
			var variable = GlobalVariables.GetVariable(nodesVisitedStringVariable);
			if (variable == null || variable.type != VariableType.String) return 0;

			string allData = variable.TextValue;
			string[] chunks = allData.Split(";"[0]);
			foreach (var chunk in chunks)
			{
				if (chunk.StartsWith(node + ":"))
				{
					int existingNumberIndex = chunk.IndexOf(":") + 1;
					if (int.TryParse(chunk.Substring(existingNumberIndex), out int existingNumber))
					{
						return existingNumber;
					}
				}
			}

			return 0;
		}
		
		
		private void SetOnce (string lineID)
		{
			var variable = GlobalVariables.GetVariable (onceLinesStringVariable);
			if (variable == null || variable.type != VariableType.String) return;

			string allData = variable.TextValue;
			
			if (string.IsNullOrEmpty (allData))
			{
				variable.TextValue = ";" + lineID + ";";
				return;
			}

			string newData = ";" + lineID;
			if (variable.TextValue.Contains(newData)) return;
			variable.TextValue += newData;
		}
		
		
		private void RemoveOnce (string lineID)
		{
			var variable = GlobalVariables.GetVariable (onceLinesStringVariable);
			if (variable == null || variable.type != VariableType.String) return;

			string allData = variable.TextValue;

			string removeString = ";" + lineID;
			if (allData.Contains(removeString))
			{
				allData = allData.Replace(removeString, string.Empty);
				variable.TextValue = allData;
			}
		}


		private bool GetOnce(string lineID)
		{
			var variable = GlobalVariables.GetVariable(onceLinesStringVariable);
			if (variable == null || variable.type != VariableType.String) return false;

			string allData = variable.TextValue;
			string[] chunks = allData.Split(";"[0]);
			foreach (var chunk in chunks)
			{
				if (chunk == lineID)
				{
					return true;
				}
			}

			return false;
		}

		#endregion

	}
	
}