﻿using UnityEngine;
using System.Collections.Generic;
#if UNITY_5_4_OR_NEWER
using UnityEngine.SceneManagement;
#endif

namespace I2.Loc
{
	public interface IResourceManager_Bundles
	{
		Object LoadFromBundle(string path, System.Type assetType );
	}

	public class ResourceManager : MonoBehaviour 
	{
		#region Singleton
		public static ResourceManager pInstance
		{
			get {
				bool changed = mInstance==null;

				if (mInstance==null)
					mInstance = (ResourceManager)FindObjectOfType(typeof(ResourceManager));

				if (mInstance==null)
				{
					GameObject GO = new GameObject("I2ResourceManager", typeof(ResourceManager));
					GO.hideFlags = GO.hideFlags | HideFlags.HideAndDontSave;	// Only hide it if this manager was autocreated
					mInstance = GO.GetComponent<ResourceManager>();
					#if UNITY_5_4_OR_NEWER
					SceneManager.sceneLoaded += MyOnLevelWasLoaded;
					#endif
				}

				if (changed && Application.isPlaying)
					DontDestroyOnLoad(mInstance.gameObject);

				return mInstance;
			}
		}
		static ResourceManager mInstance;

		#endregion

		#region Management

		public List<IResourceManager_Bundles> mBundleManagers = new List<IResourceManager_Bundles>();

		#if UNITY_5_4_OR_NEWER
		public static void MyOnLevelWasLoaded(Scene scene, LoadSceneMode mode)
		#else
		public void OnLevelWasLoaded()
		#endif
		{
			pInstance.CleanResourceCache();
			LocalizationManager.UpdateSources();
		}

		#endregion

		#region Assets

		public Object[] Assets;

		// This function tries finding an asset in the Assets array, if not found it tries loading it from the Resources Folder
		public T GetAsset<T>( string Name ) where T : Object
		{
			T Obj = FindAsset( Name ) as T;
			if (Obj!=null)
				return Obj;

			return LoadFromResources<T>( Name );
		}

		Object FindAsset( string Name )
		{
			if (Assets!=null)
			{
				for (int i=0, imax=Assets.Length; i<imax; ++i)
					if (Assets[i]!=null && Assets[i].name == Name)
						return Assets[i];
			}
			return null;
		}

		public bool HasAsset( Object Obj )
		{
			if (Assets==null)
				return false;
			return System.Array.IndexOf (Assets, Obj) >= 0;
		}

		#endregion

		#region Resources Cache

		// This cache is kept for a few moments and then cleared
		// Its meant to avoid doing several Resource.Load for the same Asset while Localizing 
		// (e.g. Lot of labels could be trying to Load the same Font)
		readonly Dictionary<string, Object> mResourcesCache = new Dictionary<string, Object>(System.StringComparer.Ordinal); // This is used to avoid re-loading the same object from resources in the same frame
		//bool mCleaningScheduled = false;

		public T LoadFromResources<T>( string Path ) where T : Object
		{
			try
			{
				if (string.IsNullOrEmpty( Path ))
					return null;

				Object Obj;
				// Doing Resource.Load is very slow so we are catching the recently loaded objects
				if (mResourcesCache.TryGetValue( Path, out Obj ) && Obj!=null)
				{
					return Obj as T;
				}

				T obj = null;

                if (Path.EndsWith("]", System.StringComparison.OrdinalIgnoreCase))  // Handle sprites (Multiple) loaded from resources :   "SpritePath[SpriteName]"
                {
                    int idx = Path.LastIndexOf("[", System.StringComparison.OrdinalIgnoreCase);
                    int len = Path.Length - idx - 2;
                    string MultiSpriteName = Path.Substring(idx + 1, len);
                    Path = Path.Substring(0, idx);

                    T[] objs = Resources.LoadAll<T>(Path);
                    for (int j = 0, jmax = objs.Length; j < jmax; ++j)
                        if (objs[j].name.Equals(MultiSpriteName))
                        {
                            obj = objs[j];
                            break;
                        }
                }
                else
                {
                    obj = Resources.Load(Path, typeof(T)) as T;
                }

				if (obj == null)
					obj = LoadFromBundle<T>( Path );

				if (obj!=null)
					mResourcesCache[Path] = obj;

				/*if (!mCleaningScheduled)
				{
					Invoke("CleanResourceCache", 0.1f);
					mCleaningScheduled = true;
				}*/
				//if (obj==null)
					//Debug.LogWarningFormat( "Unable to load {0} '{1}'", typeof( T ), Path );

				return obj;
			}
			catch (System.Exception e)
			{
				Debug.LogErrorFormat( "Unable to load {0} '{1}'\nERROR: {2}", typeof(T), Path, e.ToString() );
				return null;
			}
		}

		public T LoadFromBundle<T>(string path ) where T : Object
		{
			for (int i = 0, imax = mBundleManagers.Count; i < imax; ++i)
				if (mBundleManagers[i]!=null)
				{
					var obj = mBundleManagers[i].LoadFromBundle(path, typeof(T)) as T;
					if (obj != null)
						return obj;
				}
			return null;
		}

		public void CleanResourceCache()
		{
			mResourcesCache.Clear();
			Resources.UnloadUnusedAssets();

			CancelInvoke();
			//mCleaningScheduled = false;
		}

		#endregion
	}
}