#if UNITY_EDITOR
using FishNet.Utility.Constant;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace FishNet.Editing
{
    public static class Finding
    {
        #region Private.
        /// 
        /// Path where the FishNet.Runtime assembly is.
        /// 
        [System.NonSerialized]
        private static string _fishNetRuntimePath = string.Empty;
        /// 
        /// Path where the FishNet.Generated assembly is.
        /// 
        private static string _fishNetGeneratedPath = string.Empty;
        #endregion
        /// 
        /// Sets FishNet assembly paths.
        /// 
        /// 
        private static void SetPaths(bool error)
        {
            if (_fishNetGeneratedPath != string.Empty && _fishNetRuntimePath != string.Empty)
                return;
            string[] guids = AssetDatabase.FindAssets("t:asmdef", new string[] { "Assets" });
            string[] objectPaths = new string[guids.Length];
            for (int i = 0; i < guids.Length; i++)
                objectPaths[i] = AssetDatabase.GUIDToAssetPath(guids[i]);
            string runtimeName = (UtilityConstants.RUNTIME_ASSEMBLY_NAME + ".asmdef").ToLower();
            string generatedName = (UtilityConstants.GENERATED_ASSEMBLY_NAME + ".asmdef").ToLower();
            /* Find all network managers which use Single prefab linking
             * as well all network object prefabs. */
            foreach (string item in objectPaths)
            {
                //Found directory to create object in.
                if (item.ToLower().Contains(runtimeName))
                    _fishNetRuntimePath = System.IO.Path.GetDirectoryName(item);
                else if (item.ToLower().Contains(generatedName))
                    _fishNetGeneratedPath = System.IO.Path.GetDirectoryName(item);
                if (_fishNetGeneratedPath != string.Empty && _fishNetRuntimePath != string.Empty)
                    return;
            }
        }
        /// 
        /// Gets path for where the FishNet.Runtime assembly is.
        /// 
        /// 
        public static string GetFishNetRuntimePath(bool error)
        {
            SetPaths(error);
            return _fishNetRuntimePath;
        }
        /// 
        /// Gets path for where the FishNet.Generated assembly is.
        /// 
        /// 
        /// 
        public static string GetFishNetGeneratedPath(bool error)
        {
            SetPaths(error);
            return _fishNetGeneratedPath;
        }
        /// 
        /// Gets all GameObjects in Assets and optionally scenes.
        /// 
        /// 
        /// 
        public static List GetGameObjects(bool userAssemblies, bool fishNetAssembly, bool includeScenes, string[] ignoredPaths = null)
        {
            List results = new List();
            string[] guids;
            string[] objectPaths;
            guids = AssetDatabase.FindAssets("t:GameObject", null);
            objectPaths = new string[guids.Length];
            for (int i = 0; i < guids.Length; i++)
                objectPaths[i] = AssetDatabase.GUIDToAssetPath(guids[i]);
            foreach (string item in objectPaths)
            {
                bool inFishNet = item.Contains(_fishNetRuntimePath);
                if (inFishNet && !fishNetAssembly)
                    continue;
                if (!inFishNet && !userAssemblies)
                    continue;
                if (ignoredPaths != null)
                {
                    bool ignore = false;
                    foreach (string path in ignoredPaths)
                    {
                        if (item.Contains(path))
                        {
                            ignore = true;
                            break;
                        }
                    }
                    if (ignore)
                        continue;
                }
                GameObject go = (GameObject)AssetDatabase.LoadAssetAtPath(item, typeof(GameObject));
                results.Add(go);
            }
            if (includeScenes)
                results.AddRange(GetSceneGameObjects());
            return results;
        }
        /// 
        /// Gets all GameObjects in all open scenes.
        /// 
        /// 
        private static List GetSceneGameObjects()
        {
            List results = new List();
            for (int i = 0; i < SceneManager.sceneCount; i++)
                results.AddRange(GetSceneGameObjects(SceneManager.GetSceneAt(i)));
            return results;
        }
        /// 
        /// Gets all GameObjects in a scene.
        /// 
        private static List GetSceneGameObjects(Scene s)
        {
            List results = new List();
            List buffer = new List();
            //Iterate all root objects for the scene.
            GameObject[] gos = s.GetRootGameObjects();
            for (int i = 0; i < gos.Length; i++)
            {
                /* Get GameObjects within children of each
                 * root object then add them to the cache. */
                gos[i].GetComponentsInChildren(true, buffer);
                foreach (Transform t in buffer)
                    results.Add(t.gameObject);
            }
            return results;
        }
        /// 
        /// Gets created ScriptableObjects of T.
        /// 
        /// 
        /// 
        public static List GetScriptableObjects(bool fishNetAssembly, bool breakOnFirst = false)
        {
            System.Type tType = typeof(T);
            List results = new List();
            string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new string[] { "Assets" });
            string[] objectPaths = new string[guids.Length];
            for (int i = 0; i < guids.Length; i++)
                objectPaths[i] = AssetDatabase.GUIDToAssetPath(guids[i]);
            /* This might be faster than using directory comparers.
             * Don't really care since this occurs only at edit. */
            List fishNetPaths = new List();
            fishNetPaths.Add(_fishNetGeneratedPath.Replace(@"/", @"\"));
            fishNetPaths.Add(_fishNetGeneratedPath.Replace(@"\", @"/"));
            fishNetPaths.Add(_fishNetRuntimePath.Replace(@"/", @"\"));
            fishNetPaths.Add(_fishNetRuntimePath.Replace(@"\", @"/"));
            /* Find all network managers which use Single prefab linking
             * as well all network object prefabs. */
            foreach (string item in objectPaths)
            {
                //This will skip hidden unity types.
                if (!item.EndsWith(".asset"))
                    continue;
                if (fishNetAssembly)
                {
                    bool found = false;
                    foreach (string path in fishNetPaths)
                    {
                        if (item.Contains(path))
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                        continue;
                }
                UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(item, tType);
                if (obj != null && tType != null && obj.GetType() == tType)
                {
                    results.Add(obj);
                    if (breakOnFirst)
                        return results;
                }
            }
            return results;
        }
    }
}
#endif