/********************************************************************
	created:	2018/01/04
	created:	4:1:2018   12:33
	filename: 	F:\work\x2m\mainline\dev\client\Assets\Script\X2Engine\ResourceManager\ResourcesFolderResource.cs
	file path:	F:\work\x2m\mainline\dev\client\Assets\Script\X2Engine\ResourceManager
	file base:	WinResource
	file ext:	cs
	author:		zhangqiang
	
	purpose:	load resource from resources folder
*********************************************************************/
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Giant.Common;
//using X2Interface;
#if UNITY_EDITOR
using UnityEngine.SceneManagement;
using UnityEditor;
#endif
namespace Giant.Resource
{

#if UNITY_EDITOR
    class SceneLoadInfo
    {
        public int                          Key;
        public AsyncOperation               Operation;
        public Bounds                       Aabb;
        public LoadCallback                 Callback;
        public SceneLoadingFinishedCallback LoadingFinishedCallback;   
    }
#endif

    public class ResourcesFolderResource
    {

#if UNITY_EDITOR
        ListMap<Int32, SceneLoadInfo> m_loadingSceneList = new ListMap<Int32, SceneLoadInfo>(10);
#endif
        /// internal cache for all loaded resources, <resource name hashID, resource object>.
        Dictionary<Int32, UnityEngine.Object> m_resources = new Dictionary<Int32, UnityEngine.Object>(20);

        /// all current issued resource load requests, <resource name hashID, resource load request>.
        ListMap<Int32, stResourceLoadInfo> m_requestResources = new ListMap<Int32, stResourceLoadInfo>(20);

        /// all loaded resources under various folders <path name hashID, all resource objects under this path>.
        Dictionary<Int32, UnityEngine.Object[]> m_resourcesAll = new Dictionary<Int32, UnityEngine.Object[]>(20);

        /// Singleton instance.
        static ResourcesFolderResource m_sInstance = null;
        public static ResourcesFolderResource Instance
        {
            get
            {
                if (m_sInstance == null)
                {
                    m_sInstance = new ResourcesFolderResource();
                }
                return m_sInstance;
            }
        }

        struct stResourceLoadInfo
        {
            public int key;
            public ResourceRequest request;
            public List<LoadCallback> callbackList;
        }

       

        /// Called when the instance will be destroyed.
		public void DoDestroy()
        {
#if UNITY_EDITOR
            m_loadingSceneList.Clear();
            m_loadingSceneList = null;
#endif
            m_requestResources.Clear();
            //foreach(var res in m_resources)
            //{
            //    Resources.UnloadAsset(res.Value);
            //}
            //foreach(var res in m_resourcesAll)
            //{
            //    for(int i = 0; i < res.Value.Length; ++i)
            //    {
            //         Resources.UnloadAsset(res.Value[i]);
            //    }        
            //}
            m_resources.Clear();
            m_resourcesAll.Clear();
            m_resources = null;
            m_resourcesAll = null;
            m_sInstance = null;
            Resources.UnloadUnusedAssets();
        }

        /// Unload a resource from memory.
        /// <param name="strResourcePath">The relative path of the resource.</param>
        public void UnLoadResource(VString strResourcePath)
        {
            Int32 hash_code = strResourcePath.GetHashCode();
            UnityEngine.Object obj = null;
            if (m_resources.TryGetValue(hash_code, out obj))
            {
                obj = null;
                Resources.UnloadAsset(obj);
                m_resources.Remove(hash_code);
            }
        }

        /// Loads all assets in a folder or file at path in a Resources folder.
        /// <param name="strResourcePath">Pathname of the target folder.</param>
        public UnityEngine.Object[] LoadResourceAll(VString strResourcePath, Type t = null)
        {
            // get resources from the internal cache
            Int32 hash_code = strResourcePath.GetHashCode();
            UnityEngine.Object[] obj = null;
            if (m_resourcesAll.TryGetValue(hash_code, out obj))
            {
                return obj;
            }

            // load resoruces from the file path
            if (t != null)
                obj = Resources.LoadAll(strResourcePath.ToString(), t);
            else
                obj = Resources.LoadAll(strResourcePath.ToString());
            if (obj != null)
            {
                m_resourcesAll.Add(strResourcePath.GetHashCode(), obj);
            }

            return obj;
        }

        public void DoUpdate()
        {

#if UNITY_EDITOR
            for (int i = 0; i < m_loadingSceneList.ValueList.Count; ++i)
            {
                SceneLoadInfo info = m_loadingSceneList.ValueList[i];
                if (null != info.Operation && info.Operation.isDone)
                {
                    if (null != info.Callback)
                    {
                        info.Callback(null);
                    }
                    else if (null != info.LoadingFinishedCallback)
                    {
                        info.LoadingFinishedCallback(info.Aabb.min.x, info.Aabb.min.y, info.Aabb.min.z, info.Aabb.max.x, info.Aabb.max.y, info.Aabb.max.z);
                    }
                    m_loadingSceneList.CachedRemove(info.Key);
                }
            }
            m_loadingSceneList.RemoveFromCache();
#endif
            for (int i = 0; i < m_requestResources.ValueList.Count; ++i)
            {
                stResourceLoadInfo info = m_requestResources.ValueList[i];
                if (info.request.isDone)
                {                                  
                    // resource just loaded
                    for (int j = 0; j < info.callbackList.Count; ++j)
                        info.callbackList[j](info.request.asset);
                    
                    m_requestResources.CachedRemove(info.key);
                    if(!m_resources.ContainsKey(info.key))
                        m_resources.Add(info.key, info.request.asset);
                }
            }

            m_requestResources.RemoveFromCache();
        }

        /// load a resource from the "Resources" folder, using synchronous mode.
        /// <param name="strResourcePath"> The relative path of the resource.</param>
        /// <param name="callback">        Callback when the resource is loaded.</param>
        /// <param name="t"> Type of the resource to load.</param>
        public UnityEngine.Object LoadResourceSync(VString strResourcePath, Type t = null)
        {
            strResourcePath.ToLower();
            // get the resource from the internal cache
            Int32 hash_code = strResourcePath.GetHashCode();
            UnityEngine.Object obj = null;
            if (m_resources.TryGetValue(hash_code, out obj))
            {
                return obj;
            }

#if UNITY_EDITOR
        if (ResourceloadConfig.useAssetDataBase)
        {
            if (t != null)
                obj = AssetDatabase.LoadAssetAtPath(strResourcePath.ToString(), t);
            else
                obj = AssetDatabase.LoadMainAssetAtPath(strResourcePath.ToString());
        }
        else
#endif
        // load the resource from the Resource folder
        {
                if (t != null)
                    obj = Resources.Load(strResourcePath.ToString(), t);
                else
                    obj = Resources.Load(strResourcePath.ToString());
            }

            if (obj != null)
            {
                m_resources.Add(strResourcePath.GetHashCode(), obj);
            }
            return obj;
        }

        /// load a resource from the "Resources" folder, using asynchronous mode.
		/// <param name="strResourcePath"> The relative path of the resource.</param>
		/// <param name="callback">        Callback when the resource is loaded.</param>
		/// <param name="strResourcePath"> Type of the resource to load.</param>
        public void LoadResourceAsync(VString strResourcePath, LoadCallback callback, Type t = null)
        {
            Int32 hash_code = strResourcePath.GetHashCode();
            UnityEngine.Object obj = null;
            if (m_resources.TryGetValue(hash_code, out obj))
            {
                if (null != callback)
                {
                    callback(obj);
                }
                return;
            }
            LoadResourceAsyncInternal(strResourcePath, callback, t);
        }

        /// load a resource from the "Resources" folder, using asynchronous mode.
		/// <param name="strResourceName">The relative path of the resource.</param>
		/// <param name="callback">       Callback when the resource is loaded.</param>
		/// <param name="strResourcePath">Type of the resource to load.</param>
        void LoadResourceAsyncInternal(VString strResourceName, LoadCallback callback, Type t = null)
        {
            // try to get the resource from the internal cache
            UnityEngine.Object res;
            Int32 hashCode = strResourceName.GetHashCode();
            if (m_resources.TryGetValue(hashCode, out res))
            {
                if (null != callback)
                    callback(res);
                return;
            }
#if UNITY_EDITOR
        if (ResourceloadConfig.useAssetDataBase)
        {
            UnityEngine.Object obj = null;
            if (t != null)
                obj = AssetDatabase.LoadAssetAtPath(strResourceName.ToString(), t);
            else
                obj = AssetDatabase.LoadMainAssetAtPath(strResourceName.ToString());

            if (obj != null)
            {
                m_resources.Add(strResourceName.GetHashCode(), obj);
            }

            if (callback != null)
                callback(obj);
        }
        else
#endif

        {
            // whether this resource load has already requested
            stResourceLoadInfo loadinfo;
            if (m_requestResources.TryGetValue(hashCode, out loadinfo))
            {
                loadinfo.callbackList.Add(callback);
                m_requestResources[hashCode] = loadinfo;
                return;
            }

            // issue a resource load request
            ResourceRequest request = null;
            if (null != t)
                request = Resources.LoadAsync(strResourceName.ToString(), t);
            else
                request = Resources.LoadAsync(strResourceName.ToString());
            if (loadinfo.callbackList == null)
                loadinfo.callbackList = new List<LoadCallback>(20);
            loadinfo.callbackList.Add(callback);
            loadinfo.request = request;
            loadinfo.key = hashCode;
            m_requestResources[hashCode] = loadinfo;
        }

           
        }


#if UNITY_EDITOR
        /// load a scene resource from the "Resources" folder, using asynchronous mode.
        /// <param name="strSceneName">The scene name.</param>
        /// <param name="callback">       Callback when the resource is loaded.</param>
        /// <param name="bAdditive"> Single or additive mode.</param>
        public void LoadSceneAsync(VString strSceneName, LoadCallback callBack, bool bAdditive)
        {
            int hashCode = strSceneName.GetHashCode();
            SceneLoadInfo info;
            if (m_loadingSceneList.TryGetValue(hashCode, out info))
            {
                return;
            }

            info = new SceneLoadInfo();
            info.Key = hashCode;
            info.Operation = SceneManager.LoadSceneAsync(strSceneName.ToString(), bAdditive ? LoadSceneMode.Additive : LoadSceneMode.Single);
            info.Callback = callBack;
            info.LoadingFinishedCallback = null;
            info.Aabb = new Bounds();
            m_loadingSceneList[hashCode] = info;
        }

        /// load a scene resource from the "Resources" folder, using synchronous mode.
        /// <param name="strSceneName">The scene name.</param>
        /// <param name="callback">       Callback when the resource is loaded.</param>
        /// <param name="bAdditive"> Single or additive mode.</param>
        public void LoadSceneSync(VString strSceneName, LoadCallback callBack, bool bAdditive)
        {
            SceneManager.LoadScene(strSceneName.ToString(), bAdditive ? LoadSceneMode.Additive : LoadSceneMode.Single);
            if(null != callBack)
                callBack(null);
        }

        /// load a scene resource from the "Resources" folder, using asynchronous mode.
        /// <param name="strSceneName">The scene name.</param>
        /// <param name="callback">       Callback when the resource is loaded.</param>
        /// <param name="bAdditive"> Single or additive mode.</param>
        public void LoadSceneAsync(VString strSceneName, SceneLoadingFinishedCallback callBack,  Bounds aabb, bool bAdditive)
        {
            int hashCode = strSceneName.GetHashCode();
            SceneLoadInfo info;
            if (m_loadingSceneList.TryGetValue(hashCode, out info))
            {
                return;
            }
            
            info = new SceneLoadInfo();
            info.Key = hashCode;
            info.Operation = SceneManager.LoadSceneAsync(strSceneName.ToString(), bAdditive ? LoadSceneMode.Additive : LoadSceneMode.Single);
            info.Callback = null;
            info.LoadingFinishedCallback = callBack;
            info.Aabb = aabb;
            m_loadingSceneList[hashCode] = info;
        }

        /// load a scene resource from the "Resources" folder, using synchronous mode.
        /// <param name="strSceneName">The scene name.</param>
        /// <param name="callback">       Callback when the resource is loaded.</param>
        /// <param name="bAdditive"> Single or additive mode.</param>
        public void LoadSceneSync(VString strSceneName, SceneLoadingFinishedCallback callBack, Bounds aabb, bool bAdditive)
        {
            SceneManager.LoadScene(strSceneName.ToString(), bAdditive ? LoadSceneMode.Additive : LoadSceneMode.Single);
            if (null != callBack)
            {
                callBack(aabb.min.x, aabb.min.y, aabb.min.z, aabb.max.x, aabb.max.y, aabb.max.z);
            }
        }

        /// Build scene list in build setting
        public void RestoreSceneBuildList()
        {

            /*EditorBuildSettingsScene[] newSettings = new EditorBuildSettingsScene[3];
           
            newSettings[0] = new EditorBuildSettingsScene("Assets/Scene/main.unity", true);
            newSettings[1] = new EditorBuildSettingsScene("Assets/Scene/loading.unity", true);
			newSettings[2] = new EditorBuildSettingsScene("Assets/Scene/plus.unity", true);

			EditorBuildSettings.scenes = newSettings;
            AssetDatabase.SaveAssets();*/
        }

        /// retore the scene list in build setting
        public void BuildSceneList()
        {

            /*List<EditorBuildSettingsScene> oldBuildSettingSceneList = new List<EditorBuildSettingsScene>(50);
            List<EditorBuildSettingsScene> buildSettingSceneList = new List<EditorBuildSettingsScene>(50);

            oldBuildSettingSceneList.Clear();     
            oldBuildSettingSceneList.Add(new EditorBuildSettingsScene("Assets/Scene/main.unity", true));
            oldBuildSettingSceneList.Add(new EditorBuildSettingsScene("Assets/Scene/loading.unity", true));
			oldBuildSettingSceneList.Add(new EditorBuildSettingsScene("Assets/Scene/plus.unity", true));

			string sceneSourcePath = IResourceLoader.streamingAssetsPath.Replace("StreamingAssets", "Resources/asset/scene/");
            string [] filesName = Directory.GetFiles(sceneSourcePath, "*", SearchOption.AllDirectories);
            const string sceneSurfix = ".unity";
            buildSettingSceneList.Clear();
            int index = 0;
            for (int i = 0; i < filesName.Length; ++i)
            {
                if (filesName[i].EndsWith(sceneSurfix))
                {
                    index = filesName[i].IndexOf("Assets");
                    if(index >= 0)
                        buildSettingSceneList.Add( new EditorBuildSettingsScene(filesName[i].Substring(index), true));
                }                  
            }

            int totalCount = buildSettingSceneList.Count + oldBuildSettingSceneList.Count;
            EditorBuildSettingsScene [] newSettings = new EditorBuildSettingsScene[totalCount];

            for (int i = 0; i < oldBuildSettingSceneList.Count; ++i)
            {
                newSettings[i] = oldBuildSettingSceneList[i];
            }
            for (int i = oldBuildSettingSceneList.Count; i < totalCount; ++i)
            {
                newSettings[i] = buildSettingSceneList[i - oldBuildSettingSceneList.Count];
            }

            EditorBuildSettings.scenes = newSettings;
            AssetDatabase.SaveAssets();*/
        }
#endif
    }
}
