#if UNITY_EDITOR
using System;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;

namespace UnityEngine.InputSystem.Editor
{
    internal class AdvancedDropdownGUI
    {
        private static class Styles
        {
            public static readonly GUIStyle toolbarSearchField = "ToolbarSeachTextField";
            public static readonly GUIStyle itemStyle = new GUIStyle("PR Label")
                .WithAlignment(TextAnchor.MiddleLeft)
                .WithPadding(new RectOffset())
                .WithMargin(new RectOffset())
                .WithFixedHeight(17);
            public static readonly GUIStyle richTextItemStyle = new GUIStyle("PR Label")
                .WithAlignment(TextAnchor.MiddleLeft)
                .WithPadding(new RectOffset())
                .WithMargin(new RectOffset())
                .WithFixedHeight(17)
                .WithRichText();
            public static readonly GUIStyle header = new GUIStyle("In BigTitle")
                .WithFont(EditorStyles.boldLabel.font)
                .WithMargin(new RectOffset())
                .WithBorder(new RectOffset(0, 0, 3, 3))
                .WithPadding(new RectOffset(6, 6, 6, 6))
                .WithContentOffset(Vector2.zero);
            public static readonly GUIStyle headerArrow = new GUIStyle()
                .WithAlignment(TextAnchor.MiddleCenter)
                .WithFontSize(20)
                .WithNormalTextColor(Color.gray);
            public static readonly GUIStyle checkMark = new GUIStyle("PR Label")
                .WithAlignment(TextAnchor.MiddleCenter)
                .WithPadding(new RectOffset())
                .WithMargin(new RectOffset())
                .WithFixedHeight(17);
            public static readonly GUIContent arrowRightContent = new GUIContent("▸");
            public static readonly GUIContent arrowLeftContent = new GUIContent("◂");
        }

        //This should ideally match line height
        private static readonly Vector2 s_IconSize = new Vector2(13, 13);

        internal Rect m_SearchRect;
        internal Rect m_HeaderRect;
        private bool m_FocusSet;

        internal virtual float searchHeight => m_SearchRect.height;

        internal virtual float headerHeight => m_HeaderRect.height;

        internal virtual GUIStyle lineStyle => Styles.itemStyle;
        internal virtual GUIStyle richTextLineStyle => Styles.richTextItemStyle;
        internal GUIStyle headerStyle => Styles.header;

        internal virtual Vector2 iconSize => s_IconSize;

        internal AdvancedDropdownState state { get; set; }

        private readonly SearchField m_SearchField = new SearchField();

        public void Init()
        {
            m_FocusSet = false;
        }

        private const float k_IndentPerLevel = 20f;

        internal virtual void BeginDraw(EditorWindow window)
        {
        }

        internal virtual void EndDraw(EditorWindow window)
        {
        }

        internal virtual void DrawItem(AdvancedDropdownItem item, string name, Texture2D icon, bool enabled,
            bool drawArrow, bool selected, bool hasSearch, bool richText = false)
        {
            var content = new GUIContent(name, icon);
            var imgTemp = content.image;
            //we need to pretend we have an icon to calculate proper width in case
            if (content.image == null)
                content.image = Texture2D.whiteTexture;
            var style = richText ? richTextLineStyle : lineStyle;
            var rect = GUILayoutUtility.GetRect(content, style, GUILayout.ExpandWidth(true));
            content.image = imgTemp;

            if (Event.current.type != EventType.Repaint)
                return;

            style.Draw(rect, GUIContent.none, false, false, selected, selected);
            if (!hasSearch)
            {
                rect.x += item.indent * k_IndentPerLevel;
                rect.width -= item.indent * k_IndentPerLevel;
            }

            var imageTemp = content.image;
            if (content.image == null)
            {
                style.Draw(rect, GUIContent.none, false, false, selected, selected);
                rect.x += iconSize.x + 1;
                rect.width -= iconSize.x + 1;
            }
            rect.x += EditorGUIUtility.standardVerticalSpacing;
            rect.width -= EditorGUIUtility.standardVerticalSpacing;
            EditorGUI.BeginDisabledGroup(!enabled);
            style.Draw(rect, content, false, false, selected, selected);
            content.image = imageTemp;
            if (drawArrow)
            {
                var size = style.lineHeight;
                var arrowRect = new Rect(rect.x + rect.width - size, rect.y, size, size);
                style.Draw(arrowRect, Styles.arrowRightContent, false, false, false, false);
            }
            EditorGUI.EndDisabledGroup();
        }

        internal virtual void DrawHeader(AdvancedDropdownItem group, Action backButtonPressed, bool hasParent)
        {
            var content = new GUIContent(group.name, group.icon);
            m_HeaderRect = GUILayoutUtility.GetRect(content, Styles.header, GUILayout.ExpandWidth(true));

            if (Event.current.type == EventType.Repaint)
                Styles.header.Draw(m_HeaderRect, content, false, false, false, false);

            // Back button
            if (hasParent)
            {
                var arrowWidth = 13;
                var arrowRect = new Rect(m_HeaderRect.x, m_HeaderRect.y, arrowWidth, m_HeaderRect.height);
                if (Event.current.type == EventType.Repaint)
                    Styles.headerArrow.Draw(arrowRect, Styles.arrowLeftContent, false, false, false, false);
                if (Event.current.type == EventType.MouseDown && m_HeaderRect.Contains(Event.current.mousePosition))
                {
                    backButtonPressed();
                    Event.current.Use();
                }
            }
        }

        internal virtual void DrawFooter(AdvancedDropdownItem selectedItem)
        {
        }

        internal void DrawSearchField(bool isSearchFieldDisabled, string searchString, Action<string> searchChanged)
        {
            if (!isSearchFieldDisabled && !m_FocusSet)
            {
                m_FocusSet = true;
                m_SearchField.SetFocus();
            }

            using (new EditorGUI.DisabledScope(isSearchFieldDisabled))
            {
                var newSearch = DrawSearchFieldControl(searchString);

                if (newSearch != searchString)
                {
                    searchChanged(newSearch);
                }
            }
        }

        internal virtual string DrawSearchFieldControl(string searchString)
        {
            var paddingX = 8f;
            var paddingY = 2f;
            var rect = GUILayoutUtility.GetRect(0, 0, Styles.toolbarSearchField);
            //rect.x += paddingX;
            rect.y += paddingY + 1; // Add one for the border
            rect.height += Styles.toolbarSearchField.fixedHeight + paddingY * 3;
            rect.width -= paddingX;// * 2;
            m_SearchRect = rect;
            searchString = m_SearchField.OnToolbarGUI(m_SearchRect, searchString);
            return searchString;
        }

        internal Rect GetAnimRect(Rect position, float anim)
        {
            // Calculate rect for animated area
            var rect = new Rect(position);
            rect.x = position.x + position.width * anim;
            rect.y += searchHeight;
            rect.height -= searchHeight;
            return rect;
        }

        internal Vector2 CalculateContentSize(AdvancedDropdownDataSource dataSource)
        {
            var maxWidth = 0f;
            var maxHeight = 0f;
            var includeArrow = false;
            var arrowWidth = 0f;

            foreach (var child in dataSource.mainTree.children)
            {
                var content = new GUIContent(child.name, child.icon);
                var a = lineStyle.CalcSize(content);
                a.x += iconSize.x + 1;

                if (maxWidth < a.x)
                {
                    maxWidth = a.x + 1;
                    includeArrow |= child.children.Any();
                }
                if (child.IsSeparator())
                {
                    maxHeight += GUIHelpers.Styles.lineSeparator.CalcHeight(content, maxWidth) + GUIHelpers.Styles.lineSeparator.margin.vertical;
                }
                else
                {
                    maxHeight += lineStyle.CalcHeight(content, maxWidth);
                }
                if (arrowWidth == 0)
                {
                    lineStyle.CalcMinMaxWidth(Styles.arrowRightContent, out arrowWidth, out arrowWidth);
                }
            }
            if (includeArrow)
            {
                maxWidth += arrowWidth;
            }
            return new Vector2(maxWidth, maxHeight);
        }

        internal float GetSelectionHeight(AdvancedDropdownDataSource dataSource, Rect buttonRect)
        {
            if (state.GetSelectedIndex(dataSource.mainTree) == -1)
                return 0;
            var height = 0f;
            for (var i = 0; i < dataSource.mainTree.children.Count(); i++)
            {
                var child = dataSource.mainTree.children.ElementAt(i);
                var content = new GUIContent(child.name, child.icon);
                if (state.GetSelectedIndex(dataSource.mainTree) == i)
                {
                    var diff = (lineStyle.CalcHeight(content, 0) - buttonRect.height) / 2f;
                    return height + diff;
                }
                if (child.IsSeparator())
                {
                    height += GUIHelpers.Styles.lineSeparator.CalcHeight(content, 0) + GUIHelpers.Styles.lineSeparator.margin.vertical;
                }
                else
                {
                    height += lineStyle.CalcHeight(content, 0);
                }
            }
            return height;
        }
    }
}

#endif // UNITY_EDITOR
