/*
 *  Copyright 2019 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.
 */

/**
 * This class provides a tree panel for ODF contents.<br>
 * Add the hability to display indicators on tree nodes.
 */
Ext.define('Ametys.plugins.odf.tree.ODFContentsTreePanel', {
    extend: "Ametys.plugins.contentstree.ContentsTreePanel",
    
    statics: {
        
        /**
         * Default function to determine if an indicator is available for a record
         * @param {String} id the id of indicator
         * @param {Ext.data.Record} record the record
         * @return {Boolean} true if the indicator is available for this record
         */
        matchIndicator: function(id, record)
        {
            return record.get(id) != null;
        },
        
        /**
         * Default function to display an active indicator
         * @param {Ametys.plugins.odf.tree.ODFContentsTreePanel} tree the tree
         * @param {String} id the id of indicator
         * @param {Ext.data.Record} record the record
         * @return {String|HTML} The display value 
         */
        applyIndicator: function(tree, id, record)
        {
            var data = record.get(id);
            return `<span data-qtip="${data.tooltip || ''}" class="${tree.indicatorCls} ${data.cssClass || ''}">${data.text || ''}</span>`;
        }
    },
    
    /**
     * @property {String} indicatorCls The css class name for indicators
     */
    indicatorCls: "odf-indicator",
    /**
     * @property {String} loadingCls The css class loading state
     */
    loadingCls: 'loading',
    
    /**
     * @property {String} resultOkCls The css class when result is ok
     */
    validationCls: 'validation-panel',
    
    /**
     * @property {String} validationOkCls The css class when validation is ok
     */
    validationOkCls: 'validation-ok',
    
    /**
     * @property {String} validationInfoCls The css class when validation contains some warnings
     */
    validationInfoCls: 'validation-info',
    
    /**
     * @cfg {Object[]} indicators The configuration of available indicators
     */
    /**
     * @property {Object[]} _indicators See {@link #cfg-indicators}.
     * @private
     */
    
    /**
     * @cfg {Object[]} activeIndicators The current active indicators
     */
    /**
     * @private
     * @property {Object[]} _activeIndicators The active indicators
     */
    _activeIndicators: [],
    
    /**
     * @cfg {Boolean} [enableIndicatorMenu=true] False to hide the indicator menu
     */
    
    /**
     * @private
     * @property {Ext.Template} _boxLabelWithGlyphTpl Template for inserting the icon as a glyph
     */
    _boxLabelWithGlyphTpl: Ext.create('Ext.Template', '<span class="{iconGlyph}" style="margin-right: 5px"/><span>{label}</span>'),
    
    /**
     * @private
     * @property {Ext.Template} _resultTxtTpl Template for result panel
     */
    _resultTxtTpl: Ext.create('Ext.XTemplate', '<span class="icon {iconGlyph}"></span><span data-qtip="{text}">{text}</span>'),
    
    /**
     * @private
     * @property {Ext.Container} _validationResultPanel The panel to launch and see the global validation state.
     */
    
    /**
     * @cfg {Boolean} [enableValidationCheck=false] Set to 'true' to enable the global validation state on the root content
     */
    
    constructor: function(config)
    {
        this._indicators = config.indicators || [];
        this._activeIndicators = config.activeIndicators || ['odf-indicator-code'];
        
        if (config.displayToolbar)
        {
            config.dockedItems = config.dockedItems || [];
            
            if (config.enableValidationCheck)
            {
                config.toolbarPosition = 1;
                 
                // panel for launch and see validation state
                this._validationResultPanel = this._getValidationPanel();
                config.dockedItems.push(this._validationResultPanel);
            }
        }
        
        this.callParent(arguments);
        
        var me = this;
        var view = this.view.lockedView || this.view;
        
        var tpl = new Ext.XTemplate(view.cellTpl.html.replace('</div></td>', 
            '<tpl for="values.column.getItemId().startsWith(\'tree\') ? this.getActiveIndicators() : []">' // startsWith(\'tree\') to apply to the main column only
                + '<tpl if="this.matchIndicator(values, parent.record)">' 
                    + '{% out.push(this.applyIndicator(values, parent.record)) %}'
                 + '</tpl>' 
            + '</tpl>'
            + '</div></td>'), 
            {
                priority: view.cellTpl.priority,
                getActiveIndicators: function() {
                    // We could directly return me activeIndicators; but the order would change following the order of clicking on the checkbox
                    return me._indicators.map(function(i) { return i.id;})
                                         .filter(function(i) { return me._activeIndicators.indexOf(i) != -1; });
                },
                matchIndicator: function(id, record) {
                    var indicator = Ext.Array.findBy(me._indicators, function(v) { return v.id == id;});
                    return eval(indicator.matchFn || 'Ametys.plugins.odf.tree.ODFContentsTreePanel.matchIndicator').call(me, id, record)
                },
                applyIndicator: function(id, record) {
                    var indicator = Ext.Array.findBy(me._indicators, function(v) { return v.id == id;});
                    return eval(indicator.applyFn || 'Ametys.plugins.odf.tree.ODFContentsTreePanel.applyIndicator').call(me, me, id, record)
                }
            });
        if (this.view.lockedView)
        {
            this.view.lockedView.cellTpl = tpl;
        }
        else
        {
            this.view.cellTpl = tpl;
        }
    },
    
    /**
     * @private
     */
    _getValidationPanel: function ()
    {
        return Ext.create({
            dock: 'top',
            xtype: 'container',
            hidden: false,
            layout: {
                type: 'hbox',
                align: 'middle'
            },
            cls: this.validationCls,
            items: [
	            {
                    flex: 1,
	                xtype: 'panel',
                    layout: 'card',
                    activeItem: 1,
                    items: [
                        // button to check validation
	                    {
                            xtype: 'button',
                            handler: this.checkValidationState,
                            scope: this,
                            text: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_BTN}}",
                            tooltip: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_BTN}}"
	                    },
                        // validation ok
                        {
                            xtype: 'container',
                            html: this._resultTxtTpl.apply({iconGlyph: 'ametysicon-sign-check-black', text: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_OK}}"})
                        },
                        // invalidated contents
                        {
                            xtype: 'container',
                            html: this._resultTxtTpl.apply({iconGlyph: 'ametysicon-sign-info', text: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_INVALIDATED_CONTENTS}}"})
                        }
                    ]
	            },
                {
                    // show invalidated contents
                    xtype: 'button',
                    hidden: true,
                    iconCls: 'ametysicon-body-part-eye',
                    tooltip: "{{i18n PLUGINS_ODF_CONTENTS_TREE_SHOW_INVALIDATED_CONTENTS}}",
                    scope: this,
                    handler: this.showInvalidatedContents
                },
	            {
                    // check validation state
	                xtype: 'button',
                    hidden: true,
	                iconCls: 'ametysicon-arrow-circle-right-double',
	                tooltip: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE}}",
	                scope: this,
	                handler: this.checkValidationState
	            }
            ]
        });
    },
    
    applyState: function (state)
    {
        this.callParent(arguments);
        
        var me = this;
        this._activeIndicators = state.activeIndicators || ['odf-indicator-code'];
        
        var indicatorMenu = this.down('toolbar > button[itemId="indicators-menu"]');
        if (indicatorMenu)
        {
            indicatorMenu.getMenu().items.each(function(item) {
                item.setChecked(Ext.Array.contains(me._activeIndicators, item.name), true);
            });
        }
    },
    
    getState: function ()
    {
        var state = this.callParent(arguments) || {};
        // save indicators 
        state.activeIndicators = this._activeIndicators
        return state;
    },
    
    _getToolBarConfig: function (config)
    {
        var toolbarCfg = this.callParent(arguments);
        
        if (this._indicators.length > 0 && config.enableIndicatorMenu !== false)
        {
            var me = this;
            var menuItems = [];
            Ext.Array.forEach(this._indicators, function(indicator) {
                menuItems.push({
                    xtype: 'menucheckitem',
                    iconCls: indicator.iconGlyph,
                    text: indicator.label,
                    tooltip: indicator.description,
                    name: indicator.id,
                    itemId: indicator.id,
                    checkHandler: me._selectIndicator,
                    checked: Ext.Array.contains(me._activeIndicators, indicator.id),
                    scope: me
                })
            });
            
            toolbarCfg.items.push({
                tooltip: "{{i18n PLUGINS_ODF_CONTENTS_TREE_INDICATORS}}",
                iconCls: 'a-btn-glyph ametysicon-puzzle33 size-16',
                cls: 'a-btn-light',
                itemId: 'indicators-menu',
                menu: {
                    xtype: 'menu',
                    items: menuItems
                }
            });
        }
        
        return toolbarCfg;
    },
    
    /**
     * @private
     * Listener when an indicator is checked/unchecked
     * @param {Ext.menu.CheckItem} item the item
     * @param {Boolean} checked the checked status
     */
    _selectIndicator: function(item, checked)
    {
        var hasChanges = false;
        if (checked && !Ext.Array.contains(this._activeIndicators, item.name))
        {
            this._activeIndicators.push(item.name);
            hasChanges = true;
        }
        else if (!checked && Ext.Array.contains(this._activeIndicators, item.name))
        {
            Ext.Array.remove(this._activeIndicators, item.name);
            hasChanges = true;
        }
        
        if (hasChanges)
        {
            this.saveState();
            this.view.refresh();
        }
    },
    
    updateNodeUI: function(node)
    {
        this.callParent(arguments);
        
        this.view.refreshNode(node);
    },
    
    /**
     * Reset the validation state result
     * @param {Boolean} [refreshing] true to set refreshing
     */
    resetValidationState: function(refreshing)
    {
        this._invalidatedContents = [];
        
        this._validationResultPanel.removeCls([this.validationOkCls, this.validationInfoCls]);
        
        var cardResult = this._validationResultPanel.items.get(0);
        cardResult.setActiveItem(0);
        
        var btn = cardResult.items.get(0);
        if (refreshing)
        {
            btn.addCls(this.loadingCls);
	        btn.setText("{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_WAITING}}");
	        btn.setDisabled(true);
        }
        else
        {
            btn.removeCls(this.loadingCls);
	        btn.setText("{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_BTN}}");
	        btn.setDisabled(false);
        }
        
        // hide buttons
        this._validationResultPanel.items.get(1).setVisible(false);
        this._validationResultPanel.items.get(2).setVisible(false);
    },
    
    /**
     * Check the validation state of the root content
     */
    checkValidationState:function()
    {
        this.resetValidationState(true);
        
        var contentId = this.getRootNode().get('contentId');
        
        Ametys.data.ServerComm.callMethod({
            role: "org.ametys.odf.workflow.ODFWorkflowHelper",
            methodName: "getGlobalValidationStatus",
            parameters: [contentId],
            callback: {
                handler: this._checkValidationStateCb,
                scope: this,
                arguments: [contentId]
            },
            waitMessage: false,
            errorMessage: "{{i18n PLUGINS_ODF_WORKFLOW_ACTION_CHECK_VALIDATION_ACTION_ERROR}}"
        });
    },
    
    /**
     * @private
     * Callback function after retrieving the validation state
     * @param {Object} result the result
     * @param {Object[]} args the callback arguments
     */
    _checkValidationStateCb: function(result, args)
    {
        var contentId = args[0];
        if (contentId != this.getRootNode().get('contentId'))
        {
            // The current root was changed during process, ignore the response.
            return;
        }
        
        var cardResult = this._validationResultPanel.items.get(0);
        if (result.globalValidated)
        {
            this._validationResultPanel.addCls(this.validationOkCls);
            cardResult.setActiveItem(1);
        }
        else
        {
            this._invalidatedContents = result.invalidatedContents;
            this._validationResultPanel.addCls(this.validationInfoCls);
            cardResult.setActiveItem(2);
            this._validationResultPanel.items.get(1).setVisible(true);
        }
        
        this._validationResultPanel.items.get(2).setVisible(true);
    },
    
    /**
     * Show the list of elements needing validation
     */
    showInvalidatedContents: function()
    {
        Ametys.odf.helper.Content.showInvalidatedContents({
            id: this.getRootNode().get('contentId'),
            title: this.getRootNode().get('title'),
            invalidatedContents: this._invalidatedContents
        });
    }
});