/*
 *  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 tool displays the description of the extraction and  the list of all available extraction files
 * @private
 */
Ext.define('Ametys.plugins.extraction.execution.ExtractionExecutionTool', {
    extend: "Ametys.tool.Tool",
    
    /**
     * @readonly
     * @property {Boolean} extractionTabCompatible Specify this tool is compatible with the 'extraction tab'.
     */
    extractionTabCompatible: true,
    
    /**
     * @private
     * @property {String} _definitionFilePath Definition file path.
     */
    
    /**
     * @private
     * @property {String} _definitionFileName Definition file name.
     */
    
    /**
     * @private
     * @property {String} _descriptionId Identifier of the description.
     */

    /**
     * @private
     * @property {Object} _author the extraction definition author
     * @property {String} _author.login the author's login
     * @property {String} _author.populationId the author's population identifier
     */
     
    /**
     * @private
     * @property {String} _canRead true if the current user can read the extraction definition
     */
     
    /**
     * @private
     * @property {String} _canWrite true if the current user can write the extraction definition
     */
     
     /**
     * @private
     * @property {String} _canDelete true if the current user can delete the extraction definition
     */

    /**
     * @private
     * @property {String} _canAssignRights true if the current user can edit rights of the extraction definition
     */
    
    /**
     * @private
     * @property {Boolean} _isConcernedByCurrentModification <code>true</code> if this tool is concerned by the las MODIFYNG sent message, <code>false</code> otherwise.
     */
    
    /**
     * @private
     * @property {String} _oldFilePath the extraction definition old file path
     */
    
    /**
     * @private
     * @property {Ext.ux.IFrame} _descriptionIFrame The iframe object containing the description
     */
    
    /**
     * @private
     * @property {Ametys.form.ConfigurableFormPanel} _executionForm The form for execution parameters
     */
    
    /**
     * @private
     * @property {String} _baseUrl The url that the description iframe should stay on
     */
    
    constructor: function(config)
    {
        this.callParent(arguments);
        
        // Bus messages listeners
        Ametys.message.MessageBus.on(Ametys.message.Message.MODIFYING, this._onDefinitionFileModifying, this);
        Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
        Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onDefinitionFileDeleted, this);
    },
    
    createPanel: function()
    {
        var descriptionIFrame = this._createDescriptionIFrame(); 
        var executionFormPanel = this._createExecutionFormPanel();
        
        return Ext.create("Ext.Panel", {
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            
            items: [descriptionIFrame, executionFormPanel]
        });
    },
    
    /**
     * @private
     * Create the panel with the description
     * @return {Ext.Panel} The description panel
     */
    _createDescriptionIFrame: function()
    {
        this._descriptionIFrame = Ext.create("Ext.ux.IFrame", {
            flex:2,
            split: true,
            minHeight: 200
        }); 
        
        this._descriptionIFrame.on ('load', this._onIframeLoad, this);
        
        return this._descriptionIFrame;
    },
    
    /**
     * @private
     * Create the panel with the execution form
     * @return {Ext.Panel} The form panel
     */
    _createExecutionFormPanel: function()
    {
        var executeButton = {
            text: "{{i18n PLUGINS_EXTRACTION_EXECUTION_TOOL_BUTTON_LABEL}}",
            handler: this._onExecute,
            scope: this,
            iconCls: 'ametysicon-play124'
        };
        
        this._executionForm =  Ext.create('Ametys.form.ConfigurableFormPanel', {
            flex: 1,
            labelAlign: 'top'
        });
        
        return Ext.create("Ext.Panel", {
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            
            flex:1,
            split: true,
            minHeight: 100,
            
            items: [{
                    xtype: 'component',
                    html: "{{i18n PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_DIALOG_DESCRIPTION}}"
                },
                this._executionForm
            ],
            
            bbar : [executeButton]
        });
    },
    
    getMBSelectionInteraction: function()
    {
        return Ametys.tool.Tool.MB_TYPE_ACTIVE;
    },
    
    /**
     * Listener when definition files are beeing modifying
     * @param {Ametys.message.Message} message The edition message.
     * @private
     */
    _onDefinitionFileModifying: function(message)
    {
        var target = message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE) || message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FOLDER);
        
        if (target != null)
        {
            this._isConcernedByCurrentModification = Ext.String.startsWith(this._definitionFilePath, target.getParameters().path);
            this._oldFilePath = target.getParameters().path;
        }
    },
    
    /**
     * Listener when definition files have been edited
     * Will refresh the tool
     * @param {Ametys.message.Message} message The edition message.
     * @private
     */
    _onModified: function(message)
    {
        if (!this._isConcernedByCurrentModification)
        {
            return;
        }
        
        var hasToRefreshTool = false;
        
        // Check if the folder of the current extraction has been modified
        var folderTarget = message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FOLDER);
        if (folderTarget != null)
        {
            this._definitionFilePath = this._definitionFilePath.replace(this._oldFilePath, folderTarget.getParameters().path);
            
            hasToRefreshTool = true;
        }
        
        // Check if the definition of the current extraction has been modified
        var definitionFileTarget = message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE);
        if (definitionFileTarget != null)
        {
            this._definitionFilePath = definitionFileTarget.getParameters().path;
            this._definitionFileName = definitionFileTarget.getParameters().name;
            
            // Get the description id from the target
            var descriptionTarget = definitionFileTarget.getSubtarget(Ametys.message.MessageTarget.CONTENT);
            var description = descriptionTarget ? descriptionTarget.getParameters().content : undefined;
            this._descriptionId = description ? description.getId() : undefined
            
            this._definitionAuthor = definitionFileTarget.getParameters().author;
            this._definitionCanRead = definitionFileTarget.getParameters().canRead;
            this._definitionCanWrite = definitionFileTarget.getParameters().canWrite;
            this._definitionCanDelete = definitionFileTarget.getParameters().canDelete;
            
            hasToRefreshTool = true;
        }
        
        // Check if the description of the current extraction has been modified 
        var contentTarget = message.getTarget(Ametys.message.MessageTarget.CONTENT, 1);
        if (contentTarget != null)
        {
            var content = contentTarget.getParameters().content;
            var contentId = content ? content.getId() : undefined
            
            if (contentId == this._descriptionId)
            {
                hasToRefreshTool = true;
            }
        }
        
        if (hasToRefreshTool)
        {
            this.refresh();
        }
    },
    
    /**
     * Listener when definition files have been deleted
     * @param {Ametys.message.Message} message The deletion message.
     * @private
     */
    _onDefinitionFileDeleted: function(message)
    {
        var target = message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE) || message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FOLDER);
        
        if (target != null)
        {
            if (Ext.String.startsWith(this._definitionFilePath, target.getParameters().path))
            {
                Ametys.tool.ToolsManager.removeTool(this);
            }
        }
    },
    
    sendCurrentSelection: function()
    {
        var targetParameters = {
            path: this._definitionFilePath,
            name: this._definitionFileName,
            descriptionId: this._descriptionId,
            author: this._author,
            canRead: this._canRead,
            canWrite: this._canWrite,
            canDelete: this._canDelete,
            canAssignRights: this._canAssignRights
        }
        
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.SELECTION_CHANGED,
            targets: {
                id: Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE,
                parameters: targetParameters
            }
        });
    },
    
    setParams: function(params)
    {
        this.callParent(arguments);
        this._definitionFilePath = params['id'];
        this._definitionFileName = params['name'];
        this._descriptionId = params['descriptionId'];
        this._author = params['author'];
        this._canRead = params['canRead'];
        this._canWrite = params['canWrite'];
        this._canDelete = params['canDelete'];
        this._canAssignRights = params['canAssignRights'];
        this.refresh();
    },
    
    refresh: function()
    {
        this.showRefreshing();
        
        // Change the title of the tool to have the extraction name
        this.setTitle(this._definitionFilePath);
        
        // Refresh the description panel
        if (this._descriptionId)
        {
            this._descriptionIFrame.show();
            
            var wrappedUrl = this.getWrappedContentUrl ();
            this._descriptionIFrame.load(Ametys.CONTEXT_PATH + wrappedUrl)
            this._baseUrl = Ametys.CONTEXT_PATH + wrappedUrl;
        }
        else
        {
            this._descriptionIFrame.hide();
        }
        
        // Refresh the execution form
        var encodedDefinitionFilePath = encodeURIComponent(this._definitionFilePath);
        this.serverCall('getExecutionParameters', [encodedDefinitionFilePath], Ext.bind(this._refreshCb, this));
    },
    
    /**
     * Get the wrapped url for content
     * @returns {String} The wrapped url
     */
    getWrappedContentUrl: function()
    {
        var additionParams = '';
        
        additionParams += '&viewName=main'
        additionParams += '&lang=' + Ametys.cms.language.LanguageDAO.getCurrentLanguage(); // default rendering language for multilingual content
        
        // FIXME CMS-2316
        var appParameters = Ametys.getAppParameters();
        
        Ext.Object.each(appParameters, function(key, value) {
            additionParams += '&' + key + '=' + encodeURIComponent(value);
        });
        
        return '/_wrapped-content.html?contentId=' + this._descriptionId + additionParams;
    },
    
    /**
     * @private
     * Listener called when the iframe is loaded.
     * Protects the iframe by handling links of the internal frame by setting a onclick on every one
     * @param {Ext.ux.IFrame} iframe The iframe
     */
    _onIframeLoad: function(iframe)
    {
        if (window.event && window.event.srcElement.readyState && !/loaded|complete/.test(window.event.srcElement.readyState))
        {
            return;
        }
        
        Ametys.plugins.cms.content.tool.ContentTool.__URL_REGEXP.test(window.location.href);
        
        var win = this._descriptionIFrame.getWin();
        
        var outside = false;
        try
        {
            win.location.href
        }
        catch (e)
        {
            outside = true;
        }
        
        if (outside || (win.location.href != 'about:blank' && win.location.href.indexOf(RegExp.$1 + this._baseUrl) != 0))
        {
            var outsideUrl = win.location.href;
            // Back to iframe base url
            win.location.href = this._baseUrl;
            // Open ouside url in a new window
            window.open(outsideUrl);
        }
        else
        {
            var links = win.document.getElementsByTagName("a");
            for (var a = 0; a < links.length; a++)
            {
                if (links[a].getAttribute("internal") == null && !links[a].onclick)
                {
                    links[a].onclick = Ext.bind(this._handleLink, this, [links[a]], false);
                }
            }
            
            this._descriptionIFrame.getWin().onfocus = Ext.bind(this._onIframeFocus, this);
        }
    },
    
    /**
     * @private
     * Lazy handling of links. Each times a link is clicked, check if we should open it in a window, open another tool...
     * @param {HTMLElement} link The clicked link
     */
    _handleLink: function(link)
    {
        var currentURI = this._descriptionIFrame.getWin().location.href;
        
        var absHref = link.href;
        var relHref = null;
        if (/^(https?:\/\/[^\/]+)(\/.*)?/.test(absHref) && absHref.indexOf(RegExp.$1 + Ametys.CONTEXT_PATH) == 0)
        {
            relHref = absHref.substring(absHref.indexOf(RegExp.$1 + Ametys.CONTEXT_PATH) + (RegExp.$1 + Ametys.CONTEXT_PATH).length);
        }

        // Internal link
        if (absHref.indexOf(currentURI) == 0) return true;
        
        // JS Link
        if (absHref.indexOf("javascript:") == 0) return true;
        
        // Download link
        if (relHref != null && relHref.indexOf("/plugins/explorer/download/") == 0) return true;

        // Unknown link : open in a new window
        window.open(absHref);
        return false;
    },
    
    /**
     * @private
     * Listener when iframe takes the focus.<br/>
     * Force the focus on tool.
     */
    _onIframeFocus: function()
    {
        // Force focus on tool
        this.focus();
    },
    
    /**
     * @private
     * Callback called when the execution for parameters are retrieved
     * @param {Object} formConfiguration configuration of the execution form
     */
    _refreshCb: function(formConfiguration)
    {
        this._executionForm.destroyComponents();
        this._executionForm.configure(formConfiguration);
        this._executionForm.setValues({});
        
        this.showRefreshed();
    },
    
    /**
     * @private
     * Launch the extraction execution if the form is valid
     */
    _onExecute: function()
    {
        if (this._executionForm && !this._executionForm.isValid())
        {
            return;
        }

        var values = this._executionForm.getJsonValues();
        var variablesValues = {};
        var recipient;
        var pipelineId;
        
        Ext.Object.each(values, function(key, value) {
            
            switch (key)
            {
                case 'recipient':
                    recipient = value;
                    break;
                case 'pipeline':
                    pipelineId = value;
                    break;
                default:
                    variablesValues[key] = value;
                    break;
            }
        });
        
        this.getContentPanel().mask();
        this.serverCall('executeExtraction', [this._definitionFilePath, variablesValues, recipient, pipelineId], Ext.bind(this._executionLaunchedCb, this), { ignoreCallbackOnError: false });
    },
    
    /**
     * @private
     * Callback called when the extraction has been launched
     * @param {Object} response Server response
     */
    _executionLaunchedCb: function(response)
    {
        this.getContentPanel().unmask();
        if (response && response['error'])
        {
            Ametys.Msg.show({
                title: "{{i18n plugin.core:PLUGINS_CORE_SCHEDULER_ERROR_SCHEDULER_TITLE}}",
                msg: "{{i18n plugin.core:PLUGINS_CORE_SCHEDULER_ERROR_SCHEDULER}}",
                buttons: Ext.Msg.OK,
                icon: Ext.MessageBox.ERROR
            });
        }
        else
        {
            Ametys.notify({
                title: "{{i18n PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_LAUNCHED_NOTIF_TITLE}}",
                description: "{{i18n PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_LAUNCHED_NOTIF_DESCRIPTION}}"
            });
        }
    }
});