#if UNITY_EDITOR && UNITY_2021_3_OR_NEWER
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using UnityEngine.Windows;

[InitializeOnLoad]
public class ZFolderIcons : EditorWindow
{
    private static GUIStyle DirectoryLabelStyle;
    private static GUIStyle MonoScriptLabelStyle;
    private const float maxHeight = 18f;

    private static Texture2D scriptIconTexture;

    private static Dictionary<Regex, string> PreDefinedFolderLabels = new Dictionary<Regex, string>
    {
        { 
            new Regex(@"^Script(s)?$", RegexOptions.IgnoreCase),    
            "C#"
        },
        { 
            new Regex(@"^Plugin(s)?$", RegexOptions.IgnoreCase),            
            "Plug"
        },
        { 
            new Regex(@"^Resource(s)?$", RegexOptions.IgnoreCase),          
            "Res"
        },
        { 
            new Regex(@"^Editor$", RegexOptions.IgnoreCase),              
            "Edit"
        },
        { 
            new Regex(@"^Prefab(s)?$", RegexOptions.IgnoreCase),            
            "PF"
        },
        { 
            new Regex(@"^Tile(s)?Palette(s)?$", RegexOptions.IgnoreCase), 
            "Tile"
        },
    };

    static ZFolderIcons()
    {
        EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemGUI;
    }

    private static void OnProjectWindowItemGUI(string guid, Rect selectionRect)
    {
        if (selectionRect.height < maxHeight) 
            return;

        // Get the asset path based on the GUID
        string assetPath = AssetDatabase.GUIDToAssetPath(guid);

        // Check if the asset is a folder and not in the base project folder
        // Use "&& assetPath.Count(c => c == '/') >= 2" to ignore base assets folder    

        // TODO: Impliment AssetDatabase.GetMainAssetTypeFromGUID(new GUID(guid)).IsSubclassOf(typeof(ScriptableObject))

        Type objectType = null;
        if (AssetDatabase.IsValidFolder(assetPath))
        {
            DoFolderIcons(assetPath, selectionRect);
        }
        else
        {
            objectType = AssetDatabase.GetMainAssetTypeFromGUID(new GUID(guid));
        }

        if (objectType == null) return;
        if (objectType == typeof(MonoScript)) // Is script object
        {
            DoMonoScriptIcon(assetPath, selectionRect);
        }
    }

    static void DoFolderIcons(string folderPath, Rect selectionRect)
    {
        // Calculate the position for the custom label
        Rect labelRect = new Rect(selectionRect.x + selectionRect.width * 0.15f, selectionRect.y + selectionRect.width * 0.4f, selectionRect.width, selectionRect.height);

        string folderName = Path.GetFileName(folderPath);

        // Create GUIStyle if it hasn't been created yet
        if (DirectoryLabelStyle == null)
        {
            DirectoryLabelStyle = new GUIStyle(EditorStyles.miniLabel);
            DirectoryLabelStyle.alignment = TextAnchor.UpperLeft; // Adjust the font size as desired
            DirectoryLabelStyle.normal.textColor = Color.black; // Set the text color

            TryAssignFont(ref DirectoryLabelStyle);
        }
        DirectoryLabelStyle.fontSize = (int)(selectionRect.width * 0.35f); // Adjust the font size as desired

        string labelText;
        if (TryGetPreDefinedLabel(folderName, out string label))
        {
            labelText = label;
        }
        else if (folderName.Count(char.IsUpper) >= 2)
        {
            labelText = new string(folderName.Where(x => char.IsUpper(x)).Take(4).ToArray());
        }
        else
        {
            labelText = new(folderName.Take(4).ToArray());
        }

        // Draw the custom label with black color
        var previousColor = GUI.color;
        GUI.color = Color.black;
        EditorGUI.LabelField(labelRect, labelText, DirectoryLabelStyle);
        GUI.color = previousColor;

        DrawFolderColorStrip(folderName, selectionRect);
    }

    static void DrawFolderColorStrip(string seedString, Rect selectionRect)
    {
        Color stripColor = GetRandomColor(seedString);

        // Calculate the position for the strip
        Rect stripRect = new Rect(
            x:      selectionRect.x + selectionRect.width * 0.175f,
            y:      selectionRect.y + selectionRect.width * 0.725f,
            width:  selectionRect.width * 0.64f,
            height: selectionRect.height * 0.05f);
        
        // Draw the custom label with black color
        var previousColor = GUI.color;
        EditorGUI.DrawRect(stripRect, stripColor);
        GUI.color = previousColor;
    }

    static void DoMonoScriptIcon(string scriptPath, Rect selectionRect)
    {
        // Calculate the position for the white overlay
        Rect overwriteOverlayRect = new Rect(selectionRect.x + selectionRect.width * 0.25f, selectionRect.y + selectionRect.width * 0.20f, selectionRect.width*0.5f, selectionRect.height*0.5f);
        var previousColor = GUI.color;

        if (!Application.isPlaying)
        {
            // Draw the custom rect with white color
            previousColor = GUI.color;
            float shadeOfWhite = 247;
            GUI.color = new Color(shadeOfWhite / 255f, shadeOfWhite / 255f, shadeOfWhite / 255f);
            EditorGUI.DrawRect(overwriteOverlayRect, GUI.color);
            GUI.color = previousColor;
        }
        else
        {
            //Did not work


            //// Gets playmode tint color
            //string prefPlaymodeTint = EditorPrefs.GetString("Playmode tint", "").Replace("Playmode tint;", "");
            //string[] rgba = prefPlaymodeTint.Split(";");
            //Color color = new Color(float.Parse(rgba[0]), float.Parse(rgba[1]), float.Parse(rgba[2]), float.Parse(rgba[3]));

            //// Draw the custom rect with play color tint
            //previousColor = GUI.color;
            //GUI.color = color;
            //EditorGUI.DrawRect(overwriteOverlayRect, GUI.color);
            //GUI.color = previousColor;

            // Draw the custom rect with white color
            previousColor = GUI.color;
            float shadeOfWhite = 247;
            GUI.color = new Color(shadeOfWhite / 255f, shadeOfWhite / 255f, shadeOfWhite / 255f);
            EditorGUI.DrawRect(overwriteOverlayRect, GUI.color);
            GUI.color = previousColor;
        }


        string scriptName = Path.GetFileName(scriptPath);

        const int maxLength = 8;
        string labelText;
        {
            // Use LINQ to iterate through each character and insert a line break before uppercase letters
            labelText = new string(scriptName.SelectMany(c => char.IsUpper(c) ? new char[] { '\n', c } : new[] { c }).ToArray());
            labelText = labelText.TrimStart('\n').Replace(".cs", "");
            labelText = string.Join('\n', MergeLooseCapitalStrings(labelText.Split('\n')));
            labelText = string.Join('\n', labelText.Split('\n').Select(x => x.Length > 6 ? new string(x.Take(maxLength).ToArray()) + "�" : x));
        }

        // Calculate the position for the custom label
        Rect labelRect = new Rect(selectionRect.x + selectionRect.width * 0.15f, selectionRect.y + selectionRect.width * 0.1f, selectionRect.width, selectionRect.height);

        // Create GUIStyle if it hasn't been created yet
        if (MonoScriptLabelStyle == null)
        {
            MonoScriptLabelStyle = new GUIStyle(EditorStyles.miniLabel);
            MonoScriptLabelStyle.alignment = TextAnchor.UpperLeft; // Adjust the font size as desired
            MonoScriptLabelStyle.normal.textColor = Color.black; // Set the text color

            TryAssignFont(ref MonoScriptLabelStyle);
        }
        MonoScriptLabelStyle.fontSize = (int)(selectionRect.width * 0.2f); // Adjust the font size as desired

        // Draw the custom label with black color
        previousColor = GUI.color;
        GUI.color = Color.black;
        EditorGUI.LabelField(labelRect, labelText, MonoScriptLabelStyle);
        GUI.color = previousColor;

        // Custom icon rect
        Rect customIconRect = new Rect(selectionRect.x + selectionRect.width * 0.62f, selectionRect.y + selectionRect.width * 0.645f, selectionRect.width * 0.25f, selectionRect.height * 0.25f);

        // Load texture if null
        if (scriptIconTexture == null)
        {
            if (TryGetScriptIcon(out Texture2D icon))
            {
                scriptIconTexture = icon;
            }
            else
            {
                return; // Couldn't get icon, so don't draw it
            }
        }

        // Draw the custom icon
        GUI.DrawTexture(customIconRect, scriptIconTexture);
    }

    public static bool TryGetPreDefinedLabel(string folderName, out string label)
    {
        foreach (var regex in PreDefinedFolderLabels.Keys)
        {
            if (regex.IsMatch(folderName))
            {
                label = PreDefinedFolderLabels[regex];
                return true;
            }
        }

        label = "Error";
        return false;
    }

    static bool TryGetScriptIcon(out Texture2D icon)
    {
        if (FindFolder("ExtraIcons", out string path))
        {
            icon = AssetDatabase.LoadAssetAtPath<Texture2D>(path + "/ScriptHashtag.png");
            return true;
        }
        else
        {
            icon = null;
            return false;
        }
    }

    static bool FindFolder(string folderName, out string path)
    {
        string[] guids = AssetDatabase.FindAssets("t:Folder " + folderName);

        if (guids.Length > 0)
        {
            path = AssetDatabase.GUIDToAssetPath(guids[0]);
            return true;
        }
        else
        {
            path = null;
            return false;
        }
    }

    static string[] MergeLooseCapitalStrings(string[] arr)
    {
        List<string> result = new List<string>();
        string currentString = "";

        foreach (string s in arr)
        {
            if (IsUpperCase(s))
            {
                currentString += s;
            }
            else if (!string.IsNullOrEmpty(currentString))
            {
                result.Add(currentString + s);
                currentString = "";
            }
            else
            {
                result.Add(s);
            }
        }

        if (!string.IsNullOrEmpty(currentString))
        {
            result.Add(currentString);
        }

        return result.ToArray();
    }

    static Func<string, bool> IsUpperCase = s => !string.IsNullOrEmpty(s) && s.All(char.IsUpper);

    /// <summary>
    /// Generates a semi random color out of a seed
    /// </summary>
    /// <param name="seed">Used to generate the color</param>
    /// <returns>A semi random color</returns>
    public static Color GetRandomColor(string seed)
    {
        int intSeed = seed.Select(c=>(int)c).Sum();

        System.Random random = new System.Random(intSeed);

        // Generate random values for R, G, and B components between 0 and 1.
        float r = (float)random.NextDouble();
        float g = (float)random.NextDouble();
        float b = (float)random.NextDouble();

        // Create and return the color.
        return new Color(r, g, b);
    }

    private static void TryAssignFont(ref GUIStyle guiFont)
    {
        string[] guids = AssetDatabase.FindAssets("t:Font CozetteVector", new[] { "Assets/Plugins/ZFolderIcons" });


        if (guids.Length <= 0)
        {
            return; // Couldn't find font asset
        }

        string path = AssetDatabase.GUIDToAssetPath(guids[0]);
        guiFont.font = AssetDatabase.LoadAssetAtPath<Font>(path);
    }
}
#endif