using Impact.TagLibrary;
using ImpactCFX.EditorScripts;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

namespace ImpactCFXConversion
{
    public class ImpactCFXConversionWindow : EditorWindow
    {
        [MenuItem("Window/Impact CFX/Conversion Tool")]
        private static void createWindow()
        {
            ImpactCFXConversionWindow instance = CreateInstance<ImpactCFXConversionWindow>();
            instance.titleContent = new GUIContent("Impact CFX Conversion Tool");
            instance.minSize = new Vector2(600, 100);
            instance.Show();
        }

        private class AssetCategory
        {
            public string Header;
            public bool Foldout;
            public Vector2 Scroll;

            public List<string> Assets;
            public bool ShouldConvert;

            public AssetCategory(string header)
            {
                Header = header;
                ShouldConvert = true;
            }

            public string GetHeader()
            {
                return $"{Header} ({Assets.Count})";
            }
        }

        private class AssetCategoryWithFilter : AssetCategory
        {
            public string Filter;

            public AssetCategoryWithFilter(string header, string filter) : base(header)
            {
                this.Filter = filter;
            }

            public void FindAssetsWithFilter()
            {
                Assets = new List<string>(AssetDatabase.FindAssets(Filter, new string[] { "Assets" }));
            }
        }

        private AssetCategoryWithFilter audio;
        private AssetCategoryWithFilter particles;
        private AssetCategoryWithFilter decals;
        private AssetCategoryWithFilter materials;
        private AssetCategory objects;
        private AssetCategory decalManagers;
        private AssetCategoryWithFilter scenes;

        private ImpactTagLibraryBase impactTagLibrary;

        private bool hasScanned;

        private void OnEnable()
        {
            audio = new AssetCategoryWithFilter("Audio", "t:ImpactAudioInteraction");
            particles = new AssetCategoryWithFilter("Particles", "t:ImpactParticleInteraction");
            decals = new AssetCategoryWithFilter("Decals", "t:ImpactDecalInteraction");

            materials = new AssetCategoryWithFilter("Materials", "t:ImpactMaterial");
            objects = new AssetCategory("Objects");
            decalManagers = new AssetCategory("Decal Managers");
            scenes = new AssetCategoryWithFilter("Scenes", "t:Scene");

            hasScanned = false;
        }

        private void OnGUI()
        {
            EditorGUILayout.Separator();

            drawScanPrep();

            if (hasScanned)
            {
                separator();

                drawConversionPrep();
            }
        }

        private void drawScanPrep()
        {
            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();

            string label = hasScanned ? "Rescan!" : "Scan!";
            if (GUILayout.Button(label, GUILayout.Height(EditorGUIUtility.singleLineHeight * 1.5f), GUILayout.Width(400)))
            {
                scan();
            }

            GUILayout.FlexibleSpace();
            EditorGUILayout.EndHorizontal();
        }

        private void scan()
        {
            audio.FindAssetsWithFilter();
            particles.FindAssetsWithFilter();
            decals.FindAssetsWithFilter();

            materials.FindAssetsWithFilter();
            scenes.FindAssetsWithFilter();

            objects.Assets = new List<string>();
            decalManagers.Assets = new List<string>();

            string[] allAssetPaths = AssetDatabase.GetAllAssetPaths();
            foreach (string path in allAssetPaths)
            {
                GameObject g = AssetDatabase.LoadAssetAtPath<GameObject>(path);
                if (g != null)
                {
                    if (g.GetComponentInChildren<Impact.Objects.IImpactObject>() != null)
                    {
                        objects.Assets.Add(AssetDatabase.AssetPathToGUID(path));
                    }
                    if (g.GetComponentInChildren<Impact.Interactions.Decals.ImpactDecalManager>() != null)
                    {
                        decalManagers.Assets.Add(AssetDatabase.AssetPathToGUID(path));
                    }
                }
            }

            string[] tagLibraries = AssetDatabase.FindAssets("t:ImpactTagLibraryBase");
            if (tagLibraries.Length > 0)
            {
                string tagLibraryPath = AssetDatabase.GUIDToAssetPath(tagLibraries[0]);
                impactTagLibrary = AssetDatabase.LoadAssetAtPath<ImpactTagLibraryBase>(tagLibraryPath);
            }

            hasScanned = true;
        }

        private void drawConversionPrep()
        {
            EditorGUILayout.LabelField("Impact Material Assets", EditorStyles.boldLabel);
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);

            drawCategory(materials);

            EditorGUILayout.EndVertical();

            separator();

            EditorGUILayout.LabelField("Impact Interaction Assets", EditorStyles.boldLabel);
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);

            drawCategory(audio);
            drawCategory(particles);
            drawCategory(decals);

            EditorGUILayout.EndVertical();

            separator();

            EditorGUILayout.LabelField("Impact Object Prefabs", EditorStyles.boldLabel);
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);

            drawCategory(objects);
            drawCategory(decalManagers);

            EditorGUILayout.EndVertical();

            separator();

            EditorGUILayout.LabelField("Scenes", EditorStyles.boldLabel);
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);

            drawCategory(scenes);

            EditorGUILayout.EndVertical();

            separator();

            impactTagLibrary = EditorGUILayout.ObjectField(new GUIContent("Impact Tag Library", "The Impact Tag Library to copy tag names from."), impactTagLibrary, typeof(ImpactTagLibraryBase), false) as ImpactTagLibraryBase;

            separator();

            GUI.enabled = impactTagLibrary != null;

            EditorGUILayout.HelpBox("Make sure you have a backup of your project before converting!", MessageType.Error);

            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();

            if (GUILayout.Button("Convert!", GUILayout.Height(EditorGUIUtility.singleLineHeight * 1.5f), GUILayout.Width(400)))
            {
                convert();
            }

            GUILayout.FlexibleSpace();
            EditorGUILayout.EndHorizontal();

            GUI.enabled = true;
        }

        private void convert()
        {
            const string tempFolderPath = "Assets/Impact CFX Converted Assets";
            ConversionContext conversionContext = new ConversionContext(tempFolderPath);
            AssetDatabase.CreateFolder("Assets", "Impact CFX Converted Assets");

            //Convert tag libary to impact settings
            ImpactCFXSettings impactTagSettings = ImpactCFXSettings.instance;
            for (int i = 0; i < ImpactTagLibraryConstants.TagCount; i++)
            {
                impactTagSettings[i] = impactTagLibrary[i];
            }
            impactTagSettings.SaveSettings();

            //Convert assets and prefabs
            convertAssets(audio.Assets, conversionContext, new ImpactAudioEffectConverter(), true);
            convertAssets(particles.Assets, conversionContext, new ImpactParticleEffectConverter(), true);
            convertAssets(decals.Assets, conversionContext, new ImpactDecalEffectConverter(), true);

            convertAssets(materials.Assets, conversionContext, new ImpactMaterialConverter(), true);
            conversionContext.CreateImpactMaterialRegistryAsset();

            convertAssets(objects.Assets, conversionContext, new ImpactObjectConverter(), false);
            convertAssets(decalManagers.Assets, conversionContext, new ImpactDecalManagerConverter(), false);

            //Destroy old components on prefabs, but do not destroy assets yet.
            conversionContext.DestroyObjects(ConversionContextDestroyGroupType.Components);

            //Convert scenes
            SceneConverter sceneConverter = new SceneConverter();
            foreach (string sceneGuid in scenes.Assets)
            {
                string scenePath = AssetDatabase.GUIDToAssetPath(sceneGuid);
                sceneConverter.ConvertScene(scenePath, conversionContext);
            }

            //Destroy everything
            conversionContext.DestroyObjects(ConversionContextDestroyGroupType.Components);
            conversionContext.DestroyObjects(ConversionContextDestroyGroupType.Assets);
            conversionContext.MoveAssets();

            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }

        private void convertAssets(IEnumerable<string> assetGUIDs, ConversionContext conversionContext, IAssetConverter converter, bool createNewAssets)
        {
            foreach (string guid in assetGUIDs)
            {
                string oldAssetPath = AssetDatabase.GUIDToAssetPath(guid);
                Object oldAsset = AssetDatabase.LoadAssetAtPath<Object>(oldAssetPath);

                if (conversionContext.HasConvertedObject(oldAsset.GetInstanceID()))
                    continue;

                Object convertedAsset = converter.Convert(oldAsset, conversionContext);
                string assetName = Path.GetFileNameWithoutExtension(oldAssetPath);
                string newAssetPath = Path.Combine(conversionContext.OutputPath, assetName + ".asset");

                if (createNewAssets)
                {
                    AssetDatabase.CreateAsset(convertedAsset, newAssetPath);
                    ConversionUtility.LogAssetConversion(oldAsset, convertedAsset);

                    conversionContext.AddConvertedObject(oldAsset.GetInstanceID(), convertedAsset);
                    conversionContext.AddObjectToDestroy(ConversionContextDestroyGroupType.Assets, oldAsset);
                    conversionContext.AddMoveAsset(newAssetPath, oldAssetPath);
                }
            }
        }

        private void drawCategory(AssetCategory category)
        {
            EditorGUILayout.BeginHorizontal();

            category.Foldout = EditorGUILayout.BeginFoldoutHeaderGroup(category.Foldout, new GUIContent(category.GetHeader()));
            //category.ShouldConvert = EditorGUILayout.ToggleLeft("Convert", category.ShouldConvert, GUILayout.Width(70));

            EditorGUILayout.EndHorizontal();

            GUI.enabled = category.ShouldConvert;
            if (category.Foldout)
            {
                int count = category.Assets.Count;
                float height = count * EditorGUIUtility.singleLineHeight + EditorGUIUtility.singleLineHeight;

                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                category.Scroll = EditorGUILayout.BeginScrollView(category.Scroll, GUILayout.MaxHeight(Mathf.Min(height, 200)));

                foreach (string asset in category.Assets)
                {
                    string path = AssetDatabase.GUIDToAssetPath(asset);
                    EditorGUILayout.LabelField(path);
                }

                EditorGUILayout.EndScrollView();
                EditorGUILayout.EndVertical();
            }

            EditorGUILayout.EndFoldoutHeaderGroup();

            GUI.enabled = true;
        }

        private static void separator(bool withSpace = true)
        {
            if (withSpace)
                EditorGUILayout.Space();

            GUILayout.Box("", GUILayout.MaxWidth(Screen.width - 25f), GUILayout.Height(2));
        }
    }
}

