// Copyright (c) Rotorz Limited. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root.

using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;

namespace Unity.VisualScripting.ReorderableList.Internal
{
    /// <summary>
    /// Utility functions to assist with GUIs.
    /// </summary>
    /// <exclude />
    public static class GUIHelper
    {
        static GUIHelper()
        {
            var tyGUIClip = typeof(GUIUtility).Assembly.GetType("UnityEngine.GUIClip", true);
            if (tyGUIClip != null)
            {
                var piVisibleRect = tyGUIClip.GetProperty("visibleRect", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                if (piVisibleRect != null)
                {
                    VisibleRect = (Func<Rect>)Delegate.CreateDelegate(typeof(Func<Rect>), piVisibleRect.GetGetMethod(true));
                }
            }

            var miFocusTextInControl = typeof(EditorGUI).GetMethod("FocusTextInControl", BindingFlags.Static | BindingFlags.Public);
            if (miFocusTextInControl == null)
            {
                miFocusTextInControl = typeof(GUI).GetMethod("FocusControl", BindingFlags.Static | BindingFlags.Public);
            }

            FocusTextInControl = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), miFocusTextInControl);

            s_SeparatorColor = EditorGUIUtility.isProSkin
                ? new Color(0.11f, 0.11f, 0.11f)
                : new Color(0.5f, 0.5f, 0.5f);

            s_SeparatorStyle = new GUIStyle();
            s_SeparatorStyle.normal.background = EditorGUIUtility.whiteTexture;
            s_SeparatorStyle.stretchWidth = true;
        }

        /// <summary>
        /// Gets visible rectangle within GUI.
        /// </summary>
        /// <remarks>
        ///     <para>VisibleRect = TopmostRect + scrollViewOffsets</para>
        /// </remarks>
        public static Func<Rect> VisibleRect;

        /// <summary>
        /// Focus control and text editor where applicable.
        /// </summary>
        public static Action<string> FocusTextInControl;

        private static GUIStyle s_TempStyle = new GUIStyle();

        private static GUIContent s_TempIconContent = new GUIContent();
        private static readonly int s_IconButtonHint = "_ReorderableIconButton_".GetHashCode();

        private static readonly Color s_SeparatorColor;
        private static readonly GUIStyle s_SeparatorStyle;

        /// <summary>
        /// Draw texture using <see cref="GUIStyle" /> to workaround bug in Unity where
        /// <see cref="GUI.DrawTexture" /> flickers when embedded inside a property drawer.
        /// </summary>
        /// <param name="position">Position of which to draw texture in space of GUI.</param>
        /// <param name="texture">Texture.</param>
        public static void DrawTexture(Rect position, Texture2D texture)
        {
            if (Event.current.type != EventType.Repaint)
            {
                return;
            }

            s_TempStyle.normal.background = texture;

            s_TempStyle.Draw(position, GUIContent.none, false, false, false, false);
        }

        public static bool IconButton(Rect position, bool visible, Texture2D iconNormal, Texture2D iconActive, GUIStyle style)
        {
            var controlID = GUIUtility.GetControlID(s_IconButtonHint, FocusType.Passive);
            var result = false;

            position.height += 1;

            switch (Event.current.GetTypeForControl(controlID))
            {
                case EventType.MouseDown:
                    // Do not allow button to be pressed using right mouse button since
                    // context menu should be shown instead!
                    if (GUI.enabled && Event.current.button != 1 && position.Contains(Event.current.mousePosition))
                    {
                        GUIUtility.hotControl = controlID;
                        GUIUtility.keyboardControl = 0;
                        Event.current.Use();
                    }
                    break;

                case EventType.MouseDrag:
                    if (GUIUtility.hotControl == controlID)
                    {
                        Event.current.Use();
                    }
                    break;

                case EventType.MouseUp:
                    if (GUIUtility.hotControl == controlID)
                    {
                        GUIUtility.hotControl = 0;
                        result = position.Contains(Event.current.mousePosition);
                        Event.current.Use();
                    }
                    break;

                case EventType.Repaint:
                    if (visible)
                    {
                        var isActive = GUIUtility.hotControl == controlID && position.Contains(Event.current.mousePosition);
                        s_TempIconContent.image = isActive ? iconActive : iconNormal;
                        position.height -= 1;
                        style.Draw(position, s_TempIconContent, isActive, isActive, false, false);
                    }
                    break;
            }

            return result;
        }

        public static bool IconButton(Rect position, Texture2D iconNormal, Texture2D iconActive, GUIStyle style)
        {
            return IconButton(position, true, iconNormal, iconActive, style);
        }

        public static void Separator(Rect position, Color color)
        {
            if (Event.current.type == EventType.Repaint)
            {
                var restoreColor = GUI.color;
                GUI.color = color;
                s_SeparatorStyle.Draw(position, false, false, false, false);
                GUI.color = restoreColor;
            }
        }

        public static void Separator(Rect position)
        {
            Separator(position, s_SeparatorColor);
        }
    }
}
