using System;
using System.Linq;
using UnityEngine;

namespace MoreMountains.Tools
{
	/// <summary>
	/// A base class implementing the IMMPersistent interface, designed to be extended
	/// This mostly takes care of the GUID generation and validation
	/// </summary>
	[AddComponentMenu("")]
	public class MMPersistentBase : MonoBehaviour, IMMPersistent
	{
		[Header("Save")] 
		/// whether or not this object should be saved
		[Tooltip("whether or not this object should be saved")]
		public bool SaveActive = true;

		[Header("ID")]
		/// an optional suffix to add to the GUID, to make it more readable
		[Tooltip("an optional suffix to add to the GUID, to make it more readable")]
		public string UniqueIDSuffix;
		/// the object's unique ID
		[Tooltip("the object's unique ID")]
		[SerializeField]
		[MMReadOnly]
		protected string _guid;
		
		/// a debug button used to force a new GUI generation
		[MMInspectorButton("GenerateGuid")]
		public bool GenerateGuidButton;
		
		/// <summary>
		/// On validate, we make sure the object gets a valid GUID
		/// </summary>
		protected virtual void OnValidate()
		{
			ValidateGuid();
		}

		/// <summary>
		/// Returns the object's GUID
		/// </summary>
		/// <returns></returns>
		public virtual string GetGuid() => _guid;
		
		/// <summary>
		/// Lets you set the object's GUID
		/// </summary>
		/// <param name="newGUID"></param>
		public virtual void SetGuid(string newGUID) => _guid = newGUID;  

		/// <summary>
		/// On save, does nothing, meant to be extended
		/// </summary>
		/// <returns></returns>
		public virtual string OnSave()
		{
			return string.Empty;
		}

		/// <summary>
		/// On load, does nothing, meant to be extended
		/// </summary>
		/// <param name="data"></param>
		public virtual void OnLoad(string data)
		{
		}

		/// <summary>
		/// Lets the persistency manager know whether or not the object should be saved 
		/// </summary>
		/// <returns></returns>
		public virtual bool ShouldBeSaved()
		{
			return SaveActive;
		}

		/// <summary>
		/// Generates a unique ID for the object, using the scene name, the object name, and a GUID
		/// </summary>
		/// <returns></returns>
		public virtual string GenerateGuid()
		{
			string newGuid = Guid.NewGuid().ToString();
			
			string guid =
				this.gameObject.scene.name
				+ "-"
				+ this.gameObject.name
				+ "-"
				+ newGuid;
			
			if (!string.IsNullOrEmpty(UniqueIDSuffix))
			{
				guid += "-" + UniqueIDSuffix;
			}
			
			this.SetGuid(guid);

			return guid;
		}

		/// <summary>
		/// Checks if the object's ID is unique or not
		/// </summary>
		/// <param name="guid"></param>
		/// <returns></returns>
		public virtual bool GuidIsUnique(string guid)
		{
			return Resources.FindObjectsOfTypeAll<MMPersistentBase>().Count(x => x.GetGuid() == guid) == 1;
		}

		/// <summary>
		/// Validates the object's GUID, and generates a new one if needed, until a unique one is found
		/// </summary>
		public virtual void ValidateGuid()
		{
			if (!this.gameObject.scene.IsValid())
			{
				_guid = string.Empty;
				return;
			}

			int maxCount = 1000;
			int i = 0;
			
			while ( (string.IsNullOrEmpty(_guid) || !GuidIsUnique(_guid) ) && (i < maxCount) )
			{
				GenerateGuid();
				i++;
			}

			if (i == maxCount)
			{
				Debug.LogWarning(this.gameObject.name + " couldn't generate a unique GUID after " + maxCount + " tries, you should probably change its UniqueIDSuffix");
			}
		}
	}	
}