// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
        

#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Controls.Ribbon
#else
namespace Microsoft.Windows.Controls.Ribbon
#endif
{
    #region Using declarations

    using System;
    using System.Windows;
    using System.Windows.Automation.Peers;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Input;
#if RIBBON_IN_FRAMEWORK
    using Microsoft.Windows.Controls;
#else
    using Microsoft.Windows.Automation.Peers;
#endif

    #endregion Using declarations

    /// <summary>
    ///   Implements the functionality for a Ribbon's ApplicationMenu. 
    /// </summary>
    [TemplatePart(Name = RibbonApplicationMenu.PopupToggleButtonTemplateName, Type = typeof(RibbonToggleButton))]
    [TemplatePart(Name = RibbonApplicationMenu.PopupTemplateName, Type = typeof(Popup))]
    [TemplatePart(Name = RibbonApplicationMenu.SubmenuTemplateName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = RibbonApplicationMenu.FooterPaneTemplateName, Type = typeof(ContentPresenter))]
    [TemplatePart(Name = RibbonApplicationMenu.AuxiliaryPaneTemplateName, Type = typeof(ContentPresenter))]
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(RibbonApplicationMenuItem))]
    public class RibbonApplicationMenu : RibbonMenuButton
    {
        #region Fields

        /// <summary>
        ///   The Popup for the RibbonApplicationMenu.
        /// </summary>
        private Popup _popup;

        /// <summary>
        ///   The RibbonToggleButton in the RibbonApplicationMenu's style that overlays the Popup
        ///   in order to for the popup to look as if it is placed behind its placement target. 
        /// </summary>
        private RibbonToggleButton _popupToggleButton;

        /// <summary>
        ///   The ContentPresenter for the FooterPane's content.
        /// </summary>
        private ContentPresenter _footerPaneHost;

        /// <summary>
        ///   The ContentPresenter for the AuxiliaryPane's content.
        /// </summary>
        private ContentPresenter _auxiliaryPaneHost;

        /// <summary>
        /// Template parts names
        /// </summary>
        private const string PopupTemplateName = "PART_Popup";
        private const string PopupToggleButtonTemplateName = "PART_PopupToggleButton";
        private const string SubmenuTemplateName = "PART_SubmenuPlaceholder";
        private const string FooterPaneTemplateName = "PART_FooterPaneContentPresenter";
        private const string AuxiliaryPaneTemplateName = "PART_AuxiliaryPaneContentPresenter";
        #endregion

        #region Constructors

        /// <summary>
        ///   Initializes static members of the RibbonApplicationMenu class.  Also, overrides
        ///   the default style, and registers class handlers for a couple events.
        /// </summary>
        static RibbonApplicationMenu()
        {
            Type ownerType = typeof(RibbonApplicationMenu);

            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));

            RibbonControlService.IsInControlGroupPropertyKey.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false, null, new CoerceValueCallback(CoerceToFalse)));
            RibbonControlService.IsInQuickAccessToolBarPropertyKey.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false, null, new CoerceValueCallback(CoerceToFalse)));
            RibbonControlService.CanAddToQuickAccessToolBarDirectlyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false, null, new CoerceValueCallback(CoerceToFalse)));

            RibbonMenuButton.CanUserResizeVerticallyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false, null, new CoerceValueCallback(CoerceToFalse)));
            RibbonMenuButton.CanUserResizeHorizontallyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false, null, new CoerceValueCallback(CoerceToFalse)));
        }

        #endregion

        #region ContentModel

        /// <summary>
        ///   Gets or sets the FooterPaneContent of the RibbonApplicationMenu.
        /// </summary>
        public Object FooterPaneContent
        {
            get { return (Object)GetValue(FooterPaneContentProperty); }
            set { SetValue(FooterPaneContentProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for FooterPaneContent.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty FooterPaneContentProperty =
                     DependencyProperty.Register(
                            "FooterPaneContent",
                            typeof(Object),
                            typeof(RibbonApplicationMenu),
                            new FrameworkPropertyMetadata(null));
        /// <summary>
        ///   Gets or sets the FooterPaneContentTemplate of the RibbonApplicationMenu.
        /// </summary>
        public DataTemplate FooterPaneContentTemplate
        {
            get { return (DataTemplate)GetValue(FooterPaneContentTemplateProperty); }
            set { SetValue(FooterPaneContentTemplateProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for FooterPaneContent.
        /// </summary>
        public static readonly DependencyProperty FooterPaneContentTemplateProperty =
                     DependencyProperty.Register(
                            "FooterPaneContentTemplate",
                            typeof(DataTemplate),
                            typeof(RibbonApplicationMenu),
                            new FrameworkPropertyMetadata(null));

        /// <summary>
        ///   Gets or sets the FooterPaneContentTemplateSelector of the RibbonApplicationMenu.
        /// </summary>
        public DataTemplateSelector FooterPaneContentTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(FooterPaneContentTemplateSelectorProperty); }
            set { SetValue(FooterPaneContentTemplateSelectorProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for FooterPaneContent.
        /// </summary>
        public static readonly DependencyProperty FooterPaneContentTemplateSelectorProperty =
                     DependencyProperty.Register(
                            "FooterPaneContentTemplateSelector",
                            typeof(DataTemplateSelector),
                            typeof(RibbonApplicationMenu),
                            new FrameworkPropertyMetadata(null));

        /// <summary>
        ///   Gets or sets the AuxiliaryPaneContent of the RibbonApplicationMenu.
        /// </summary>
        public object AuxiliaryPaneContent
        {
            get { return (object)GetValue(AuxiliaryPaneContentProperty); }
            set { SetValue(AuxiliaryPaneContentProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for AuxiliaryPaneContent.
        /// </summary>
        public static readonly DependencyProperty AuxiliaryPaneContentProperty =
                    DependencyProperty.Register(
                            "AuxiliaryPaneContent",
                            typeof(object),
                            typeof(RibbonApplicationMenu),
                            new FrameworkPropertyMetadata(null));

        /// <summary>
        ///   Gets or sets the AuxiliaryPaneContentTemplate of the RibbonApplicationMenu.
        /// </summary>
        public DataTemplate AuxiliaryPaneContentTemplate
        {
            get { return (DataTemplate)GetValue(AuxiliaryPaneContentTemplateProperty); }
            set { SetValue(AuxiliaryPaneContentTemplateProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for AuxiliaryPaneContentTemplate.
        /// </summary>
        public static readonly DependencyProperty AuxiliaryPaneContentTemplateProperty =
                    DependencyProperty.Register(
                            "AuxiliaryPaneContentTemplate",
                            typeof(DataTemplate),
                            typeof(RibbonApplicationMenu),
                            new FrameworkPropertyMetadata(null));

        /// <summary>
        ///   Gets or sets the AuxiliaryPaneContentTemplateSelector of the RibbonApplicationMenu.
        /// </summary>
        public DataTemplateSelector AuxiliaryPaneContentTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(AuxiliaryPaneContentTemplateSelectorProperty); }
            set { SetValue(AuxiliaryPaneContentTemplateSelectorProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for AuxiliaryPaneContentTemplateSelector.
        /// </summary>
        public static readonly DependencyProperty AuxiliaryPaneContentTemplateSelectorProperty =
                    DependencyProperty.Register(
                            "AuxiliaryPaneContentTemplateSelector",
                            typeof(DataTemplateSelector),
                            typeof(RibbonApplicationMenu),
                            new FrameworkPropertyMetadata(null));


        #endregion ContentModel

        #region ContainerGeneration

        private object _currentItem;

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            bool ret = (item is RibbonApplicationMenuItem) || (item is RibbonApplicationSplitMenuItem) || (item is RibbonSeparator) || (item is RibbonGallery);
            if (!ret)
            {
                _currentItem = item;
            }

            return ret;
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            object currentItem = _currentItem;
            _currentItem = null;

            if (UsesItemContainerTemplate)
            {
                DataTemplate itemContainerTemplate = ItemContainerTemplateSelector.SelectTemplate(currentItem, this);
                if (itemContainerTemplate != null)
                {
                    object itemContainer = itemContainerTemplate.LoadContent();
                    if (itemContainer is RibbonApplicationMenuItem || itemContainer is RibbonApplicationSplitMenuItem || itemContainer is RibbonSeparator || itemContainer is RibbonGallery)
                    {
                        return itemContainer as DependencyObject;
                    }
                    else
                    {
                        throw new InvalidOperationException(Microsoft.Windows.Controls.SR.Format(Microsoft.Windows.Controls.SR.InvalidApplicationMenuOrItemContainer, this.GetType().Name, itemContainer));
                    }
                }
            }

            return new RibbonApplicationMenuItem();
        }

        protected override bool ShouldApplyItemContainerStyle(DependencyObject container, object item)
        {
            if (container is RibbonApplicationSplitMenuItem ||
                container is RibbonSeparator ||
                container is RibbonGallery)
            {
                return false;
            }
            else
            {
                return base.ShouldApplyItemContainerStyle(container, item);
            }
        }

        #endregion ContainerGeneration

        #region UIAutomation

        internal UIElement FooterPaneHost 
        {
            get { return _footerPaneHost; }
        }

        internal UIElement AuxiliaryPaneHost
        {
            get { return _auxiliaryPaneHost; }
        }

        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new RibbonApplicationMenuAutomationPeer(this);
        }

        #endregion UIAutomation

        #region Popup Placement

        /// <summary>
        ///   Invoked whenever the control's template is applied.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Cleanup the previous template elements
            if (_popup != null)
            {
                _popup.Opened -= new EventHandler(this.OnPopupOpened);
            }

            _popup = this.GetTemplateChild(PopupTemplateName) as Popup;
            SubmenuPlaceholder = this.GetTemplateChild(SubmenuTemplateName) as FrameworkElement;
            _popupToggleButton = this.GetTemplateChild(PopupToggleButtonTemplateName) as RibbonToggleButton;
            _footerPaneHost = this.GetTemplateChild(FooterPaneTemplateName) as ContentPresenter;
            _auxiliaryPaneHost = this.GetTemplateChild(AuxiliaryPaneTemplateName) as ContentPresenter;

            if (_popup != null)
            {
                _popup.Opened += new EventHandler(this.OnPopupOpened);
            }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for SubmenuPlaceholder.
        /// </summary>
        internal static readonly DependencyProperty SubmenuPlaceholderProperty =
                     DependencyProperty.Register(
                            "SubmenuPlaceholder",
                            typeof(FrameworkElement),
                            typeof(RibbonApplicationMenu));

        /// <summary>
        ///   Gets or sets the SubmenuPlaceholder of the RibbonApplicationMenu. This 
        ///   internal DependencyProperty is bound to by all of the top level 
        ///   RibbonApplicationMenuItems and RibbonApplicationSplitMenuItems as the 
        ///   PlacementTarget for their submenu Popups.
        /// </summary>
        internal FrameworkElement SubmenuPlaceholder
        {
            get { return (FrameworkElement)GetValue(SubmenuPlaceholderProperty); }
            set { SetValue(SubmenuPlaceholderProperty, value); }
        }

        /// <summary>
        ///   Called when the RibbonApplicationMenu's popup is opened. This code is placing a 
        ///   popup toggle button on top of the main toggle button to achieve a look of popup
        ///   being under the menu button.
        /// </summary>
        /// <param name="sender">The RibbonApplicationMenu whose Popup is opening.</param>
        /// <param name="e">The event data.</param>
        private void OnPopupOpened(object sender, EventArgs e)
        {
            // Position the inner ToggleButton to render on top of main ToggleButton
            if (_popupToggleButton != null && PartToggleButton != null)
            {
                double currentXPosition = Canvas.GetLeft(_popupToggleButton);
                if (double.IsNaN(currentXPosition))
                {
                    currentXPosition = 0;
                }

                double currentYPosition = Canvas.GetTop(_popupToggleButton);
                if (double.IsNaN(currentYPosition))
                {
                    currentYPosition = 0;
                }

                Point mainToggleButtonScreenPosition = PartToggleButton.PointToScreen(new Point());
                Point popupToggleButtonOffset = _popupToggleButton.PointFromScreen(mainToggleButtonScreenPosition);
                Canvas.SetLeft(_popupToggleButton, currentXPosition + popupToggleButtonOffset.X);
                Canvas.SetTop(_popupToggleButton, currentYPosition + popupToggleButtonOffset.Y);
            }
        }

        #endregion

        #region Private Methods

        private static object CoerceToFalse(DependencyObject d, object value)
        {
            return false;
        }

        #endregion

        #region KeyTips

        protected override void OnActivatingKeyTip(ActivatingKeyTipEventArgs e)
        {
            if (e.OriginalSource == this)
            {
                e.KeyTipHorizontalPlacement = KeyTipHorizontalPlacement.KeyTipLeftAtTargetCenter;
                e.KeyTipVerticalPlacement = KeyTipVerticalPlacement.KeyTipTopAtTargetCenter;
                e.KeyTipHorizontalOffset = e.KeyTipVerticalOffset = 0;
            }
        }

        #endregion

        #region Input

        /// <summary>
        ///     Helper method to move focus into auxiliary pane.
        /// </summary>
        internal bool AuxiliaryPaneMoveFocus(FocusNavigationDirection direction)
        {
            UIElement auxiliaryPaneHost = AuxiliaryPaneHost;
            if (auxiliaryPaneHost != null &&
                auxiliaryPaneHost.IsVisible &&
                auxiliaryPaneHost.IsEnabled &&
                auxiliaryPaneHost.MoveFocus(new TraversalRequest(direction)))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        ///     Helper method to move focus into the footer pane
        /// </summary>
        internal bool FooterPaneMoveFocus(FocusNavigationDirection direction)
        {
            UIElement footerPaneHost = FooterPaneHost;
            if (footerPaneHost != null &&
                footerPaneHost.IsVisible &&
                footerPaneHost.IsEnabled &&
                footerPaneHost.MoveFocus(new TraversalRequest(direction)))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        ///     Helper method to move focus into the items pane.
        /// </summary>
        internal bool ItemsPaneMoveFocus(FocusNavigationDirection direction)
        {
            UIElement subMenuScrollViewer = SubMenuScrollViewer;
            if (subMenuScrollViewer != null &&
                subMenuScrollViewer.MoveFocus(new TraversalRequest(direction)))
            {
                return true;
            }
            return false;
        }

        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            if (e.Handled)
            {
                return;
            }
            if (e.Key == Key.Down)
            {
                DependencyObject element = e.OriginalSource as DependencyObject;
                if (element != null)
                {
                    UIElement footerPaneHost = FooterPaneHost;
                    if (footerPaneHost != null &&
                        footerPaneHost.IsKeyboardFocusWithin &&
                        TreeHelper.IsVisualAncestorOf(footerPaneHost, element))
                    {
                        DependencyObject nextFocus = RibbonHelper.PredictFocus(element, FocusNavigationDirection.Down);
                        if (nextFocus == null ||
                            nextFocus == element)
                        {
                            // If the focus is on the last element of footer pane,
                            // then try moving focus into items pane and then into
                            // auxiliary pane if needed.
                            if (ItemsPaneMoveFocus(FocusNavigationDirection.First) ||
                                AuxiliaryPaneMoveFocus(FocusNavigationDirection.First))
                            {
                                e.Handled = true;
                            }
                        }
                    }
                }
            }
            else if (e.Key == Key.Up)
            {
                UIElement popupChild = _popup.TryGetChild();
                if (popupChild != null &&
                    !popupChild.IsKeyboardFocusWithin)
                {
                    // If the popup does not have focus with in then try moving focus to
                    // last element of FooterPane and then to the last element of
                    // auxiliary pane if needed.
                    if (FooterPaneMoveFocus(FocusNavigationDirection.Last) ||
                        AuxiliaryPaneMoveFocus(FocusNavigationDirection.Last))
                    {
                        e.Handled = true;
                    }
                }
                else
                {
                    DependencyObject element = e.OriginalSource as DependencyObject;
                    if (element != null)
                    {
                        UIElement auxilaryPaneHost = AuxiliaryPaneHost;
                        if (auxilaryPaneHost != null && 
                            auxilaryPaneHost.IsKeyboardFocusWithin && 
                            TreeHelper.IsVisualAncestorOf(auxilaryPaneHost, element))
                        {
                            DependencyObject nextFocus = RibbonHelper.PredictFocus(element, FocusNavigationDirection.Up);
                            if (nextFocus == null ||
                                nextFocus == element)
                            {
                                // If the focus is on last first element of auxiliary pane,
                                // then try moving focus to last element of Items Pane and
                                // then to last element of FooterPane if needed.
                                if (ItemsPaneMoveFocus(FocusNavigationDirection.Last) ||
                                    FooterPaneMoveFocus(FocusNavigationDirection.Last))
                                {
                                    e.Handled = true;
                                }
                            }
                        }
                    }
                }
            }
            else if (e.Key == Key.Left ||
                e.Key == Key.Right)
            {
                DependencyObject element = e.OriginalSource as DependencyObject;
                if (element != null)
                {
                    if ((e.Key == Key.Left) == (FlowDirection == FlowDirection.LeftToRight))
                    {
                        UIElement auxilaryPaneHost = AuxiliaryPaneHost;
                        if (auxilaryPaneHost != null &&
                            auxilaryPaneHost.IsKeyboardFocusWithin &&
                            TreeHelper.IsVisualAncestorOf(auxilaryPaneHost, element))
                        {
                            // If the effective key is left and the focus is on left most element
                            // of auxiliary pane, then move focus to nearest element to the left of
                            // auxiliarypane.
                            DependencyObject nextFocus = RibbonHelper.PredictFocus(element, FocusNavigationDirection.Left);
                            if (nextFocus != null &&
                                !TreeHelper.IsVisualAncestorOf(auxilaryPaneHost, nextFocus))
                            {
                                if (RibbonHelper.Focus(nextFocus))
                                {
                                    e.Handled = true;
                                }
                            }
                        }
                    }
                    else if (e.Key == Key.Left)
                    {
                        ScrollViewer subMenuScrollViewer = SubMenuScrollViewer;
                        if (subMenuScrollViewer != null &&
                            subMenuScrollViewer.IsKeyboardFocusWithin &&
                            TreeHelper.IsVisualAncestorOf(subMenuScrollViewer, element))
                        {
                            // If the flow direction is RightToLeft and the key is Left,
                            // and the focus is in items pane, move the the focus outside teh
                            // items pane if needed.
                            RibbonMenuItem menuItem = element as RibbonMenuItem;
                            if (menuItem == null)
                            {
                                menuItem = TreeHelper.FindVisualAncestor<RibbonMenuItem>(element);
                            }
                            if (menuItem != null &&
                                !menuItem.CanOpenSubMenu)
                            {
                                DependencyObject nextFocus = menuItem.PredictFocus(FocusNavigationDirection.Right);
                                if (nextFocus != null)
                                {
                                    if (RibbonHelper.Focus(nextFocus))
                                    {
                                        e.Handled = true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            base.OnPreviewKeyDown(e);
        }

        #endregion
    }
}
