using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

namespace Unity.UI.Builder
{
    internal class BuilderInspectorHeader
    {
        // Shared variables regardless of selected type
        BuilderInspector m_Inspector;
        BuilderSelection m_Selection;
        VisualElement m_Header;

        VisualElement m_InnerHeader;
        VisualElement m_Icon;

        FieldStatusIndicator m_StatusIndicator;
        Label m_Pill;
        TextField m_TextField;

        UnityEngine.UIElements.HelpBox m_EditorWarningHelpBox;
        VisualElement m_ErrorIcon;

        VisualElement currentVisualElement => m_Inspector.currentVisualElement;

        StyleSheet styleSheet => m_Inspector.styleSheet;
        public VisualElement header => m_Header;

        // Store callbacks to reduce delegate allocations
       EventCallback<ChangeEvent<string>> m_ElementNameChangeCallback;
       EventCallback<ChangeEvent<string>> m_SelectorNameChangeCallback;
       EventCallback<KeyDownEvent> m_SelectorEnterKeyDownCallback;

       IManipulator m_RightClickManipulator;

        public BuilderInspectorHeader(BuilderInspector inspector)
        {
            m_Inspector = inspector;
            m_Selection = inspector.selection;
            m_Header = m_Inspector.Q<VisualElement>("header-container");
            m_StatusIndicator = m_Header.Q<FieldStatusIndicator>("header-field-status-indicator");
            m_StatusIndicator.populateMenuItems = (menu) =>
            {
                BuildNameFieldContextualMenu(menu, m_TextField);
            };

            m_InnerHeader = m_Inspector.Q<VisualElement>("header-container-minor");
            m_Icon = m_Inspector.Q<VisualElement>("header-icon");

            m_Pill = m_Inspector.Q<Label>("header-selected-pill");
            m_Pill.AddToClassList("unity-builder-tag-pill");

            m_TextField = m_Inspector.Q<TextField>("header-selected-text-field");
            m_TextField.isDelayed = true;
            m_TextField.tooltip = "name";
            m_TextField.bindingPath = "name";

            m_EditorWarningHelpBox = m_Inspector.Q<UnityEngine.UIElements.HelpBox>("header-editor-warning-help-box");
            m_EditorWarningHelpBox.text = BuilderConstants.HeaderSectionHelpBoxMessage;
            m_ErrorIcon = m_Inspector.Q<VisualElement>("header-error-icon");

            // Warning must be hidden at first
            m_ErrorIcon.style.backgroundImage = EditorGUIUtility.LoadIcon("console.erroricon");
            AdjustBottomPadding(false);

            // Store callbacks to reduce delegate allocations
            m_ElementNameChangeCallback = OnNameAttributeChange;
            m_SelectorNameChangeCallback = OnStyleSelectorNameChange;
            m_SelectorEnterKeyDownCallback = OnEnterStyleSelectorNameChange;

            m_TextField.RegisterValueChangedCallback(m_ElementNameChangeCallback);
            m_RightClickManipulator = new ContextualMenuManipulator(BuildNameFieldContextualMenu);
        }

        public void Refresh()
        {
            if (currentVisualElement == null)
            {
                return;
            }

            if (m_Selection.selectionType == BuilderSelectionType.Nothing)
            {
                return;
            }

            m_TextField.UnregisterValueChangedCallback(m_SelectorNameChangeCallback);
            m_TextField.UnregisterCallback(m_SelectorEnterKeyDownCallback);
            m_TextField.UnregisterValueChangedCallback(m_ElementNameChangeCallback);
            m_TextField.RemoveManipulator(m_RightClickManipulator);

            if (m_Selection.selectionType == BuilderSelectionType.StyleSelector ||
                m_Selection.selectionType == BuilderSelectionType.ParentStyleSelector)
            {
                m_TextField.RegisterValueChangedCallback(m_SelectorNameChangeCallback);
                m_TextField.RegisterCallback(m_SelectorEnterKeyDownCallback, TrickleDown.TrickleDown);

                ToggleNameOverrideBox(false);

                m_StatusIndicator.style.visibility = Visibility.Hidden;

                (m_TextField.labelElement as INotifyValueChanged<string>).SetValueWithoutNotify(BuilderConstants.BuilderInspectorSelector);

                m_TextField.SetValueWithoutNotify(BuilderSharedStyles.GetSelectorString(currentVisualElement));

                m_Icon.style.backgroundImage = EditorGUIUtility.Load("StyleSheet Icon") as Texture2D;
            }
            else
            {
                ToggleNameOverrideBox(!string.IsNullOrEmpty(currentVisualElement.name));
                m_StatusIndicator.style.visibility = Visibility.Visible;
                m_TextField.SetValueWithoutNotify(currentVisualElement.name);
                SetTypeAndIcon();

                if (m_Selection.selectionType == BuilderSelectionType.Element)
                {
                    m_TextField.RegisterValueChangedCallback(m_ElementNameChangeCallback);
                    m_TextField.AddManipulator(m_RightClickManipulator);
                }
            }

            var currentElementType = currentVisualElement.GetType();
            var isEditorOnlyElement = false;

            if (currentElementType.Namespace != null)
            {
                isEditorOnlyElement = currentElementType.Namespace.Contains("UnityEditor");
            }

            m_Pill.style.display = isEditorOnlyElement ? DisplayStyle.Flex : DisplayStyle.None;
            AdjustBottomPadding(isEditorOnlyElement && !m_Inspector.document.fileSettings.editorExtensionMode);
        }

        private void SetTypeAndIcon()
        {
            string typeName;

            if (currentVisualElement.typeName == nameof(TemplateContainer))
            {
                typeName = BuilderConstants.BuilderInspectorTemplateInstance;
                m_Icon.style.backgroundImage = EditorGUIUtility.Load("VisualTreeAsset Icon") as Texture2D;
            }
            else
            {
                typeName = currentVisualElement.typeName;
                m_Icon.style.backgroundImage =
                    BuilderLibraryContent.GetTypeLibraryLargeIcon(currentVisualElement.GetType());
            }

            (m_TextField.labelElement as INotifyValueChanged<string>).SetValueWithoutNotify(typeName);
        }

        private void BuildNameFieldContextualMenu(DropdownMenu menu, object target)
        {
            menu.AppendAction(
                BuilderConstants.ContextMenuUnsetMessage,
                UnsetName,
                action =>
                {
                    var vea = currentVisualElement.GetVisualElementAsset();

                    return vea != null && vea.HasAttribute("name")
                        ? DropdownMenuAction.Status.Normal
                        : DropdownMenuAction.Status.Disabled;
                },
                target);
        }

        private void BuildNameFieldContextualMenu(ContextualMenuPopulateEvent evt)
        {
            BuildNameFieldContextualMenu(evt.menu, evt.target);
        }

        private void UnsetName(DropdownMenuAction action)
        {
            // Undo/Redo
            Undo.RegisterCompleteObjectUndo(m_Inspector.visualTreeAsset,
                BuilderConstants.ChangeAttributeValueUndoMessage);

            // Unset value in asset.
            var vea = currentVisualElement.GetVisualElementAsset();

            vea.RemoveAttribute("name");

            m_TextField.SetValueWithoutNotify(string.Empty);

            m_Inspector.CallInitOnElement();

            // Notify of changes.
            m_Selection.NotifyOfHierarchyChange(m_Inspector);

            ToggleNameOverrideBox(false);
        }

        void OnNameAttributeChange(ChangeEvent<string> evt)
        {
            if (m_Selection.selectionType == BuilderSelectionType.Element)
            {
                m_Inspector.attributesSection.OnValidatedAttributeValueChange(evt, BuilderNameUtilities.attributeRegex,
                    BuilderConstants.AttributeValidationSpacialCharacters);
                ToggleNameOverrideBox(true);
            }
        }

        void OnStyleSelectorNameChange(ChangeEvent<string> evt)
        {
            if (evt.leafTarget != m_TextField)
                return;
            
            if (m_Selection.selectionType != BuilderSelectionType.StyleSelector)
                return;

            if (evt.newValue.Length == 0)
            {
                Refresh();
                return;
            }

            if (evt.newValue == evt.previousValue)
                return;

            if (!ValidateStyleSelectorNameChange(evt.newValue))
                evt.StopImmediatePropagation();
        }

        void OnEnterStyleSelectorNameChange(KeyDownEvent evt)
        {
            if (evt.keyCode != KeyCode.Return && evt.keyCode != KeyCode.KeypadEnter)
                return;

            if (m_Selection.selectionType != BuilderSelectionType.StyleSelector)
                return;

            if (m_TextField.text.Length == 0)
            {
                Refresh();
                return;
            }

            if (m_TextField.text == currentVisualElement.name)
                return;

            if (!ValidateStyleSelectorNameChange(m_TextField.text))
            {
                evt.PreventDefault();
                evt.StopImmediatePropagation();    
            }
        }

        bool ValidateStyleSelectorNameChange(string value)
        {
            if (!BuilderNameUtilities.styleSelectorRegex.IsMatch(value))
            {
                Builder.ShowWarning(string.Format(BuilderConstants.StyleSelectorValidationSpacialCharacters, "Name"));
                m_TextField.schedule.Execute(() =>
                {
                    m_TextField.SetValueWithoutNotify(BuilderSharedStyles.GetSelectorString(currentVisualElement));
                    m_TextField.focusController?.focusedElement?.Blur();
                    m_TextField.textInputBase.Focus();
                    m_TextField.text = value;
                    m_TextField.SelectAll();
                });
                return false;
            }

            Undo.RegisterCompleteObjectUndo(
                styleSheet, BuilderConstants.RenameSelectorUndoMessage);

            if (!BuilderSharedStyles.SetSelectorString(currentVisualElement, styleSheet, value, out var error))
            {
                Builder.ShowWarning(error);
                m_TextField.schedule.Execute(() =>
                {
                    m_TextField.SetValueWithoutNotify(BuilderSharedStyles.GetSelectorString(currentVisualElement));
                    m_TextField.focusController?.focusedElement?.Blur();
                    m_TextField.textInputBase.Focus();
                    m_TextField.text = value;
                    m_TextField.textSelection.SelectAll();
                });
                return false;
            }

            m_Selection.NotifyOfHierarchyChange(m_Inspector);
            m_Selection.NotifyOfStylingChange(m_Inspector);
            return true;
        }

        public void Enable()
        {
            m_Header.SetEnabled(true);
        }

        public void Disable()
        {
            m_Header.SetEnabled(false);
        }

        void AdjustBottomPadding(bool isHelpBoxShowing)
        {
            if (isHelpBoxShowing)
            {
                m_EditorWarningHelpBox.style.display = DisplayStyle.Flex;
                m_ErrorIcon.style.display = DisplayStyle.Flex;
                m_Header.style.paddingBottom = 0;
            }
            else
            {
                m_EditorWarningHelpBox.style.display = DisplayStyle.None;
                m_ErrorIcon.style.display = DisplayStyle.None;
                m_Header.style.paddingBottom = 12;
            }
        }

        void ToggleNameOverrideBox(bool isOverridden)
        {
            m_InnerHeader.EnableInClassList(BuilderConstants.InspectorLocalStyleOverrideClassName, isOverridden);
            m_StatusIndicator.tooltip = isOverridden
                ? BuilderConstants.FieldStatusIndicatorInlineTooltip
                : BuilderConstants.FieldStatusIndicatorDefaultTooltip;
        }
    }
}
