// RibbonUICommand.cs
// Copyright (c) 2017 Bentley Systems, Incorporated. All Rights Reserved.

using Haestad.Support.Library;
using Haestad.Support.Support;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.RibbonView;

namespace Haestad.Framework.UI.Integration
{
    /// <summary>
    /// Class wraps up an ICommand and embues it with additional context needed for linking the command to a ribbon button or menu item.
    /// Allows standard, global, application commands to be defined in a single location (e.g., command factory).
    /// WPF user interface elements whose Command property is set to an instance of this class will reflect the executability of the underlying ICommand through their IsEnabled property.
    /// This includes visually becoming disabled when the underlying command answers false to CanExecute().
    /// </summary>
    public class RibbonUICommand : IRibbonUICommand, IRibbonSearchLinkedUICommand, IInvokableUICommand
    {
        #region Constructors
        /// <summary>
        /// Instantiates a new RibbonUICommand.
        /// </summary>
        /// <param name="command">The associated ICommand.</param>
        /// <param name="name">The *unique* name of the ribbon UI command.</param>
        /// <param name="labelKey">The resource key used to look up the ribbon UI command label from TextManager.</param>
        /// <param name="toolTipTitleKey">The resource key used to look up the ribbon UI command label from TextManager.</param>
        /// <param name="toolTipDescriptionKey">The resource key used to look up the ribbon UI command label from TextManager.</param>
        /// <param name="graphicResourceKey">The graphic resource key for the regular (small) image size.</param>
        public RibbonUICommand(ICommand command, string name, string labelKey, string toolTipTitleKey, string toolTipDescriptionKey,
            string graphicResourceKey)
            : this(command, name, labelKey, toolTipTitleKey, toolTipDescriptionKey, graphicResourceKey, String.Empty)
        {
        }

        /// <summary>
        /// Instantiates a new RibbonUICommand.
        /// </summary>
        /// <param name="command">The associated ICommand.</param>
        /// <param name="name">The *unique* name of the ribbon UI command.</param>
        /// <param name="labelKey">The resource key used to look up the ribbon UI command label from TextManager.</param>
        /// <param name="toolTipTitleKey">The resource key used to look up the ribbon UI command label from TextManager.</param>
        /// <param name="toolTipDescriptionKey">The resource key used to look up the ribbon UI command label from TextManager.</param>
        /// <param name="graphicResourceKey">The graphic resource key for the regular (small) image size.</param>
        /// <param name="largeGraphicResourceKey">The graphic resource key for the large image size.</param>
        public RibbonUICommand(ICommand command, string name, string labelKey, string toolTipTitleKey, string toolTipDescriptionKey,
            string graphicResourceKey, string largeGraphicResourceKey)
            : this(command, name, labelKey, toolTipTitleKey, toolTipDescriptionKey, graphicResourceKey, largeGraphicResourceKey, RibbonHotKey.None)
        {
        }

        /// <summary>
        /// Instantiates a new RibbonUICommand.
        /// </summary>
        /// <param name="command">The associated ICommand.</param>
        /// <param name="name">The *unique* name of the ribbon UI command.</param>
        /// <param name="labelKey">The resource key used to look up the ribbon UI command label from TextManager.</param>
        /// <param name="toolTipTitleKey">The resource key used to look up the ribbon UI command label from TextManager.</param>
        /// <param name="toolTipDescriptionKey">The resource key used to look up the ribbon UI command label from TextManager.</param>
        /// <param name="graphicResourceKey">The graphic resource key for the regular (small) image size.</param>
        /// <param name="largeGraphicResourceKey">The graphic resource key for the large image size.</param>
        /// <param name="hotKey">The ribbon hot key to use for the command.</param>
        public RibbonUICommand(ICommand command, string name, string labelKey, string toolTipTitleKey, string toolTipDescriptionKey,
            string graphicResourceKey, string largeGraphicResourceKey, RibbonHotKey hotKey)
        {
            Command = command;
            Command.CanExecuteChanged += Command_CanExecuteChanged;

            Name = name;

            LabelKey = labelKey;
            ToolTipTitleKey = toolTipTitleKey;
            ToolTipDescriptionKey = toolTipDescriptionKey;

            m_label = String.Empty;
            m_toolTipTitle = String.Empty;
            m_toolTipDescription = String.Empty;

            GraphicResourceKey = graphicResourceKey;
            LargeGraphicResourceKey = largeGraphicResourceKey;
            HotKey = hotKey;

            HiddenFromSearch = false;
        }
        #endregion

        #region Event Handlers
        private void Command_CanExecuteChanged(object sender, EventArgs e)
        {
            OnCanExecuteChanged(sender, e);
        }

        private void DispatcherOperation_Completed(object sender, EventArgs e)
        {
            // Note: <whar> Since our way of dealing with the potential race condition means we may end up calling this event handler twice, 
            // we make sure that we only handle it once.

            // This thread is a good read if you are curious as to why I am doing this here:
            // http://www.pcreview.co.uk/threads/wpf-dispatcher-queues-race-condition.3970476/

            lock (m_thisLock)
            {
                if (DispatchOperationCompletedHandled)
                    return;

                DispatchOperationCompletedHandled = true;

                AfterComplete();
            }
        }
        #endregion

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return Command.CanExecute(parameter);
        }

        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Executes the underlying ICommand. *DO NOT* make this method virtual. Use provided hooks (BeforeExecute, AfterExecute, AfterComplete) instead.
        /// </summary>
        public void Execute(object parameter)
        {
            DispatchOperationCompletedHandled = false;

            if (BeforeExecute())
            {
                Dispatcher dispatcher = GetDispatcher();
                if (dispatcher != null)
                {
                    try
                    {
                        DispatcherOperation dispatcherOperation = dispatcher.BeginInvoke(
                          new InvokeCommandDelegate(InvokeCommand), System.Windows.Threading.DispatcherPriority.Input, new object[] { parameter });
                        dispatcherOperation.Completed += DispatcherOperation_Completed;

                        // Note: <whar> To deal with potential race condition, we check if the command already completed, and if so, call the handler.

                        // This thread is a good read if you are curious as to why I am doing this here:
                        // http://www.pcreview.co.uk/threads/wpf-dispatcher-queues-race-condition.3970476/

                        if (dispatcherOperation.Status == DispatcherOperationStatus.Completed)
                            DispatcherOperation_Completed(this, EventArgs.Empty);
                    }
                    finally
                    {
                        AfterExecute();
                    }
                }
            }
        }
        #endregion

        #region INamable Members

        public string Name
        {
            get;
            private set;
        }

        #endregion

        #region ILabeled Members
        public string Label
        {
            get
            {
                if (StringLibrary.IsBlank(m_label) && !StringLibrary.IsBlank(LabelKey))
                    m_label = GetLabel(LabelKey);
                return m_label;
            }
        }
        #endregion

        #region IRibbonUICommand Members

        public string ToolTipTitle
        {
            get
            {
                if (StringLibrary.IsBlank(m_toolTipTitle) && !StringLibrary.IsBlank(ToolTipTitleKey))
                    m_toolTipTitle = GetToolTipTitle(ToolTipTitleKey);
                return m_toolTipTitle;
            }
            protected set
            {
                m_toolTipTitle = value;
            }
        }

        public string ToolTipDescription
        {
            get
            {
                if (StringLibrary.IsBlank(m_toolTipDescription) && !StringLibrary.IsBlank(ToolTipDescriptionKey))
                    m_toolTipDescription = GetToolTipDescription(ToolTipDescriptionKey);
                return m_toolTipDescription;
            }
            protected set
            {
                m_toolTipDescription = value;
            }
        }

        public string GraphicResourceKey
        {
            get;
            private set;
        }

        public string LargeGraphicResourceKey
        {
            get;
            private set;
        }

        public RibbonHotKey HotKey
        {
            get;
            private set;
        }

        public string GetCleanLabel()
        {
            return StringLibrary.CleanTextResource(Label);
        }

        public string GetCleanToolTipTitle()
        {
            if (UseHotKeyInToolTip && HotKey.IsValidShortCut)
                return TextManager.Current["hfui_toolTipTitleAppend", new string[] { StringLibrary.CleanTextResource(ToolTipTitle), HotKey.ToString() }];
            else
                return StringLibrary.CleanTextResource(ToolTipTitle);
        }

        public string GetCleanToolTipDescription()
        {
            return StringLibrary.CleanTextResource(ToolTipDescription);
        }

        public bool UseHotKeyInToolTip
        {
            set;
            get;
        }
        #endregion

        #region IRibbonLinkedUICommand Members
        public IList<FrameworkElement> GetLinkedElements(RibbonUICommandLinkedElementTypes linkedElementTypes)
        {
            IList<FrameworkElement> linkedElements = new List<FrameworkElement>(LinkedRegularButtons.Count + LinkedToggleButtons.Count +
                LinkedDropDownOnlyButtons.Count + LinkedDropDownButtons.Count + LinkedDropDownToggleButtons.Count + LinkedMenus.Count +
                LinkedCheckedMenus.Count + LinkedToggleMenus.Count + LinkedRibbonGroups.Count);

            AddElements(ref linkedElements, LinkedRegularButtons, linkedElementTypes, RibbonUICommandLinkedElementTypes.RegularButtons);
            AddElements(ref linkedElements, LinkedToggleButtons, linkedElementTypes, RibbonUICommandLinkedElementTypes.ToggleButtons);
            AddElements(ref linkedElements, LinkedDropDownOnlyButtons, linkedElementTypes, RibbonUICommandLinkedElementTypes.DropDownOnlyButtons);
            AddElements(ref linkedElements, LinkedDropDownButtons, linkedElementTypes, RibbonUICommandLinkedElementTypes.DropDownButtons);
            AddElements(ref linkedElements, LinkedDropDownToggleButtons, linkedElementTypes, RibbonUICommandLinkedElementTypes.DropDownToggleButtons);
            AddElements(ref linkedElements, LinkedMenus, linkedElementTypes, RibbonUICommandLinkedElementTypes.Menus);
            AddElements(ref linkedElements, LinkedCheckedMenus, linkedElementTypes, RibbonUICommandLinkedElementTypes.CheckedMenus);
            AddElements(ref linkedElements, LinkedToggleMenus, linkedElementTypes, RibbonUICommandLinkedElementTypes.ToggleMenus);
            AddElements(ref linkedElements, LinkedRibbonGroups, linkedElementTypes, RibbonUICommandLinkedElementTypes.RibbonGroups);
            AddElements(ref linkedElements, LinkedBackstageItems, linkedElementTypes, RibbonUICommandLinkedElementTypes.BackstageItems);

            return linkedElements;
        }

        public void AddLinkedRegularButton(RadRibbonButton button)
        {
            LinkedRegularButtons.Add(button);
        }

        public void AddLinkedToggleButton(RadRibbonToggleButtonEx button)
        {
            LinkedToggleButtons.Add(button);
        }

        public void AddLinkedDropDownOnlyButton(RadRibbonDropDownButton button)
        {
            LinkedDropDownOnlyButtons.Add(button);
        }

        public void AddLinkedDropDownButton(RadRibbonSplitButton button)
        {
            LinkedDropDownButtons.Add(button);
        }

        public void AddLinkedDropDownToggleButton(RadRibbonToggleSplitButton button)
        {
            LinkedDropDownToggleButtons.Add(button);
        }

        public void AddLinkedMenu(RadMenuItem menuItem)
        {
            if (menuItem is RadToggleMenuItem)
                throw new Exception("Called wrong method. Call AddLinkedToggleMenu for RadToggleMenuItem objects."); // I18N. OK.

            LinkedMenus.Add(menuItem);
        }

        public void AddLinkedCheckedMenu(RadMenuItem menuItem)
        {
            if (menuItem is RadToggleMenuItem)
                throw new Exception("Called wrong method. Call AddLinkedToggleMenu for RadToggleMenuItem objects."); // I18N. OK.

            LinkedCheckedMenus.Add(menuItem);
        }

        public void AddLinkedToggleMenu(RadToggleMenuItem menuItem)
        {
            LinkedToggleMenus.Add(menuItem);
        }

        public void AddLinkedRibbonGroup(RadRibbonGroup ribbonGroup)
        {
            LinkedRibbonGroups.Add(ribbonGroup);
        }

        public void AddLinkedBackstageItem(RadRibbonBackstageItem backstageItem)
        {
            LinkedBackstageItems.Add(backstageItem);
        }

        public void RemoveLinkedControl(FrameworkElement control)
        {
            LinkedRegularButtons.Remove(control);
            LinkedToggleButtons.Remove(control);
            LinkedDropDownOnlyButtons.Remove(control);
            LinkedDropDownButtons.Remove(control);
            LinkedDropDownToggleButtons.Remove(control);
            LinkedMenus.Remove(control);
            LinkedCheckedMenus.Remove(control);
            LinkedToggleMenus.Remove(control);
            LinkedRibbonGroups.Remove(control);
            LinkedRegularButtons.Remove(control);
            LinkedBackstageItems.Remove(control);
        }
        #endregion

        #region IRibbonSearchLinkedUICommand Members
        public virtual bool HiddenFromSearch
        {
            get;
            set;
        }

        public void GetLinkedElementSearchData(RibbonUICommandLinkedElementTypes linkedElementTypes, 
            out IList<RibbonLocation> elementLocations, out IList<FrameworkElement> elementRibbonTabs, out IList<string> elementPaths)
        {
            IList<FrameworkElement> linkedElements = GetLinkedElements(linkedElementTypes);
            
            elementLocations = new List<RibbonLocation>(linkedElements.Count);
            elementRibbonTabs = new List<FrameworkElement>(linkedElements.Count);
            elementPaths = new List<string>(linkedElements.Count);

            foreach (FrameworkElement controlElement in linkedElements)
            {
                if (GetShouldIncludePathForLinkedControl(controlElement))
                {
                    elementLocations.Add(TelerikLibrary.GetControlLocation(controlElement as Control));
                    elementRibbonTabs.Add(GetRibbonTabForLinkedControl(controlElement));
                    elementPaths.Add(GetPathForLinkedControl(controlElement));
                }
            }
        }
        #endregion

        #region IInvokableUICommand Members
        public void SetDispatcherElement(FrameworkElement dispatcherElement)
        {
            DispatcherElement = dispatcherElement;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Answers the underlying (wrapped) ICommand.
        /// Intended for special cases only (e.g., where some persistent command context might need to be set for cases where an Execute() must happen in the absence of such context). 
        /// Having this method avoids the need to implement subclasses of RibbonUICommand simply to be able to get at the underlying command.
        /// *** Clients should NOT execute the underlying command directly. *** (Always Execute through the RibbonUICommand; dynamic command context can and is intended to be passed 
        /// into the Execute() method directly.)
        /// </summary>
        /// <returns>The underlying ICommand.</returns>
        public ICommand GetUnderlyingCommand()
        {
            return Command;
        }
        #endregion

        #region Internal Methods
        internal string GetPathForLinkedButton(IRibbonButton button)
        {
            string cleanLabel = GetCleanLabel();
            string commandText = button.Text == String.Empty ? cleanLabel : button.Text;

            if (StringLibrary.IsWordSubsetOfFullLocalizedLabel(commandText, cleanLabel))
                commandText = StringLibrary.GetLabelWithMissingWords(commandText, cleanLabel);
            else if (StringLibrary.IsAlternateShortRepresentationOfFullLocalizedLabel(commandText, cleanLabel))
                commandText = TextManager.Current["hfui_ShortLabelLongLabel", commandText, cleanLabel]; 

            RibbonLocation location = TelerikLibrary.GetControlLocation(button as Control);

            switch (location)
            {
                case RibbonLocation.RibbonTab:
                    return GetPathForTabBasedControl(button as Control, commandText);

                case RibbonLocation.QuickAccessToolbar:
                    return GetPathForQATBasedControl(commandText);

                case RibbonLocation.TopTabStrip:
                    return GetPathForTopTabStripBasedControl(commandText);

                // Note: <whar> No buttons on backstage as yet.

                default:
                    return String.Empty;
            }
        }
        #endregion

        #region Protected Virtual Methods
        /// <summary>
        /// Hook for subclasses to do work before the command is executed.
        /// </summary>
        /// <returns>Returns whether the command should proceed.</returns>
        protected virtual bool BeforeExecute()
        {
            return true;
        }

        /// <summary>
        /// Hook for subclasses to do work after the command is executed.
        /// Work done after the command is invoked is done on the UI thread. The command may not have completed at this point.
        /// Anything that needs to happen in the command should be in the ICommand itself.
        /// </summary>
        protected virtual void AfterExecute()
        {
            /*No-op*/
        }

        /// <summary>
        /// Hook for subclasses to do work after the command fully completes.
        /// (If opening modal UI or the like, will not occur until that UI is closed).
        /// Will *not* be called if an exception occurs during the command.
        /// </summary>
        protected virtual void AfterComplete()
        {
            /*No-op*/
        }

        protected virtual string GetLabel(string labelKey)
        {
            return TextManager.Current[labelKey];
        }

        protected virtual string GetToolTipTitle(string toolTipTitleKey)
        {
            return TextManager.Current[toolTipTitleKey];
        }

        protected virtual string GetToolTipDescription(string toolTipDescriptionKey)
        {
            return TextManager.Current[toolTipDescriptionKey];
        }
        #endregion

        #region Protected Methods
        protected void OnCanExecuteChanged(object sender, EventArgs e)
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged.Invoke(sender, e);
        }
        protected Dispatcher GetDispatcher()
        {
            if (DispatcherElement != null)
                return DispatcherElement.Dispatcher;

            IList<FrameworkElement> linkedElements = GetLinkedElements(RibbonUICommandLinkedElementTypes.All);
            if (linkedElements.Count == 0)
                return null;

            return linkedElements[0].Dispatcher;
        }
        #endregion

        #region Private Methods
        private void AddElements(ref IList<FrameworkElement> linkedElements, IList<FrameworkElement> listToIterate,
            RibbonUICommandLinkedElementTypes linkedElementTypes, RibbonUICommandLinkedElementTypes linkedElementType)
        {
            if (RibbonUICommandLinkedElementTypesInclude(linkedElementTypes, linkedElementType))
            {
                foreach (FrameworkElement element in listToIterate)
                    linkedElements.Add(element);
            }
        }

        private bool RibbonUICommandLinkedElementTypesInclude(RibbonUICommandLinkedElementTypes linkedElementTypes, RibbonUICommandLinkedElementTypes linkedElementType)
        {
            return ((linkedElementTypes & linkedElementType) > RibbonUICommandLinkedElementTypes.None);
        }

        private void InvokeCommand(object parameter)
        {
            if (Command.CanExecute(parameter))
                Command.Execute(parameter);
        }

        private bool GetShouldIncludePathForLinkedControl(FrameworkElement element)
        {
            // Note: <whar> Avoids showing menus that are just dupes of the parent button.
            if (element is RadMenuItem)
                return !GetIsLinkedMenuAssignedToLinkedButton((RadMenuItem)element);

            return true;
        }

        private bool GetIsLinkedMenuAssignedToLinkedButton(RadMenuItem menuItem)
        {
            IRibbonButton button = TelerikLibrary.GetOwnerButton(menuItem);
            if (button == null)
                return false;

            IList<FrameworkElement> linkedMenuAcceptingButtons = GetLinkedElements(RibbonUICommandLinkedElementTypes.DropDownButtons |
                RibbonUICommandLinkedElementTypes.DropDownOnlyButtons | RibbonUICommandLinkedElementTypes.DropDownToggleButtons);

            return linkedMenuAcceptingButtons.Contains((FrameworkElement)button);
        }

        private string GetPathForLinkedControl(FrameworkElement controlElement)
        {
            Control control = controlElement as Control;
            if (control == null)
                return String.Empty;

            if (control is IRibbonButton)
                return GetPathForLinkedButton((IRibbonButton)control);

            if (control is RadMenuItem)
                return GetPathForLinkedMenuItem((RadMenuItem)control);

            if (control is RadRibbonBackstageItem)
                return GetPathForBackstageBasedControl(((RadRibbonBackstageItem)control).Header.ToString());

            if (control is RadRibbonGroup)
                return GetPathForDialogLauncher((RadRibbonGroup)control);

            return String.Empty;
        }

        private RadRibbonTab GetRibbonTabForLinkedControl(FrameworkElement controlElement)
        {
            Control control = controlElement as Control;
            if (control == null)
                return null;

            if (control is IRibbonButton)
                return GetRibbonTabForLinkedButton((IRibbonButton)control);

            if (control is RadMenuItem)
                return GetRibbonTabForLinkedMenuItem((RadMenuItem)control);

            if (control is RadRibbonBackstageItem)
                return null;

            if (control is RadRibbonGroup)
                return GetRibbonTabForDialogLauncher((RadRibbonGroup)control);

            return null;
        }

        private RadRibbonTab GetRibbonTabForLinkedButton(IRibbonButton button)
        {
            RibbonLocation location = TelerikLibrary.GetControlLocation(button as Control);

            switch (location)
            {
                case RibbonLocation.RibbonTab:
                    return GetRibbonTabForTabBasedControl(button as Control);

                case RibbonLocation.QuickAccessToolbar:
                    return null;

                case RibbonLocation.TopTabStrip:
                    return null;

                default:
                    return null;
            }
        }

        private string GetPathForLinkedMenuItem(RadMenuItem menuItem)
        {
            IRibbonButton button = TelerikLibrary.GetOwnerButton(menuItem);
            if (button == null)
                return String.Empty;

            string menuLabel = String.Empty;
            if (menuItem.Header is string) // Note: <whar> Kris is manually setting Header in some places (e.g., where he wants a short label).
                menuLabel = (string)menuItem.Header;
            else if (menuItem.Header is Grid)
            {
                TextBlock textBlock = ((Grid)menuItem.Header).GetChildOfType<TextBlock>();
                menuLabel = textBlock.Text;
            }

            string pathArrow = TextManager.Current["hfui_PathArrow"];

            if (button is IRibbonLinkedUIElement && ((IRibbonLinkedUIElement)button).LinkedRibbonUICommand is RibbonUICommand)
            {
                RibbonUICommand ribbonUICommand = ((RibbonUICommand)((IRibbonLinkedUIElement)button).LinkedRibbonUICommand);
                return ribbonUICommand.GetPathForLinkedButton(button) + SPACE_STRING + TextManager.Current["hfui_PathFormatMenu", pathArrow, menuLabel];
            }

            // Note: < whar > Can't get a valid path in this case, because we cannot find the IRibbonUICommand associated with the button (which is linked
            // to a *different* command than the menu item. Still, this should not happen; will manifest as blank entry in the ribbon search drop down.
            return TextManager.Current["hfui_UnknownPath", pathArrow, menuLabel];
        }

        private RadRibbonTab GetRibbonTabForLinkedMenuItem(RadMenuItem menuItem)
        {
            IRibbonButton button = TelerikLibrary.GetOwnerButton(menuItem);
            if (button == null)
                return null;
            return GetRibbonTabForTabBasedControl(button as Control);
        }

        private string GetPathForTabBasedControl(Control control, string commandText)
        {
            RadRibbonTab ribbonTab = GetRibbonTabForTabBasedControl(control);
            if (ribbonTab == null)
                return String.Empty;

            RadRibbonGroup ribbonGroup = TelerikLibrary.GetOwnerGroup(control);
            if (ribbonGroup == null)
                return String.Empty;

            string ribbonGroupLabel = ribbonGroup.Header.ToString();
            if (StringLibrary.IsBlank(ribbonGroupLabel))
                ribbonGroupLabel = TextManager.Current["hfui_UnnamedGroup"];
            string pathArrow = TextManager.Current["hfui_PathArrow"];
            return TextManager.Current["hfui_PathFormat", ribbonTab.Header.ToString(), pathArrow, ribbonGroupLabel, pathArrow, commandText];
        }

        private RadRibbonTab GetRibbonTabForTabBasedControl(Control control)
        {
            // TODO 0: <whar> Should this be trying to get the group first, as an alternate way to get the tab?

            return TelerikLibrary.GetOwnerTab(control);
        }

        private string GetPathForQATBasedControl(string commandText)
        {
            string pathArrow = TextManager.Current["hfui_PathArrow"];
            string quickAccessToolbar = TextManager.Current["hfui_PathQuickAccessToolbar"];
            return TextManager.Current["hfui_PathFormatQAT", quickAccessToolbar, pathArrow, commandText];
        }

        private string GetPathForTopTabStripBasedControl(string commandText)
        {
            string pathArrow = TextManager.Current["hfui_PathArrow"];
            string topTabStrip = TextManager.Current["hfui_TopTabStrip"];
            return TextManager.Current["hfui_PathFormatTopTabStrip", topTabStrip, pathArrow, commandText];
        }

        private string GetPathForBackstageBasedControl(string backstageItemText)
        {
            string pathArrow = TextManager.Current["hfui_PathArrow"];
            string backstage = TextManager.Current["hfui_PathBackstage"];
            return TextManager.Current["hfui_PathFormat", backstage, pathArrow, backstageItemText, pathArrow, GetCleanLabel()];
        }

        private string GetPathForDialogLauncher(RadRibbonGroup ribbonGroup)
        {
            RadRibbonTab ribbonTab = GetRibbonTabForDialogLauncher(ribbonGroup);
            if (ribbonTab == null)
                return String.Empty;

            string pathArrow = TextManager.Current["hfui_PathArrow"];
            return TextManager.Current["hfui_PathFormat", ribbonTab.Header.ToString(), pathArrow, ribbonGroup.Header.ToString(), pathArrow, GetCleanLabel()];
        }

        private RadRibbonTab GetRibbonTabForDialogLauncher(RadRibbonGroup ribbonGroup)
        {
            return TelerikLibrary.GetOwnerTab(ribbonGroup);
        }

        #endregion

        #region Protected Properties
        protected ICommand Command
        {
            get;
            set;
        }

        protected string LabelKey
        {
            private get;
            set;
        }
        #endregion

        #region Private Properties
        private string ToolTipTitleKey
        {
            get;
            set;
        }

        private string ToolTipDescriptionKey
        {
            get;
            set;
        }

        private IList<FrameworkElement> LinkedRegularButtons
        {
            get
            {
                if (m_linkedRegularButtons == null)
                    m_linkedRegularButtons = new List<FrameworkElement>();
                return m_linkedRegularButtons;
            }
        }

        private IList<FrameworkElement> LinkedToggleButtons
        {
            get
            {
                if (m_linkedToggleButtons == null)
                    m_linkedToggleButtons = new List<FrameworkElement>();
                return m_linkedToggleButtons;
            }
        }

        private IList<FrameworkElement> LinkedDropDownOnlyButtons
        {
            get
            {
                if (m_linkedDropDownOnlyButtons == null)
                    m_linkedDropDownOnlyButtons = new List<FrameworkElement>();
                return m_linkedDropDownOnlyButtons;
            }
        }

        private IList<FrameworkElement> LinkedDropDownButtons
        {
            get
            {
                if (m_linkedDropDownButtons == null)
                    m_linkedDropDownButtons = new List<FrameworkElement>();
                return m_linkedDropDownButtons;
            }
        }

        private IList<FrameworkElement> LinkedDropDownToggleButtons
        {
            get
            {
                if (m_linkedDropDownToggleButtons == null)
                    m_linkedDropDownToggleButtons = new List<FrameworkElement>();
                return m_linkedDropDownToggleButtons;
            }
        }

        private IList<FrameworkElement> LinkedMenus
        {
            get
            {
                if (m_linkedMenus == null)
                    m_linkedMenus = new List<FrameworkElement>();
                return m_linkedMenus;
            }
        }

        private IList<FrameworkElement> LinkedCheckedMenus
        {
            get
            {
                if (m_linkedCheckedMenus == null)
                    m_linkedCheckedMenus = new List<FrameworkElement>();
                return m_linkedCheckedMenus;
            }
        }

        private IList<FrameworkElement> LinkedToggleMenus
        {
            get
            {
                if (m_linkedToggleMenus == null)
                    m_linkedToggleMenus = new List<FrameworkElement>();
                return m_linkedToggleMenus;
            }
        }

        private IList<FrameworkElement> LinkedRibbonGroups
        {
            get
            {
                if (m_linkedRibbonGroups == null)
                    m_linkedRibbonGroups = new List<FrameworkElement>();
                return m_linkedRibbonGroups;
            }
        }

        private IList<FrameworkElement> LinkedBackstageItems
        {
            get
            {
                if (m_linkedBackstageItems == null)
                    m_linkedBackstageItems = new List<FrameworkElement>();
                return m_linkedBackstageItems;
            }
        }

        private FrameworkElement DispatcherElement
        {
            get;
            set;
        }

        private bool DispatchOperationCompletedHandled
        {
            get;
            set;
        }
        #endregion

        #region Private Delegates
        private delegate void InvokeCommandDelegate(object parameter);
        #endregion

        #region Private Fields
        private string m_label;
        private string m_toolTipTitle;
        private string m_toolTipDescription;

        private IList<FrameworkElement> m_linkedRegularButtons;
        private IList<FrameworkElement> m_linkedToggleButtons;
        private IList<FrameworkElement> m_linkedDropDownOnlyButtons;
        private IList<FrameworkElement> m_linkedDropDownButtons;
        private IList<FrameworkElement> m_linkedDropDownToggleButtons;
        private IList<FrameworkElement> m_linkedMenus;
        private IList<FrameworkElement> m_linkedCheckedMenus;
        private IList<FrameworkElement> m_linkedToggleMenus;
        private IList<FrameworkElement> m_linkedRibbonGroups;
        private IList<FrameworkElement> m_linkedBackstageItems;

        private object m_thisLock = new object();
        #endregion

        #region Private Constants
        private const string SPACE_STRING = " ";
        #endregion
    }
}
