
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using FileInfo = System.IO.FileInfo;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace UdonSharp.Updater
{
    internal class UdonSharpDataLocator : ScriptableObject
    {
        private const string DEFAULT_DATA_PATH = "Assets/UdonSharp/UdonSharpDataLocator.asset";
        private const string PROJECT_LOADED_KEY = "UdonSharpDataLocator_HasPostProcessedAssets";

        private static string _cachedDataLocation;

#if UNITY_EDITOR
        internal static bool HasEverPostProcessedAssets
        {
            get => SessionState.GetBool(PROJECT_LOADED_KEY, false);
            set => SessionState.SetBool(PROJECT_LOADED_KEY, value);
        }
#endif

        public static string DataPath
        {
            get
            {
            #if UNITY_EDITOR
                if (_cachedDataLocation != null)
                    return _cachedDataLocation;

                string[] foundLocatorGuids = AssetDatabase.FindAssets($"t:{nameof(UdonSharpDataLocator)}");
                List<UdonSharpDataLocator> foundLocators = new List<UdonSharpDataLocator>();

                foreach (string locatorGuid in foundLocatorGuids)
                {
                    UdonSharpDataLocator locator =
                        AssetDatabase.LoadAssetAtPath<UdonSharpDataLocator>(AssetDatabase.GUIDToAssetPath(locatorGuid));

                    if (locator)
                        foundLocators.Add(locator);
                }

                if (foundLocators.Count > 1)
                    throw new System.Exception(
                        "Multiple UdonSharp data locators found, make sure you do not have multiple installations of UdonSharp and have not duplicated any UdonSharp directories");

                if (foundLocators.Count == 0)
                {
                    if (!HasEverPostProcessedAssets)
                    {
                        // We can't trust this result, the asset database may be invalid if the Library folder was cleared.
                        // This may be running too early.
                        // Best effort, give back the default path without caching.
                        return Path.GetDirectoryName(DEFAULT_DATA_PATH);
                    }

                    foundLocators.Add(InitializeUdonSharpData());
                }

                _cachedDataLocation = Path.GetDirectoryName(AssetDatabase.GetAssetPath(foundLocators[0]));
                return _cachedDataLocation;
            #else
                throw new System.PlatformNotSupportedException("Cannot get UdonSharp data path outside of the Editor runtime");
            #endif
            }
        }

#if UNITY_EDITOR
        private static string GetUtilitiesPath(string locatorPath)
        {
            return Path.Combine(Path.GetDirectoryName(locatorPath), "UtilityScripts");
        }
        
        private static UdonSharpDataLocator InitializeUdonSharpData()
        {
            if (!AssetDatabase.IsValidFolder(Path.GetDirectoryName(DEFAULT_DATA_PATH)))
                AssetDatabase.CreateFolder("Assets", "UdonSharp");

            string utilsTargetPath = GetUtilitiesPath(DEFAULT_DATA_PATH);
            
            string utilsSourcePath = Path.Combine(UdonSharpLocator.SamplesPath, "Utilities");
            
            if (Directory.Exists(utilsSourcePath))
                DeepCopyDirectory(utilsSourcePath, utilsTargetPath);
            else
                Debug.LogWarning("No utilities directory found to copy from for UdonSharp utility scripts");
            
            UdonSharpDataLocator locator = CreateInstance<UdonSharpDataLocator>();
            AssetDatabase.CreateAsset(locator, DEFAULT_DATA_PATH);

            Debug.Log("Created UdonSharp data directory", locator);

            AssetDatabase.Refresh();

            return locator;
        }
    #endif

        private static void DeepCopyDirectory(string sourcePath, string destinationPath)
        {
            string[] sourceDirs = Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories);

            foreach (string sourceDir in sourceDirs)
            {
                Directory.CreateDirectory(sourceDir.Replace(sourcePath, destinationPath));
            }

            string[] sourceFiles = Directory.GetFiles(sourcePath, "*", SearchOption.AllDirectories);

            foreach (string sourceFile in sourceFiles)
            {
                string targetFilePath = sourceFile.Replace(sourcePath, destinationPath);
                File.Copy(sourceFile, targetFilePath, true);
                new FileInfo(targetFilePath).IsReadOnly = false;
            }
        }
    }

#if UNITY_EDITOR
    [CustomEditor(typeof(UdonSharpDataLocator))]
    internal class UdonSharpDataLocatorEditor : Editor
    {
        public override void OnInspectorGUI()
        {
            EditorGUILayout.HelpBox("Do not delete this file! This is used by UdonSharp to locate its data directory.", MessageType.Error);
        }
    }
 
    internal class InitUSharpDataOnImport : AssetPostprocessor
    {
        private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
        {
            UdonSharpDataLocator.HasEverPostProcessedAssets = true;
            
            if (importedAssets.Length <= 0) return;
            
            try
            {
                foreach (string importedAsset in importedAssets)
                {
                    if (Path.GetFileName(importedAsset) == "UdonSharpLocator.asset" && 
                        AssetDatabase.LoadAssetAtPath<UdonSharpLocator>(importedAsset))
                    {
                        string _ = UdonSharpDataLocator.DataPath; // Implicitly initializes the data asset if it doesn't exist
                    }
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(e);
            }
        }
    }
#endif
}
