﻿#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using System.IO;
using UnityEngine.UI;
using System;
using System.Text;
using UnityEngine.Assertions;
using System.Linq;
using System.Text.RegularExpressions;
using Poiyomi.ModularShaderSystem;

//using Poiyomi.ModularShaderSystem;

// PoiyomiPatreon.Scripts.poi_tools.Editor

namespace Poiyomi.AngryLabs
{
    [ExecuteInEditMode]
    public class ModuleInstaller : EditorWindow
    {
        [MenuItem("Poi/Module Installer")]
        static void Init()
        {
            //var window =  GetWindowWithRect<BlendshapeToTextures>(new Rect(0, 0, 200, 500));
            var window = GetWindow(typeof(ModuleInstaller));
            window.minSize = new Vector2(250, 300);
            window.Show();
        }

        private static void ShowWindow()
        {
            var window = GetWindow<ModuleInstaller>();
            window.titleContent = new GUIContent("Module Installer");
            window.Show();

            RefreshShaderList();
        }

        string CopyName { get; set; }
        string NewID { get; set; } = ".poiyomi/User Modules/";

        string OutputPath;

        static List<ModularShader> _shaders;
        static List<Shader> _compiledShaders;

        static IEnumerable<T> FindAssetsByType<T>(string searchFolder = null)
        {
            AssetDatabase.Refresh();
            string[] guids;
            if(searchFolder != null)
            {
                guids  = AssetDatabase.FindAssets($"t:{typeof(T).ToString().Replace("UnityEngine.", "")}", new string[] { searchFolder });
            }
            else
            {
                guids = AssetDatabase.FindAssets($"t:{typeof(T).ToString().Replace("UnityEngine.", "")}");
            }

            var paths = guids.Select(x => AssetDatabase.GUIDToAssetPath(x)).ToList();

            var oassets = paths
                .Select(path => AssetDatabase.LoadMainAssetAtPath(path))
                .Where(x => x != null)
                .ToList()
                ;

            List<T> assets = new List<T>(oassets.Cast<T>());
            return assets;
        }

        ModularShader _activeShader;
        ModularShader ActiveShader
        {
            get
            {
                return _activeShader;
            }
            set
            {
                if(ActiveShader == value)
                {
                    return;
                }

                _activeShader = value;
            }
        }

        /// <summary>
        /// List of modules to install
        /// </summary>
        List<ShaderModule> ToInstall = new List<ShaderModule>();

        bool AlreadyInstalled
        {
            get
            {
                if (ToInstall == null) return false;

                var modules = ActiveShader.BaseModules;

                var hash = new HashSet<string>(ToInstall.Select(x=>x?.Name).Where(x=>x!=null));

                return modules.Any(x => hash.Contains(x?.Name));
            }
        }

        bool ValidCopyName
        {
            get
            {
                if (_shaders == null) RefreshShaderList();
                return !_shaders.Any(x => x.Name == CopyName) && ! string.IsNullOrEmpty(CopyName);
            }
        }

        bool ValidNewID
        {
            get
            {
                if (_compiledShaders == null) RefreshShaderList();

                if (NewID.EndsWith("/"))
                {
                    return false;
                }

                Regex alreadyExists = new Regex($@".*{NewID}$");
                return !_compiledShaders.Any(x => alreadyExists.IsMatch(x.name)) && ! string.IsNullOrEmpty(NewID) ;
            }
        }

        Vector2 ScrollPosition { get; set; }

        bool _refreshing;
        private void OnFocus()
        {
            RefreshShaderList();
        }

        void OnGUI()
        {
            GUILayout.ExpandWidth(true);

            using (new GUILayout.HorizontalScope())
            {
                GUILayout.FlexibleSpace();
                EditorGUILayout.LabelField($"By: AngriestSCV");


                var sty = new GUIStyle(GUI.skin.button)
                {
                    padding = new RectOffset()
                    {
                        bottom = 8,
                        top = 8,
                        left = 25,
                        right = 25,
                    },
                    
                };

                if (GUILayout.Button("angriestscv.gumroad.com", sty))
                {
                    Application.OpenURL("https://angriestscv.gumroad.com");
                }
            }

            using(var sv = new GUILayout.ScrollViewScope(ScrollPosition))
            using (new GUILayout.VerticalScope())
            {
                ScrollPosition = sv.scrollPosition;

                if (_shaders == null || _compiledShaders == null)
                {
                    RefreshShaderList();
                }

                EditorGUILayout.LabelField($""); // provides the perfect amount of vertical space
                ActiveShader = (ModularShader) EditorGUILayout.ObjectField("Shader to install to", ActiveShader, typeof(ModularShader), true);

                if (ActiveShader == null)
                {
                    this.ShowNotification(new GUIContent("A Modular Shader target is required"));
                    return;
                }
                else
                {
                    RemoveNotification();
                }

                GUILayout.Space(20);

                CopyName = EditorGUILayout.TextField("Modular Shader Name", CopyName);
                NewID = EditorGUILayout.TextField("Output Shader Name", NewID);

                OutputPath = EditorGUILayout.TextField("Output Path", OutputPath);
                //EditorGUILayout.LabelField("Output Path", OutputPath);
                if (GUILayout.Button("Set Output Path"))
                {
                    string startPath = OutputPath;
                    if (!Directory.Exists(startPath??""))
                    {
                        startPath = "Assets";
                    }
                    OutputPath = EditorUtility.OpenFolderPanel("Output Path Picker", startPath, "");
                    if (OutputPath.StartsWith(Application.dataPath))
                    {
                        OutputPath = "Assets" + OutputPath.Substring(Application.dataPath.Length);
                    }
                    else
                    {
                        OutputPath = "Assets/";
                    }

                    if(!Directory.Exists(OutputPath))
                    {
                        OutputPath = "Assets/";
                    }

                }

                GUILayout.Space(20);
                using (new GUILayout.HorizontalScope())
                {
                    EditorGUILayout.LabelField("Modules to install");

                    if (GUILayout.Button("+"))
                    {
                        ToInstall.Add(null);
                    }
                    else if(GUILayout.Button("-"))
                    {
                        if(ToInstall.Count != 0)
                        {
                            ToInstall.RemoveAt(ToInstall.Count - 1);
                        }
                        else
                        {
                            Debug.Log("You are module-less.");
                        }
                    }
                }

                for(int i=0; i<ToInstall.Count; i++)
                {
                    ToInstall[i] = (ShaderModule) EditorGUILayout.ObjectField("", ToInstall[i], typeof(ShaderModule), true);
                }

                GUILayout.Space(20);
                if (!ValidCopyName || !ValidNewID)
                {
                    if (string.IsNullOrEmpty(CopyName))
                    {
                        GUILayout.Label("The output Modular Shader name is required.");
                    }
                    else if (!ValidCopyName)
                    {
                        GUILayout.Label("The Modular Shader asset already exists.");
                        if (GUILayout.Button("Delete it/them"))
                        {
                            DeleteModularShaderByName(CopyName);
                            RefreshShaderList();
                        }
                    }

                    if (string.IsNullOrEmpty(NewID))
                    {
                        GUILayout.Label("A new shader name is required");
                    }
                    else if (NewID.EndsWith("/"))
                    {
                        GUILayout.Label("'/' is not a valid final character");
                    }
                    else if (!ValidNewID)
                    {
                        GUILayout.Label("The shader name is already used.");

                        if (GUILayout.Button("Delete it"))
                        {
                            DeleteShaderByName(NewID);
                            RefreshShaderList();
                        }
                    }
                    else
                    {
                        GUILayout.Label("Valid NewID");
                    }
                }
                else if(ToInstall.All(x=>x==null))
                {
                    GUILayout.Label("There are no modules to install");
                }
                else if(string.IsNullOrEmpty(OutputPath))
                {
                    GUILayout.Label("An output path is required");
                }
                else if (!Directory.Exists(OutputPath))
                {
                    GUILayout.Label("The given output directory does not exist");
                }
                else 
                {

                    if (AlreadyInstalled)
                    {
                        GUILayout.Label("At least one of the modules to install is already in the active shader. Duplicates will not be reinstalled");
                    }

                    if (GUILayout.Button("Install Modules"))
                    {
                        Install();
                    }
                }
            }
        }

        private void DeleteModularShaderByName(string name)
        {
            AssetDatabase.Refresh();
            string[] guids;
            guids = AssetDatabase.FindAssets($"t:{typeof(ModularShader).ToString().Replace("UnityEngine.", "")}");
            
            IEnumerable<string> paths = guids.Select(x => AssetDatabase.GUIDToAssetPath(x)).ToList();
            var assets = paths
                .Select(path => new { Asset = AssetDatabase.LoadMainAssetAtPath(path) as ModularShader, Path = path })
                .Where(x => x.Asset != null)
                .Where(x => x.Asset.Name == name)
                .ToList();

            var toDel = assets
                .Select(x => x.Path)
                .ToList()
                ;

            foreach(string path in toDel)
            {
                AssetDatabase.DeleteAsset(path);
            }
        }

        private void DeleteShaderByName(string name)
        {
            AssetDatabase.Refresh();
            string[] guids;
            guids = AssetDatabase.FindAssets($"t:{typeof(Shader).ToString().Replace("UnityEngine.", "")}");
            
            IEnumerable<string> paths = guids.Select(x => AssetDatabase.GUIDToAssetPath(x)).ToList();
            var assets = paths
                .Select(path => new { Asset = AssetDatabase.LoadMainAssetAtPath(path) as Shader, Path = path })
                .Where(x => x.Asset != null)
                .ToList();

            Regex re = new Regex($@".*{name}$");

            string delAsset = assets
                .Where(x => re.IsMatch(x.Asset.name))
                .Select(x => x.Path)
                .FirstOrDefault()
                ;

            AssetDatabase.DeleteAsset(delAsset);
        }

        private void Install()
        {
            RefreshShaderList();
            if(!ValidCopyName || !ValidNewID)
            {
                return;
            }

            ModularShader shader = new ModularShader()
            {
                AdditionalModules = ActiveShader.AdditionalModules.ToList(),
                AdditionalSerializedData = ActiveShader.AdditionalSerializedData,
                Author = ActiveShader.Author,
                BaseModules = ActiveShader.BaseModules.Where(x=>x!=null).ToList(),
                CustomEditor = ActiveShader.CustomEditor,
                Description = ActiveShader.Description,
                hideFlags = ActiveShader.hideFlags,
                Name = CopyName,
                Properties = ActiveShader.Properties.ToList(),
                ShaderTemplate = ActiveShader.ShaderTemplate,
                ShaderPropertiesTemplate = ActiveShader.ShaderPropertiesTemplate,
                UseTemplatesForProperties = ActiveShader.UseTemplatesForProperties,
                Version = ActiveShader.Version,
                Id = ActiveShader.Id + "AudioBump",
                ShaderPath = NewID,
                LockBaseModules = ActiveShader.LockBaseModules,
                LastGeneratedShaders = new List<Shader>(),
            };

            string savePath = $"{OutputPath}/{CopyName}.asset";
            Debug.Log($"Creating new asset at [{savePath}]");
            AssetDatabase.CreateAsset(shader, savePath);

            var installedNames = new HashSet<string>(shader.BaseModules.Select(x => x.Name).Where(x => x != null));

            foreach(ShaderModule mod in ToInstall)
            {
                if (mod == null) continue;
                if (installedNames.Contains(mod.Name)) continue;

                installedNames.Add(mod.Name);
                shader.BaseModules.Add(mod);
            }

            AssetDatabase.SaveAssets();

            RefreshShaderList();
        }

        ShaderModule FindModuleById(string name, string searchPath)
        {
            var modules = FindAssetsByType<ShaderModule>(searchPath);

            return modules.FirstOrDefault(x => x?.Id == name);
        }
        

        private static void RefreshShaderList()
        {
            _shaders = FindAssetsByType<ModularShader>().ToList();
            _compiledShaders = FindAssetsByType<Shader>().ToList();
        }

    }
}
#endif
