using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

[assembly: UxmlNamespacePrefix("UnityEditor.AddressableAssets.GUIElements", "AddressablesGUI")]
namespace UnityEditor.AddressableAssets.GUIElements
{
    internal class Ribbon : VisualElement
    {
        static class Content
        {
            // Technically Rbbon is a UI controll that could be more agnostic of this specific implementation for the Memory Profiler.
            // While this content may be reused in other places of the Memory Profiler and therefore makes sense to be located on TextContent,
            // keeping a Content class here makes it easier to keep things separated and helps when copying it out into other tools.
            public static readonly string OpenManualTooltip = GUIUtility.OpenManualTooltip;
        }
        static class Styles
        {
            public static readonly string HelpIconButtonClass = GUIUtility.HelpIconButtonClass;
            public static readonly string MenuIconButtonClass = GUIUtility.MenuIconButtonClass;
        }

        Align m_Alignment;
        public Align Alignment
        {
            get { return m_Alignment; }
            private set
            {
                switch (value)
                {
                    case Align.Auto:
                    case Align.FlexStart:
                        m_Alignment = value;
                        SetLeftAligned();
                        break;
                    case Align.Center:
                        m_Alignment = value;
                        SetCenterAligned();
                        break;
                    case Align.FlexEnd:
                    case Align.Stretch:
                    default:
                        Debug.LogError("Ribbons can only be left of center aligned");
                        break;
                }
            }
        }

        public bool ShowHelpButton { get; private set; }
        public bool ShowMenuButton { get; private set; }
        public int InitialOption { get; private set; }

        public int m_CurrentOption = 0;
        public int CurrentOption
        {
            get { return m_CurrentOption; }
            set { m_CurrentOption = value; RefreshButtonToggleStates(); }
        }

        public override VisualElement contentContainer
        {
            get { return m_Content; }
        }

        public event Action<int> Clicked = delegate { };
        public event Action HelpClicked = delegate { };
        public event Action MenuClicked = delegate { };

        VisualElement m_Root;
        VisualElement m_Content;
        VisualElement m_Container;
        List<RibbonButton> m_Buttons = new List<RibbonButton>();
        VisualElement m_OptionsAndInfos;
        Button m_Menu;
        Button m_Help;

        const string k_ContainerClassCenterAligned = "ribbon__container--centered";
        const string k_ClassCenterAligned = "ribbon--centered";
        const string k_ContainerClassLeftAligned = "ribbon__container--left-aligned";
        const string k_ClassLeftAligned = "ribbon--left-aligned";

        public Ribbon()
        {
            VisualTreeAsset ribbonViewTree = AssetDatabase.LoadAssetAtPath(GUIUtility.RibbonUxmlPath, typeof(VisualTreeAsset)) as VisualTreeAsset;

            m_Root = ribbonViewTree.Clone();

            // clear out the style sheets defined in the template uxml file so they can be applied from here in the order of: 1. theming, 2. base
            m_Root.styleSheets.Clear();
            var themeStyle = AssetDatabase.LoadAssetAtPath(EditorGUIUtility.isProSkin ? GUIUtility.RibbonDarkUssPath : GUIUtility.RibbonLightUssPath, typeof(StyleSheet)) as StyleSheet;
            m_Root.styleSheets.Add(themeStyle);

            var ribbonStyle = AssetDatabase.LoadAssetAtPath(GUIUtility.RibbonUssPath, typeof(StyleSheet)) as StyleSheet;
            m_Root.styleSheets.Add(ribbonStyle);

            hierarchy.Add(m_Root);

            style.flexShrink = 0;

            m_Container = m_Root.Q("ribbon__container");
            m_Content = m_Root.Q("ribbon__buttons");
            m_OptionsAndInfos = m_Root.Q("options-and-info");
            m_Menu = m_OptionsAndInfos.Q<Button>("", Styles.MenuIconButtonClass);
            m_Help = m_OptionsAndInfos.Q<Button>("", Styles.HelpIconButtonClass);
            m_Help.tooltip = Content.OpenManualTooltip;
            m_CurrentOption = InitialOption;
            Setup();
        }

        void Init()
        {
            m_CurrentOption = InitialOption;
            Setup();
        }

        public void Setup()
        {
            GatherElements();

            if (m_Buttons.Count > 0)
            {
                UnregisterCallback<GeometryChangedEvent>(OnPostDisplaySetup);
                SetupElements();
            }
            else
                RegisterCallback<GeometryChangedEvent>(OnPostDisplaySetup);
        }

        void GatherElements()
        {
            m_Buttons.Clear();
            if (m_Content == null)
                return;
            for (int i = 0; i < m_Content.childCount; i++)
            {
                if (m_Content[i] is RibbonButton)
                {
                    m_Buttons.Add(m_Content[i] as RibbonButton);
                }
            }
        }

        void OnPostDisplaySetup(GeometryChangedEvent evt)
        {
            GatherElements();
            if (m_Buttons.Count > 0)
            {
                UnregisterCallback<GeometryChangedEvent>(OnPostDisplaySetup);
                SetupElements();
            }
        }

        void SetupElements()
        {
            if (m_Alignment == Align.Center)
                SetCenterAligned();
            else
                SetLeftAligned();

            GUIUtility.SetVisibility(m_Help, ShowHelpButton);
            m_Help.clickable.clicked += HelpClicked;
            GUIUtility.SetVisibility(m_Menu, ShowMenuButton);
            m_Menu.clickable.clicked += MenuClicked;

            RefreshButtonToggleStates();
            for (int i = 0; i < m_Buttons.Count; i++)
            {
                // copy index value to local scope before enclosing to ensure it isn't Count-1
                int buttonIndex = i;
                m_Buttons[i].clickable.clicked += () => ButtonClicked(buttonIndex);
            }
        }

        void RefreshButtonToggleStates()
        {
            m_CurrentOption = Mathf.Clamp(m_CurrentOption, 0, Mathf.Max(0, m_Buttons.Count - 1));
            for (int i = 0; i < m_Buttons.Count; i++)
            {
                m_Buttons[i].Toggled = i == m_CurrentOption;
            }
        }

        public void ButtonClicked(int index)
        {
            m_CurrentOption = index;
            RefreshButtonToggleStates();
            Clicked(index);
        }

        void SetCenterAligned()
        {
            m_Container.SwitchClasses(classToAdd: k_ContainerClassCenterAligned, classToRemove: k_ContainerClassLeftAligned);
            m_Content.SwitchClasses(classToAdd: k_ClassCenterAligned, classToRemove: k_ClassLeftAligned);
        }

        void SetLeftAligned()
        {
            m_Container.SwitchClasses(classToAdd: k_ContainerClassLeftAligned, classToRemove: k_ContainerClassCenterAligned);
            m_Content.SwitchClasses(classToAdd: k_ClassLeftAligned, classToRemove: k_ClassCenterAligned);
        }

        /// <summary>
        /// Instantiates a <see cref="Ribbon"/> using the data read from a UXML file.
        /// </summary>
        public new class UxmlFactory : UxmlFactory<Ribbon, UxmlTraits> { }

        /// <summary>
        /// Defines <see cref="UxmlTraits"/> for the <see cref="Ribbon"/>.
        /// </summary>
        public new class UxmlTraits : VisualElement.UxmlTraits
        {
            UxmlEnumAttributeDescription<Align> m_Align = new UxmlEnumAttributeDescription<Align> { name = "alignment", defaultValue = Align.Center };
            UxmlBoolAttributeDescription m_ShowHelp = new UxmlBoolAttributeDescription { name = "show-help-button", defaultValue = false };
            UxmlBoolAttributeDescription m_ShowMenu = new UxmlBoolAttributeDescription { name = "show-menu-button", defaultValue = false };
            UxmlIntAttributeDescription m_InitialOption = new UxmlIntAttributeDescription { name = "initial-option", defaultValue = 0 };
            public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
            {
                get
                {
                    // can only contain ribbon buttons
                    yield return new UxmlChildElementDescription(typeof(RibbonButton));
                }
            }


            public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
            {
                base.Init(ve, bag, cc);
                var ribbon = ((Ribbon)ve);
                var align = m_Align.GetValueFromBag(bag, cc);
                var help = m_ShowHelp.GetValueFromBag(bag, cc);
                var menu = m_ShowMenu.GetValueFromBag(bag, cc);
                var initialOption = m_InitialOption.GetValueFromBag(bag, cc);

                ribbon.Alignment = align;
                ribbon.ShowHelpButton = help;
                ribbon.ShowMenuButton = menu;
                ribbon.InitialOption = initialOption;

                ((Ribbon)ve).Init();
            }
        }
    }

}