/*
 *  Copyright 2015 Anyware Services
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * The ribbon is an impl of the Microsoft Fluent Ribbon use in Microsoft Products such as Microsoft Office 2007.
 * This class is the entry point of this package.
 * 
 * 		var ribbon = Ext.create("Ametys.ui.fluent.ribbon.Ribbon", {
 *                      quickToolbar: [ ...small buttons configuration... ],
 *                  
 *                      title: 'My document',
 *                      applicationTitle : 'My application',
 *                      iconSmall: "/plugins/core/resources/img/image_16.png",
 *                      iconGlyph: "fa-icon",
 *                  
 *                      searchMenu: {  // to activate a Tell me what you want to do feature
 *                          // emptyText: "Specify to replace the default value",
 *                          searchURL: "http://www.google.com?q={query}", // to search in doc
 *                          items: [
 *                              {
 *                                  text: 'A button',
 *                                  searchTerms: ['a', 'button']
 *                              }
 *                          ]
 *                      },
 *                   
 *                      // The main menu button
 *                      mainButton:
 *                      {
 *                          xtype: 'button',
 *                          icon: 'resources/img/ametys.gif',
 *                          text: 'Ametys',
 *                          tooltip: {title: 'Bouton Ametys', image: 'resources/img/ametys.gif', text: 'A long descriptive text', footertext: 'See help for details', inribbon: true},
 *                          items:
 *                          [ ...todo... ]
 *                      },
 *                   
 *                      // The tabs
 *                      items:
 *                          [
 *                              // Tab Home
 *                              { 
 *                                  title: 'Home', 
 *                                  items: [
 *                                      // Group Test
 *                                      {
 *                                          title: 'Test',
 *                                          largeItems: [...],
 *                                          items: [
 *                                                      {
 *                                                          xtype: 'ametys.ribbon-button',
 *                                                          scale: 'large',
 *                                                          enableToggle: true,
 *                                                          pressed: true,
 *                                                          handler: function() { alert('sitemap') },
 *                                                  
 *                                                          icon: 'resources/img/sitemap_32.png',
 *                                                          text: 'Sitemap',
 *                                                          tooltip: {title: 'Sitemap tool', image: 'resources/img/ametys.gif', text: 'Click here to open or close the sitemap tool that allow to manage pages', helpId: 'help.sitemap.id, inribbon: true}
 *                                                      },
 *                                                      {
 *                                                           columns: 2,
 *                                                           align: 'middle',
 *                                                           items: [
 *                                                                   { xtype: 'ametys.ribbon-button', scale: 'small', enableToggle: true, pressed: true, handler: function() { alert('a') }, icon: 'resources/img/editpaste_16.gif', text: 'Files', tooltip: {...} },
 *                                                                   { xtype: 'ametys.ribbon-button', scale: 'small', enableToggle: true, pressed: true, handler: function() { alert('b') }, icon: 'resources/img/editpaste_16.gif', text: 'Search', tooltip: {...} },
 *                                                                   { xtype: 'ametys.ribbon-button', colspan: 2, scale: 'small', enableToggle: true, pressed: true, handler: function() { alert('c') }, icon: 'resources/img/editpaste_16.gif', text: 'Profiles', tooltip: {...} }
 *                                                           ]
 *                                                       }
 *                                          ],
 *                                          smallItems: [...]
 *                                      }
 *                                  ],
 *                              },
 *                              // Contextual tab
 *                              { 
 *                                  title: 'Images', 
 *                                  items: [...], 
 *                                  contextualTab: 2, 
 *                                  contextualGroup: 'image.tools'
 *                              }
 *                          ],
 *
 *                      // Display the user logged in with a profile card in a menu
 *                      user: {
 *                          fullName: 'Raphaƫl Franchet',
 *                          login: 'raphael',
 *                          email: 'raphael.franchet@anyware-services.com',
 *                             smallPhoto: "http://www.gravatar.com/avatar/2f7124c10b1d2775303cd40bc6244419?s=16&d=blank",
 *                          mediumPhoto: "http://www.gravatar.com/avatar/2f7124c10b1d2775303cd40bc6244419?s=32&d=mm",
 *                          largePhoto: "http://www.gravatar.com/avatar/2f7124c10b1d2775303cd40bc6244419?s=48&d=mm",
 *                          extraLargePhoto: "http://www.gravatar.com/avatar/2f7124c10b1d2775303cd40bc6244419?s=64&d=mm",                        
 *                          menu: { items: [ "-", {text: 'Disconnect' } ]}
 *                      },
 *                      
 *                      // Activate the notification system
 *                      notification: {
 *                          tooltip: 'A descriptive text',
 *                          handler: function() { ... }
 *                      }
 * 		});
 * 
 * For technical reason this class is just a wrapper for the ribbon Panel. So see the {Ametys.yu.fluent.ribbon.Ribbon} configuration for parameters and usefull methods
 * Use the #getPanel method to access the wrapped object
 */
Ext.define(
	"Ametys.ui.fluent.ribbon.Ribbon",
	{
		extend: "Ext.panel.Panel",
		alias: 'widget.ametys.ribbon',
		
        /**
         * @property {String} defaultApplicationTitle The #cfg-applicationTitle default value.
         * @readonly
         * @private
         */
        defaultApplicationTitle: "Ametys",
        
        /**
         * @cfg {String} [ui="ribbon"] @inheritdoc
         */
        ui: 'ribbon',
        
        /**
         * @cfg {Object/Object[]} message Display one or more information messages under the ribbon
         * @cfg {String} message.title The message title
         * @cfg {String} [message.type="info"] The type of message 'info', 'question', 'warning' or 'error'.
         * @cfg {String} message.text The message text
         * @cfg {Boolean} [message.closeable=true] Can the message be dimissed
         * @cfg {String} [message.id] An identifier for the message to dismiss it later
         */
        
        /**
         * @private
         * @property {String} messageCloseText The message on the close button of the information message #cfg-message
         */
        messageCloseText: "{{i18n plugin.core-ui:PLUGINS_CORE_UI_WORKSPACE_AMETYS_RIBBON_MESSAGE_CLOSE_DESCRIPTION}}",
		
        /**
         * @cfg {Object/Ext.toolbar.Toolbar} quickToolbar The quicktoolbar (configuration or object) on the top left of the ribbon. 
         */
        /**
         * @cfg {Object/Ametys.ui.fluent.ribbon.Ribbon.SearchMenu} searchMenu A search field configuration or object that will open results as a menu.
         */
        
        /**
         * @property {Ametys.ui.fluent.ribbon.Ribbon.MessageContainer} _messageContainer The container for information messages (under the ribbon)
         * @private
         */
        
        /**
         * @property {Boolean} _ribbonCollapsed The ribbon state: collapsed or expanded? Used with #_ribbonFloating.
         * @private
         */
        _ribbonCollapsed: false,
        /**
         * @property {Boolean} _ribbonFloating The ribbon state: floating over the ui or fixed? Used with #_ribbonCollapsed.
         * @private
         */
        _ribbonFloating: false,
        /**
         * @private
         * @property {Boolean} _actionPerfomed Has an action been performed since the last contextual tab was hidden.
         */
        _actionPerfomed: false,
        
        /**
         * @private
         * @property {Object} userCfg A configuration for user menu button
         */
        userCfg: {},
        
        /**
         * @cfg {Object} user Specify this to display the name of the connected user. This will create a button with user photo and name, and clicking on it will open a menu with a profile card. This object is also a configuration for a {@link Ext.button.Button} (you can for example add a menu configuration to add items under the profile card):
         * @cfg {String} user.fullName The full name of the connected user
         * @cfg {String} user.login The identifier of the user
         * @cfg {String} [user.email] The user email address
         * @cfg {String} [user.smallPhoto] The absolute path to the user photo in 16x16.
         * @cfg {String} [user.mediumPhoto] The absolute path to the user photo in 32x32.
         * @cfg {String} [user.largePhoto] The absolute path to the user photo in 48x48.
         * @cfg {String} [user.extraLargePhoto] The absolute path to the user photo in 64x64.
         */

        /**
         * @cfg {Object/Ametys.ui.fluent.ribbon.Ribbon.Notificator} notification Display the notification indicator by providing a notification configuration
         */

		/**
		 * Creates new ribbon. This class is technically just a placeholder for a real Ametys.ui.fluent.ribbon.Panel
		 * @param {Object} config The config object passed to Ametys.ui.fluent.ribbon.Panel
		 */
		constructor: function(config)
		{
            config = config || {};
            
            /**
             * @cfg {Object[]/Ext.panel.Tool[]} tools tools Doesn't apply to ribbon element.
             * @private
             */
            config.tools = [];
            
            this._messageContainer = Ext.create("Ametys.ui.fluent.ribbon.Ribbon.MessageContainer", {});
            config.dockedItems = Ext.Array.from(config.dockedItems);
            config.dockedItems.push(this._messageContainer);
            
            if (config.message)
            {
                config.message = Ext.Array.from(config.message);
                
                for (var i = 0; i < config.message.length; i++)
                {
                    var message = config.message[i];

                    this.addMessage(message);
                }
            }
            
            
            // The position of "title" between tools in the top header line
            var titlePosition = 0;
            
            // Handle the quickbar
            if  (config.quickToolbar)
            {
                if (!config.quickToolbar.isComponent)
                {
                    config.quickToolbar.xtype = config.quickToolbar.xtype || 'toolbar';
                    config.quickToolbar.ui = 'ribbon-header-toolbar';
                    config.quickToolbar.defaults = { ui: 'ribbon-header-button' };
                }
                
                config.tools.push(config.quickToolbar);
                titlePosition++;
            }
            
            config.tools.push({ 
                xtype: 'component', 
                itemId: 'separator',
                hidden: true
            }); 
            titlePosition++;

            // Handle the title
            config.title = {
                xtype: 'ametys.ribbon-title',
                text: config.title,
                applicationTitle: config.applicationTitle || this.defaultApplicationTitle
            };
            
            /**
             * @cfg {Boolean/Object} header Doesn't apply to ribbon element. 
             * @private
             */
            config.header = {
                titleAlign: 'left',
                titlePosition: titlePosition
            };
            
            // Handle the search menu
            if (config.searchMenu)
            {
                if (!config.searchMenu.isSearchMenu)
                {
                    config.searchMenu.xtype = 'ametys.ribbon-searchmenu';
                }
                config.tools.push(config.searchMenu);
            }
            
            if (config.searchMenu && config.user)
            {
                config.tools.push({
                    xtype: 'tbspacer',
                    width: 25
                });
            }
            
            // Handle user
            if (config.user)
            {
                config.user = Ext.applyIf(config.user, this.userCfg);
                
                config.user.text = config.user.fullName;
                config.user.ui = 'ribbon-linkbutton';
                config.user.menuAlign = 'tr-br?';
                config.user.menu = config.user.menu || {};
                config.user.menu.ui = 'ribbon-menu';
                config.user.menu.items = config.user.menu.items || [];
                config.user.menu.items = Ext.Array.insert(config.user.menu.items, 0, [{
                   xtype: 'component',
                   isMenuItem: true, // to be responsible to draw the whole line => this allow to be on the left of menu even with vertical separator activated.
                   html: new Ext.XTemplate(
                        "<div class='a-fluent-user-card'>",
                           "<tpl if='extraLargePhoto'><div class='photo'><img src='{extraLargePhoto}'/></div></tpl>",
                           "<div class='main'>", 
                               "<div class='name-wrapper'>", 
                                   "<div class='name'>{fullName}</div>",
                                   "<div class='login'>{login}</div>",
                               "</div>",
                               "<tpl if='email'><div class='email' title=\"{email}\">{email}</div></tpl>", 
                           "</div>",
                       "</div>").apply(config.user)
                }]);
                
                config.tools.push(Ext.apply({
                        xtype: 'button', 
                        cls: 'a-fluent-user',
                        icon: config.user.smallPhoto,
                        iconCls: 'foo' // ensure icon is set (even if there is no smallPhoto as theme will put a background image
                    },
                    config.user
                ));
            }
            
            // Handle notifications
            if (config.notification)
            {
                config.tools.push(Ext.applyIf({
                    xtype: 'ametys.ribbon-notificator'
                }, config.notification));
            }

            
            // The tabPanel
            
            // move many properties from ribbon config to tabpanel config
            var tabPanelConfig = Ext.moveTo({
                xtype: 'ametys.ribbon-tabpanel',
                
                // The default tab HAS TO be the first one according to Microsoft UI Fluent guide lines. 
                activeTab: 0,
                
                border: false,
                shadow: false,
                
                floating: true,
                autoShow: true,
                
                tabBar: {
                    listeners: {
                        'dblclick': {
                            fn: this.toggleRibbonCollapse,
                            scope: this,
                            element: 'el'
                        }
                    }
                }
            }, config, [ 'defaults', 'items', 'mainButton' ]);
            /**
             * @cfg {Object/Function} defaults This configuration is transmitting to the underlying tab panel. See Ametys.ui.fluent.ribbon.TabPanel#cfg-mainButton 
             */
            /**
             * @cfg {String} defaultType This configuration is transmitting to the underlying tab panel. See Ametys.ui.fluent.ribbon.TabPanel#cfg-mainButton 
             */
            /**
             * @cfg {Object/Object[]} items This configuration is transmitting to the underlying tab panel. See Ametys.ui.fluent.ribbon.TabPanel#cfg-mainButton 
             */
            /**
             * @cfg {Object} mainButton This configuration is transmitting to the underlying tab panel. See Ametys.ui.fluent.ribbon.TabPanel#cfg-mainButton 
             */
            
            config.items = [tabPanelConfig];
            
            this.callParent([config]);
            
            this.on('resize', this._onRibbonResize, this);
            this.on('afterlayout', this._setRibbonHeight, this);
		},
        
        /**
         * Add an information message
         * @param {Object} message See #cfg-message.
         * @return {String} The id of the newly created component containing the added message
         */
        addMessage: function(message)
        {
            var msgId = message.id || Ext.id();
            
            var items = [
                {
                    xtype: 'component',
                    ui: 'ribbon-message-title',
                    html: message.title
                },
                {
                    xtype: 'component',
                    flex: 1,
                    ui: 'ribbon-message-text',
                    html: message.text
                }
            ];
            
            if (message.closeable !== false)
            {
                
                items.push({
                    xtype: 'tool',
                    type: 'close',
                    tooltip: this.messageCloseText,
                    callback: Ext.bind(this.closeMessage, this, [msgId], false)
                });
            }
            
            var messageCmp = this._messageContainer.add({
                id: "AmetysRibbonMessage-" + msgId,
                cls: 'a-message-type-' + (message.type || 'info'),
                items: items
            });
            
            return msgId;
        },
        
        /**
         * Remove a previously added message. 
         * @param {String} msgId The component id of the message component returned by {@link #addMessage}
         */
        closeMessage: function(msgId)
        {
            this._messageContainer.remove("AmetysRibbonMessage-" + msgId);
        },
        
        getState: function()
        {
            return {
                _ribbonCollapsed: this._ribbonCollapsed
            };
        },   
	
		/**
		 * Get the underlying panel
		 * @return {Ametys.ui.fluent.ribbon.Panel} The wrapper panel
		 */
		getPanel: function()
		{
			return this.floatingItems.items[0];
		},
        
        /**
         * @private
         * We want to protect all right click, and potentially re-collapse the floating panel
         */
        afterRender: function() 
        {
            this.callParent(arguments);
            
            Ext.getDoc().on("click", this._onAnyMouseDown, this);
            Ext.getDoc().on("contextmenu", this._onAnyMouseDown, this);
            
            if (this._ribbonCollapsed)
            {
                this.collapseRibbon();
            }
        },
        
        /**
         * Listener for preventing right click on the ribbon, and potentially re-collapse the floating panel
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         * @param {Object} eOpts The options object passed to Ext.util.Observable.addListener
         */
        _onAnyMouseDown: function(e, t, eOpts) 
        {
            // We want to cancel right-click on the ribbon (but the ribbon has 2 dom root because the tabpanel is floating)
            if (Ext.fly(t).findParent("#" + this.getId()) || Ext.fly(t).findParent("#" + this.getPanel().getId()))
            {
                if (e.button != 0 && !/^(input|textarea)$/i.test(t.tagName))
                {
                    e.preventDefault();
                }
            }
            
            // We want to recollapse the ribbon when in collapse mode (but not if the click was in the tab panel
            else
            {
                if (this._ribbonCollapsed && this._ribbonFloating) 
                {
                    this._hideCollapsed();
                }
                
                if (this._actionPerfomed)
                {
                    this.getPanel()._lastContextualTabHidden = null;
                }
                
                this._actionPerfomed = true;
            }
        },
        
        /**
         * Listener when the ribbon get resized to apply new width to the floating subpanel
         * @param {Ext.Component} placeHolder The place holder
         * @param {Number} width The new width that was set.
         * @param {Number} height The new height that was set.
         * @param {Number} oldWidth The previous width.
         * @param {Number} oldHeight The previous height. 
         * @param {Object} eOpts The event options
         * @private
         */
        _onRibbonResize: function(placeHolder, width, height, oldWidth, oldHeight, eOpts)
        {
            if (width != oldWidth)
            {
                this.getPanel().setWidth(width);
            }
        },
        
        /**
         * @protected
         * Change the ribbon height to reflect its current state
         */
        _setRibbonHeight: function()
        {
            if (this.rendered)
            {
                var newHeight = this.getHeader().getHeight() 
                            + (this._ribbonCollapsed ? this.getPanel().getHeader().getHeight() : this.getPanel().getHeight()) 
                            + this._messageContainer.getHeight();

                this.setHeight(newHeight);
            }
        },
                        
        /**
         * Toggle the ribbon state
         * @chainable
         */
        toggleRibbonCollapse: function() 
        {
            this[this._ribbonCollapsed ? 'expandRibbon' : 'collapseRibbon']();
            return this;
        },

        /**
         * Enter in collapsed mode
         * @chainable
         */
        collapseRibbon: function() 
        {
            this._ribbonCollapsed = true;
            this._ribbonFloating = true;
            this.saveState();
            this._hideCollapsed();

            return this;
        },
        
        /**
         * @private 
         * When in collapsed mode but visible: hide it
         */
        _hideCollapsed: function() 
        {
            if (this._ribbonCollapsed && this._ribbonFloating) 
            {
                Ext.suspendLayouts();

                try
                {
                    this._ribbonFloating = false;
    
                    this.getPanel().body.setDisplayed(false);
                    this.getPanel().setHeight(this.getHeader().getHeight());
                    
                    // Deactivate the active tab
                    this.getPanel().getTabBar().activeTab.el.removeCls('x-tab-active');
                    if (this.getPanel().activeTab) 
                    {
                        this.getPanel().activeTab.fireEvent('deactivate', this.getPanel().activeTab);
                        this.getPanel().activeTab = null;
                    }
                }
                finally
                {
                    Ext.resumeLayouts(true);
                }
            }
        },

        /**
         * Expand the ribbon
         * @chainable
         */
        expandRibbon: function() 
        {
            this._ribbonCollapsed = false;
            this._showCollapsed(true);
            this._ribbonFloating = false;
            this.saveState();

            return this;
        },

        /**
         * @private 
         * When in collapsed mode, show it
         * @param {Boolean} force To force the collapse
         */
        _showCollapsed: function(force) 
        {
            if ((this._ribbonCollapsed && !this._ribbonFloating) || force) 
            {
                Ext.suspendLayouts();

                try
                {
                    this._ribbonFloating = true;
                    
                    this.getPanel().body.setDisplayed(true);
                    this.getPanel().setHeight(null);
                    this._setRibbonHeight(); // the panel onResize may not have do the job if the panel was floating
    
                    if (this.getPanel().getTabBar().activeTab.el.isVisible())
                    {
                        this.getPanel().setActiveTab(this.getPanel().lastActiveTab);
                        this.getPanel().getTabBar().activeTab.el.addCls('x-tab-active');
                    }
                    else
                    {
                        this.getPanel().setActiveTab(0);
                    }
                }
                finally
                {
                    Ext.resumeLayouts(true);
                }
            }
        },
        
        /**
         * Get the notificator if created
         * @return {Ametys.ui.fluent.ribbon.Ribbon.Notificator} The notification button or null
         */
        getNotificator: function()
        {
            return this.getHeader().items.last();
        }
    }
);