using System;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using HeurekaGames.AssetHunterPRO.BaseTreeviewImpl.DependencyGraph;
using UnityEditorInternal;

namespace HeurekaGames.AssetHunterPRO
{
    public class AH_DependencyGraphWindow : EditorWindow
    {
        private static AH_DependencyGraphWindow window;
        [SerializeField] public AH_DependencyGraphManager dependencyGraphManager;

        private GUIContent lockedReference;
        private GUIContent unlockedReference;
        private GUIContent contentToggleRefsTo;
        private GUIContent contentToggleRefsFrom;

        [SerializeField] GUIContent guiContentRefresh;

        [SerializeField] SearchField searchField;
        private bool initialized;

        // Editor gameObjectEditor;
        private UnityEngine.Object previewObject;

        //UI Rect
        Vector2 uiStartPos = new Vector2(10, 50);
        [SerializeField] private bool seeRefsToInProject;
        [SerializeField] private bool seeRefsFromInProject;
        private Texture2D previewTexture;
        private static readonly string WINDOWNAME = "AH Dependency Graph";

        //Add menu named "Dependency Graph" to the window menu  
        [UnityEditor.MenuItem("Tools/Asset Hunter PRO/Dependency Graph", priority = AH_Window.WINDOWMENUITEMPRIO + 1)]
        [UnityEditor.MenuItem("Window/Heureka/Asset Hunter PRO/Dependency Graph _%#h", priority = AH_Window.WINDOWMENUITEMPRIO + 1)]
        public static void OpenDependencyGraph()
        {
            Init();
        }

        public static void Init()
        {
            window = AH_DependencyGraphWindow.GetWindow<AH_DependencyGraphWindow>(WINDOWNAME, true);
            if (window.dependencyGraphManager == null)
                window.dependencyGraphManager = AH_DependencyGraphManager.instance;

            window.initializeGUIContent();
        }

        public static void Init(Docker.DockPosition dockPosition = Docker.DockPosition.Right)
        {
            Init();

            AH_Window[] mainWindows = Resources.FindObjectsOfTypeAll<AH_Window>();
            if (mainWindows.Length != 0)
            {
                HeurekaGames.Docker.Dock(mainWindows[0], window, dockPosition);
            }
        }

        private void OnEnable()
        {
            Selection.selectionChanged += OnSelectionChanged;
            EditorApplication.projectChanged += EditorApplication_projectChanged;
            EditorApplication.projectWindowItemOnGUI += EditorApplication_ProjectWindowItemCallback;

            contentToggleRefsFrom = new GUIContent(EditorGUIUtility.IconContent("sv_icon_dot9_sml"));
            contentToggleRefsFrom.text = "Is dependency";

            contentToggleRefsTo = new GUIContent(EditorGUIUtility.IconContent("sv_icon_dot12_sml"));
            contentToggleRefsTo.text = "Has dependencies";

            lockedReference = new GUIContent()
            {
                tooltip = "Target Asset is locked, click to unlock",
                image = EditorGUIUtility.IconContent("LockIcon-On").image
            };
            unlockedReference = new GUIContent()
            {
                tooltip = "Target Asset is unlocked, click to lock",
                image = EditorGUIUtility.IconContent("LockIcon").image
            };
            seeRefsToInProject = EditorPrefs.GetBool("AHP_seeRefsToInProject", true);
            seeRefsFromInProject = EditorPrefs.GetBool("AHP_seeRefsFromInProject", true);
        }

        void EditorApplication_ProjectWindowItemCallback(string guid, Rect r)
        {
            //If nothing references this asset, ignore it
            if (!seeRefsFromInProject && !seeRefsToInProject)
                return;

            var frame = new Rect(r);
            frame.x += frame.width;

            if (seeRefsFromInProject && dependencyGraphManager!=null && dependencyGraphManager.GetReferencesFrom().ContainsKey(guid))
            {
                frame.x += -12;
                frame.width += 10f;

                GUI.Label(frame, contentToggleRefsFrom.image, EditorStyles.miniLabel);
            }
            if (seeRefsToInProject && dependencyGraphManager.GetReferencesTo().ContainsKey(guid))
            {
                frame.x += -12f;
                frame.width += 10f;

                GUI.Label(frame, contentToggleRefsTo.image, EditorStyles.miniLabel);
            }
        }

        private void EditorApplication_projectChanged()
        {
            dependencyGraphManager.IsDirty = true;
            dependencyGraphManager.ResetHistory();
        }

        private void OnGUI()
        {
            initIfNeeded();
            doHeader();

            if (dependencyGraphManager != null)
            {
                //If window has no cached data
                if (!dependencyGraphManager.HasCache())
                {
                    Heureka_WindowStyler.DrawCenteredMessage(window, AH_EditorData.Instance.RefFromWhiteIcon.Icon, 240f, 110f, "No Graph" + Environment.NewLine + "Build Graph");
                    EditorGUILayout.BeginVertical();
                    GUILayout.FlexibleSpace();
                    Color origClr = GUI.backgroundColor;

                    GUI.backgroundColor = Heureka_WindowStyler.clr_Red;
                    if (GUILayout.Button("Build Graph", GUILayout.Height(40)))
                    {
                        dependencyGraphManager.RefreshReferenceGraph();
                    }
                    GUI.backgroundColor = origClr;
                    EditorGUILayout.EndVertical();
                    return;
                }

                if (dependencyGraphManager.HasSelection)
                {
                    using (new EditorGUILayout.VerticalScope("box"))
                    {
                        using (new EditorGUILayout.HorizontalScope())
                        {
                            if (GUILayout.Button($"{dependencyGraphManager.GetSelectedName()}" + (dependencyGraphManager.LockedSelection ? " (Locked)" : ""), GUILayout.ExpandWidth(true)))
                                EditorGUIUtility.PingObject(dependencyGraphManager.SelectedAsset);

                            if (GUILayout.Button(dependencyGraphManager.LockedSelection ? lockedReference : unlockedReference, EditorStyles.boldLabel, GUILayout.ExpandWidth(false)))
                                dependencyGraphManager.LockedSelection = !dependencyGraphManager.LockedSelection;
                        }
                        drawPreview();
                    }

                    var viewFrom = dependencyGraphManager.GetTreeViewFrom();
                    bool isValidFrom = (viewFrom?.treeModel?.numberOfDataElements > 1);

                    var viewTo = dependencyGraphManager.GetTreeViewTo();
                    bool isValidTo = (viewTo?.treeModel?.numberOfDataElements > 1);

                    using (new EditorGUILayout.VerticalScope("box", GUILayout.ExpandWidth(true)))
                    {
                        using (new EditorGUILayout.HorizontalScope("box", GUILayout.ExpandWidth(true)))
                        {
                            GUILayout.Label(AH_EditorData.Instance.RefFromIcon.Icon, GUILayout.Width(32), GUILayout.Height(32));
                            using (new EditorGUILayout.VerticalScope(GUILayout.Height(32)))
                            {
                                GUILayout.FlexibleSpace();
                                EditorGUILayout.LabelField($"A dependency of {(isValidFrom ? (viewFrom.treeModel.root.children.Count()) : 0)}", EditorStyles.boldLabel);
                                GUILayout.FlexibleSpace();
                            }
                        }
                        if (isValidFrom)
                            drawAssetList(dependencyGraphManager.GetTreeViewFrom());

                        using (new EditorGUILayout.HorizontalScope("box", GUILayout.ExpandWidth(true)))
                        {
                            GUILayout.Label(AH_EditorData.Instance.RefToIcon.Icon, GUILayout.Width(32), GUILayout.Height(32));
                            using (new EditorGUILayout.VerticalScope(GUILayout.Height(32)))
                            {
                                GUILayout.FlexibleSpace();
                                EditorGUILayout.LabelField($"Depends on {(isValidTo ? (viewTo.treeModel.root.children.Count()) : 0)}", EditorStyles.boldLabel);
                                GUILayout.FlexibleSpace();
                            }
                        }
                        if (isValidTo)
                            drawAssetList(dependencyGraphManager.GetTreeViewTo());

                        //Force flexible size here to make sure the preview area doesn't fill entire window
                        if (!isValidTo && !isValidFrom)
                            GUILayout.FlexibleSpace();
                    }
                }
                else
                {
                    Heureka_WindowStyler.DrawCenteredMessage(window, AH_EditorData.Instance.RefFromWhiteIcon.Icon, 240f, 110f, "No selection" + Environment.NewLine + "Select asset in project view");
                }
            }
            doFooter();
            //Make sure this window has focus to update contents
            Repaint();
        }

        private void drawPreview()
        {
            if (dependencyGraphManager.SelectedAsset != null)
            {
                var old = previewObject;
                previewObject = dependencyGraphManager.SelectedAsset;
                //if (previewObject != old)
                {
                    previewTexture = AssetPreview.GetAssetPreview(previewObject); //Asnyc, so we have to do this each frame
                    if (previewTexture == null)
                        previewTexture = AssetPreview.GetMiniThumbnail(previewObject);
                }
                using (new EditorGUILayout.VerticalScope("box"))
                {
                    EditorGUILayout.BeginHorizontal();
                    drawHistoryButton(-1);
                    GUILayout.FlexibleSpace();
                    if (GUILayout.Button(previewTexture, EditorStyles.boldLabel, /*GUILayout.Width(64),*/ GUILayout.MaxHeight(64), GUILayout.ExpandWidth(true)))
                    {
                        EditorGUIUtility.PingObject(previewObject);
                    }
                    GUILayout.FlexibleSpace();
                    drawHistoryButton(1);
                    EditorGUILayout.EndHorizontal();
                }
            }
        }

        private void drawHistoryButton(int direction)
        {
            if (!dependencyGraphManager.LockedSelection)
            {
                var style = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter };
                string tooltip;
                bool validDirection = dependencyGraphManager.HasHistory(direction, out tooltip);

                EditorGUI.BeginDisabledGroup(!validDirection);
                var content = new GUIContent(validDirection ? direction == -1 ? "<" : ">" : string.Empty);
                if (!string.IsNullOrEmpty(tooltip))
                    content.tooltip = tooltip;

                if (GUILayout.Button(content, style, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(false), GUILayout.Width(12)))
                {
                    if (direction == -1)
                        dependencyGraphManager.SelectPreviousFromHistory();
                    else if (direction == 1)
                        dependencyGraphManager.SelectNextFromHistory();
                    else
                        Debug.LogWarning("Wrong integer. You must select -1 or 1");
                }
                EditorGUI.EndDisabledGroup();
            }
        }

        private void initIfNeeded()
        {
            if (!dependencyGraphManager || !window)
                Init();

            if (dependencyGraphManager.RequiresRefresh())
                OnSelectionChanged();

            //We dont need to do stuff when in play mode
            if (dependencyGraphManager && !initialized)
            {
                if (searchField == null)
                    searchField = new SearchField();

                dependencyGraphManager.Initialize(searchField, multiColumnTreeViewRect);
                initialized = true;

                InternalEditorUtility.RepaintAllViews();
            }

            //This is an (ugly) fix to make sure we dotn loose our icons due to some singleton issues after play/stop
            if (guiContentRefresh.image == null)
                initializeGUIContent();
        }

        private void initializeGUIContent()
        {
            titleContent = new GUIContent(WINDOWNAME, AH_EditorData.Instance.RefFromIcon.Icon);
            guiContentRefresh = new GUIContent(AH_EditorData.Instance.RefreshIcon.Icon, "Refresh data");
        }

        private void doFooter()
        {
            if (dependencyGraphManager != null)
            {
                if (!dependencyGraphManager.HasSelection)
                    GUILayout.FlexibleSpace();

                GUIContent RefreshGUIContent = new GUIContent(guiContentRefresh);
                Color origColor = GUI.color;
                if (dependencyGraphManager.IsDirty)
                {
                    GUI.color = Heureka_WindowStyler.clr_Red;
                    RefreshGUIContent.tooltip = String.Format("{0}{1}", RefreshGUIContent.tooltip, " (Project has changed which means that treeview is out of date)");
                }

                if (AH_UIUtilities.DrawSelectionButton(RefreshGUIContent))
                    dependencyGraphManager.RefreshReferenceGraph();

                GUI.color = origColor;

                EditorGUILayout.BeginHorizontal();
                EditorGUI.BeginChangeCheck();
                seeRefsToInProject = GUILayout.Toggle(seeRefsToInProject, contentToggleRefsTo);
                seeRefsFromInProject = GUILayout.Toggle(seeRefsFromInProject, contentToggleRefsFrom);
                //Do we need to repaint projewct view?
                if (EditorGUI.EndChangeCheck())
                {
                    EditorPrefs.SetBool("AHP_seeRefsToInProject", seeRefsToInProject);
                    EditorPrefs.SetBool("AHP_seeRefsFromInProject", seeRefsFromInProject);
                    InternalEditorUtility.RepaintAllViews();
                }

                EditorGUILayout.EndHorizontal();
            }
        }

        private void doHeader()
        {
            Heureka_WindowStyler.DrawGlobalHeader(Heureka_WindowStyler.clr_lBlue, WINDOWNAME);

            bool hasReferenceGraph = (dependencyGraphManager != null);
            if (hasReferenceGraph)
            {
                if (dependencyGraphManager.HasSelection && dependencyGraphManager.HasCache())
                {
                    EditorGUILayout.BeginHorizontal(GUILayout.Height(AH_Window.ButtonMaxHeight));
                    doSearchBar(searchBar);
                    GUILayout.FlexibleSpace();
                    EditorGUILayout.EndHorizontal();
                }
            }
        }

        private void drawAssetList(TreeView view)
        {
            if (view != null)
            {
                var rect = EditorGUILayout.BeginVertical();
                GUILayout.FlexibleSpace();
                view.OnGUI(rect);
                EditorGUILayout.EndVertical();
            }
        }
        /*private bool doSelectionButton(GUIContent content)
        {
            GUIContent btnContent = new GUIContent(content);
            if (AH_SettingsManager.Instance.HideButtonText)
                btnContent.text = null;

            return GUILayout.Button(btnContent, GUILayout.MaxHeight(AH_SettingsManager.Instance.HideButtonText ? AH_Window.ButtonMaxHeight * 2f : AH_Window.ButtonMaxHeight));
        }*/

        void doSearchBar(Rect rect)
        {
            if (searchField != null)
                dependencyGraphManager.SearchString = searchField.OnGUI(rect, dependencyGraphManager.SearchString);
        }

        private void OnSelectionChanged()
        {
            if (!dependencyGraphManager.LockedSelection)
            {
                dependencyGraphManager.UpdateSelectedAsset((Selection.activeObject) ? Selection.activeObject : null);
                initialized = false;
            }
        }

        Rect searchBar
        {
            get { return new Rect(uiStartPos.x + AH_Window.ButtonMaxHeight, uiStartPos.y - (AH_Window.ButtonMaxHeight + 6), position.width - (uiStartPos.x * 2) - AH_Window.ButtonMaxHeight * 2, AH_Window.ButtonMaxHeight); }
        }

        Rect multiColumnTreeViewRect
        {
            get
            {
                Rect newRect = new Rect(uiStartPos.x, uiStartPos.y + 20 + (AH_SettingsManager.Instance.HideButtonText ? 20 : 0), position.width - (uiStartPos.x * 2), position.height - 90 - (AH_SettingsManager.Instance.HideButtonText ? 20 : 0));
                return newRect;
            }
        }

        private void OnDisable()
        {
            Selection.selectionChanged -= OnSelectionChanged;
            EditorApplication.projectChanged -= EditorApplication_projectChanged;
            EditorApplication.projectWindowItemOnGUI -= EditorApplication_ProjectWindowItemCallback;
        }

        private void OnDestroy()
        {
            DestroyImmediate(dependencyGraphManager);
        }
    }
}
