/*
 *  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 search menu for the ribbon panel.
 * @inheritdoc
 * @private
 */
Ext.define(
    "Ametys.ui.fluent.ribbon.Ribbon.SearchMenu",
    {
        extend: "Ext.form.field.Text",
        alias: 'widget.ametys.ribbon-searchmenu',
        
        /** 
         * @cfg {String} [emptyText="Tell me what you want to do..."] @inheritdoc. 
         */

        /** 
         * @cfg {Object/Object[]} items The menu items to search into
         * @cfg {String/String[]} items.ELEMENT.keywords Terms that will be part of the search (additionnaly to 'text' configuration)
         */
        
        /**
         * @cfg {Boolean} allowSearch When true, a menu item is added at the end to be able to search in the HelpTool.
         */
        /**
         * @property {Boolean} allowSearch True if search is allowed. See #cfg-allowSearch
         * @private
         */
        
        /**
         * @private
         * @cfg {String} ui Not overidable
         */
        ui: 'ribbon-header-text',
         
        /**
         * @property {String} searchMenuCls The CSS classname to set on the search menu field
         * @readonly
         * @private
         */
        searchMenuCls: 'a-fluent-searchmenu',
        
        /**
         * @property {String} searchMenuDefaultText If Ametys.ui.fluent.ribbon.Ribbon#cfg-searchMenu is not a string, this text will be used as default value)
         * @readonly
         * @private
         */
        searchMenuDefaultText: "{{i18n plugin.core-ui:PLUGINS_CORE_UI_WORKSPACE_AMETYS_RIBBON_SEARCHMENU_PLACEHOLDER}}",
        /**
         * @property {String} searchMenuHelpItem A template string for the final item menu to open help with that search. An object with a string property "text" is provided to the template.
         * @readonly
         * @private
         */
        searchMenuHelpItem: "{{i18n plugin.core-ui:PLUGINS_CORE_UI_WORKSPACE_AMETYS_RIBBON_SEARCHMENU_SEARCHITEM_LABEL}}",
        
        /**
         * @readonly
         * @private
         * @property {Number} searchMenuMaxResults The max number of results displayed
         */
        searchMenuMaxResults: 15,
        
        /**
         * @readonly
         * @private
         * @property {Number} searchMenuMaxDisabledResults The max number of results which are disabled controllers displayed
         */
        searchMenuMaxDisabledResults: 5,
        
        /**
         * @private
         * @property {Ext.menu.Menu} _menu The menu instance for search results.
         */
        
        /**
         * @readonly
         * @private
         * @property {Number} searchAfterTime Time in millisecond to wait after last change to launch a search
         */
        searchAfterTime: 500,
        
        /**
         * @property {Number} codeStep The current step for "the code"
         */
        codeStep: 0, 
        
        /**
         * @private
         * @property {Array} _lastResults The results of the last search by lunr.
         */
        
        constructor: function(config) 
        {
            var me = this;
            
            config = config || {};
            
            config.cls = Ext.Array.from(config.cls);
            config.cls.push(this.searchMenuCls);
            
            config.emptyText = config.emptyText || this.searchMenuDefaultText;
            
            config.items = Ext.Array.from(config.items);
            config.items.push({
            	id: "enabledControllersGroup",
            	text: "<b>{{i18n PLUGINS_CORE_UI_WORKSPACE_AMETYS_RIBBON_SEARCHMENU_ITEMS_AVAILABLE}}</b>",
            	hideOnClick: false,
            	focusable: false
            }, {
            	xtype: 'menuseparator',
            	id: 'groupsSeparator'
            }, {
            	id: "disabledControllersGroup",
            	text: "<b>{{i18n PLUGINS_CORE_UI_WORKSPACE_AMETYS_RIBBON_SEARCHMENU_ITEMS_UNAVAILABLE}}</b>",
            	hideOnClick: false,
            	focusable: false
            });
            
            this.searchMenuHelpItem = Ext.create("Ext.Template", this.searchMenuHelpItem);

            if (config.allowSearch)
            {
                this.allowSearch = true;
                config.items.push("-");
                config.items.push({
                	id: 'openHelpButton',
                    handler: this._openHelp,
                    scope: this
                });
                delete config.allowSearch;
            }
            
            config.width = config.width | 200; 
            
            this._menu = Ext.create("Ext.menu.Menu", {
                defaultAlign: "tr-br",
                ui: 'ribbon-menu',
                listeners: {
                    'close': this._onMenuClose,
                    scope: this
                },
                items: config.items
            })
            delete config.items;
            
            this.callParent([config]);
            
            this.on('click', this._reopenLastSearchIfAvailable, this, { element: 'inputEl' });
            this.on('change', Ext.Function.createBuffered(this._searchNow, this.searchAfterTime, this));
            this.on('specialkey', this._onSpecialKey);
            this.on('render', this._setMinWidthMenu, this);
            this.on('blur', this._onBlur, this);
        },
        
        /**
         * @private
         * Set the minimum width of the menu to the value of the search menu width
         */
        _setMinWidthMenu: function()
        {
        	this._menu.setMinWidth(this.getWidth());
        },
        
        /**
         * @private
         * On blur we should remove value if menu is closed
         */
        _onBlur: function() 
        {
            if (!this._menu.isVisible())
            {
                this.reset();
            }
        },
        
        /**
         * @private
         * When the menu is closed
         */
        _onMenuClose: function()
        {
            if (!this.hasFocus)
            {
                this.reset();
            }
        },
        
        /**
         * @private
         * Open the help
         */
        _openHelp: function()
        {
            Ametys.tool.ToolsManager.openTool("uitool-help", {searchQuery: this.getValue()});
        },
        
        /**
         * @private
         * Event when a special key is pressed
         * @param {Ext.form.field.Text} input The search input
         * @param {Ext.event.Event} event The key event
         */
        _onSpecialKey: function(input, event)
        {
            if (event.getKey() == event.UP)
            {
                if (this.codeStep == 0 || this.codeStep == 1)
                {
                    this.codeStep++;
                }
                else
                {
                    this.codeStep = 0;
                }
            }
            else if (event.getKey() == event.LEFT)
            {
                if (this.codeStep == 4 || this.codeStep == 6)
                {
                    this.codeStep++;
                }
                else
                {
                    this.codeStep = 0;
                }
            }
            else if (event.getKey() == event.RIGHT)
            {
                if (this.codeStep == 5 || this.codeStep == 7)
                {
                    this.codeStep++;
                }
                else
                {
                    this.codeStep = 0;
                }
            }
            else if (event.getKey() == event.DOWN) 
            {
                if (this.codeStep == 2 || this.codeStep == 3)
                {
                    this.codeStep++;
                }
                else
                {
                    this.codeStep = 0;
                    this._reopenLastSearchIfAvailable();
                    this._menu.focus();
                }
            }
            else
            {
                this.codeStep = 0;
            }
        },
        
        /**
         * @private
         * Launch a search now
         */
        _searchNow: function()
        {
            var me = this;
            
            if (!this.getValue())
            {
                this._menu.hide();
                return;
            }
            
            if (this.getValue().toLowerCase() == 'ba' && this.codeStep == 8) 
            {
                this.codeStep = 0;
                this._loadCompanions();
                this.reset();
                return;
            }
            
            if (this.allowSearch)
            {
                // Update the search menu items
                var value = this.getValue();
                if (value.length > 22)
                {
                    value = value.substring(0, 20) + "…";
                }
                this._menu.items.getByKey("openHelpButton").setText(this.searchMenuHelpItem.apply({ text: value }));
            }
            
            // Search with lunr.js the relevant controllers ids
            var foundControllers = lunr.controllersIndex.search(this.getValue());
            this._lastResults = foundControllers;
            
            this._displayResults(foundControllers);
        },
        
        /**
         * @private
         * Display the results of the query
         * @param {Array} controllers The controllers found by lunr
         */
        _displayResults: function(controllers)
        {
        	var me = this;
        	
        	// Browse all items for hiding them
            this._menu.items.each(function(item) {
            	if (item.getId() != "openHelpButton" && Ext.ClassManager.getName(item) != "Ext.menu.Separator")
            	{
            		item.hide();
            	}
            });
        	
        	// Retrieve the controllers and put the top 'searchMenumaxResults' results in arrays
            var enableItems = [],
            	disableItems = [];
            Ext.each(controllers, function(controller, index) {
            	if (enableItems.length + disableItems.length >= this.searchMenuMaxResults) {return false;}
            	
            	var menuItem = this._menu.items.getByKey( controller.ref );
            	if (!menuItem.isDisabled())
            	{
            		enableItems.push(menuItem);
            	}
            	else if (menuItem.isDisabled() && disableItems.length < this.searchMenuMaxDisabledResults)
            	{
	            	disableItems.push(menuItem);
            	}
            }, this);
            
            // Finally reverse the lists, put the items at the top and show them
            Ext.Array.forEach(disableItems.reverse(), function (item) {
            	me._menu.insert(0, item);
                item.show();
            });
            var disableGroup = me._menu.items.getByKey('disabledControllersGroup');
            me._menu.insert(0, disableGroup);
            disableGroup.setVisible(disableItems.length > 0);
            
            var separator = me._menu.items.getByKey('groupsSeparator');
            me._menu.insert(0, separator);
            separator.setVisible(disableItems.length > 0 && enableItems.length > 0);
            
            Ext.Array.forEach(enableItems.reverse(), function (item) {
            	me._menu.insert(0, item);
                item.show();
            });
            var enableGroup = me._menu.items.getByKey('enabledControllersGroup');
            me._menu.insert(0, enableGroup);
            enableGroup.setVisible(enableItems.length > 0);
            
            // Show the open help button separator (the item just before the last one)
            if (this.allowSearch)
            {
                this._menu.items.get(this._menu.items.getCount() - 2).setVisible(controllers.length > 0);
            }
            
            this._menu.showBy(this);
        },
        
        /**
         * @private
         * Load companions
         */
        _loadCompanions: function(msg)
        {
            var me = this;
            if (!me._agents)
            {
                me._agents = {};
                me._currentAgent = null;
                me._companions = ['Bonzi', 'Clippy', 'F1', 'Genie', 'Genius', 'Links', 'Merlin', 'Peedy', 'Rocky', 'Rover'];
                
                Ext.override(Ametys.ui.fluent.ribbon.Ribbon.Notificator, {
                    notify: function(config)
                    {
                        this.callParent(arguments);
                        
                        if (me._currentAgent)
                        {
                            var text = config.isModel ? 
                                (config.get('title')) : 
                                (config.title);
                            me._currentAgent.speak(text);
                        }
                    }
                });
                
                Ext.override(Ametys.ui.fluent.ribbon.Ribbon.Notificator.Toast, {
                    show: function()
                    {
                        if (!me._currentAgent)
                        {
                            this.callParent(arguments);
                        }                        
                        else
                        {
                            Ext.destroy(this);
                        }
                    }
                });
            }
            
            function loadAgentRnd()
            {
                if (me._currentAgent != null)
                {
                    me._currentAgent.hide();
                    me._currentAgent = null;
                    return;
                }
                
                var index = Math.floor(Math.random() * me._companions.length);
                
                
                if (me._agents[me._companions[index]])
                {
                    me._agents[me._companions[index]].show();
                    me._currentAgent =  me._agents[me._companions[index]];
                }
                else
                {
                    clippy.load(me._companions[index], function (agent) {
                        me._agents[me._companions[index]] = agent;
                        
                        agent.show() 
                        me._currentAgent = agent;
                    });
                }
            }
            
            if (!this._companionsInitialization)
            {
                /**
                 * @private
                 * @property {Boolean} _backLoadInitialized Was the back load already initialize
                 */
                this._companionsInitialization = 'loading';
                
                Ametys.loadStyle("//clippy.js.s3.amazonaws.com/clippy.css");
                if (!window.jQuery)
                {
                    Ametys.loadScript("//code.jquery.com/jquery-1.11.3.min.js");
                }
                Ametys.loadScript("//clippy.js.s3.amazonaws.com/clippy.js", function() {
                    me._companionsInitialization = 'loaded';
                    clippy.BASE_PATH = '//clippy.js.s3.amazonaws.com/Agents/';
                    
                    loadAgentRnd();
                });
            }
            else if (this._companionsInitialization == 'loaded')
            {
                loadAgentRnd();
            }
             
        },
        
        /**
         * @private
         * Reshow last menu
         */
        _reopenLastSearchIfAvailable: function()
        {
            if (this.getValue() && this._lastResults)
            {
            	this._displayResults(this._lastResults);
            }
        }
    }
);