/*
 *  Copyright 2021 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 all forms in READ access for current user
 * @private
 */
Ext.define('Ametys.plugins.forms.tool.FormTool', {
    extend: "Ametys.tool.Tool",
    
    constructor: function(config)
    {
        this.callParent(arguments);
        Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onDeletionMessage, this);
        Ametys.message.MessageBus.on(Ametys.plugins.forms.tool.FormsPreviewTool.SELECTION_PREVIEW_CHANGED, this._onSelectionPreviewChanged, this);
        Ametys.message.MessageBus.on(Ametys.message.Message.MOVED, this._onMoved, this);
        Ametys.message.MessageBus.on(Ametys.message.Message.SELECTION_CHANGED, this._onMessageSelectionChanged, this);
    },
    
    setParams: function (params)
    {
        this.callParent(arguments);
        this._formId = this.getParams().id;
        
        this.refresh();
    },
    
    refresh: function()
    {
        this.showRefreshing();
        this._tree.initRootNodeParameter(Ext.bind(this._loadTree, this), this._formId);
    },
    
    /**
     * @private
     * Load the tree
     */
    _loadTree: function()
    {
        this._tree.getStore().load({
            scope: this,
            callback: Ext.bind(this._refreshCb, this)
        });
    },
    
    /**
     * @private
     * Callback function after (re)loading the tree
     */
    _refreshCb: function ()
    {
        this.showRefreshed();
        
        // Select first page
        this._tree.getSelectionModel().select(this._tree.getRootNode().firstChild);
    },
    
    getMBSelectionInteraction: function() 
    {
        return Ametys.tool.Tool.MB_TYPE_ACTIVE;
    },

    onFocus: function()
    {
        this._previousSelection = Ametys.message.MessageBus.getCurrentSelectionMessage().getTargets();
        Ametys.plugins.forms.tool.FormTool.superclass.onFocus.call(this);
    },
    
    sendCurrentSelection: function()
    {
        var selection = this._tree.getSelectionModel().getSelection();
        if(selection != null && selection.length > 0){
            
            var currentRecord = selection[0]; //multiselection is not supported
            var target = this._getMessageTargetConfiguration(currentRecord) || [],
                realTarget;
            
            if(currentRecord.isRoot())
            {
                realTarget = target;
            }
            else if(currentRecord.getData().type == "page")
            {
                var formNode = this._tree.getStore().getNodeById(currentRecord.get('parentId'));
                
                realTarget = this._getMessageTargetConfiguration(formNode);
                realTarget.subtargets = target;
            }
            else if(currentRecord.getData().type == "question")
            {
                var pageNode = this._tree.getStore().getNodeById(currentRecord.get('parentId'));
                var formNode = this._tree.getStore().getNodeById(pageNode.get('parentId'));
                
                realTarget = this._getMessageTargetConfiguration(formNode);
                var subtarget = this._getMessageTargetConfiguration(pageNode);
                subtarget.subtargets = target;
                realTarget.subtargets = subtarget;
            }
        }
        
        Ext.create("Ametys.message.Message", {
            type: Ametys.message.Message.SELECTION_CHANGED,
            targets: realTarget,
            parameters: {}
        });
    },
    
    createPanel: function()
    {
        this._tree = Ext.create("Ametys.plugins.forms.tree.FormTree", { 
            rootVisible: true,
            stateful: true,         
            stateId: this.self.getName() + "$tree",     
            scrollable: true,
            indicators: this._getIndicators(),
            cls: 'uitool-form',
            
            listeners: {                
                beforerender: this._onBeforeRender,
                selectionchange: this.sendCurrentSelection,
                itemdblclick: function(view, record){
                    this._openPreviewTool(view, record);
                    if (record.get("type") == 'question' && record.data.canWrite)
                    {
                        this._openQuestionDialog(record.data);
                    }
                },
                scope: this
            },
            
            viewConfig: {
                plugins: {
                    ptype: 'ametystreeviewdragdrop',
                    containerScroll: true,
                    appendOnly: false,
                    sortOnDrop: false,
                    expandDelay: 500,
                    allowContainerDrops: false,
                    setAmetysDragInfos: Ext.bind(this._getDragInfo, this),
                    setAmetysDropZoneInfos: Ext.bind(this._getDropInfo, this)
                }
            }
        });
        
        return this._tree;
    },
    
    /**
     * @private
     * Listener before render to add edtior on grid
     * @return {Object} grid the grid
     */
    _onBeforeRender: function(grid)
    {
        grid.getColumns()[0].editor = {
          xtype: 'textfield',
          allowBlank: false,
          selectOnFocus: true
        }
    },
    
    /**
     * @protected
     * Get for the available indicators
     * @return {Object[]} the available indicators
     */
    _getIndicators: function()
    {
        return [
            // configuration status
            {
                id: 'config-status',
                description: "{{i18n PLUGINS_FORMS_TOOL_INDICATOR_WARNING_MSG}}",
                label: "{{i18n PLUGINS_FORMS_TOOL_INDICATOR_WARNING}}",
                iconGlyph: 'ametysicon-sign-caution',
                matchFn: this._needConfiguration,
                applyFn: this._showConfigurationStatus,
                defaultValue: true
            },
            {
                id: 'rules-status',
                description: "{{i18n PLUGINS_FORMS_TOOL_INDICATOR_RULE_MSG}}",
                label: "{{i18n PLUGINS_FORMS_TOOL_INDICATOR_RULE}}",
                iconGlyph: 'ametysicon-arrow-up-right-curve',
                matchFn: this._hasRules,
                applyFn: this._showRulesIcon,
                defaultValue: true
            },
            {
                id: 'read-status',
                description: "{{i18n PLUGINS_FORMS_TOOL_INDICATOR_READ_RESTRICTION_MSG}}",
                label: "{{i18n PLUGINS_FORMS_TOOL_INDICATOR_READ_RESTRICTION}}",
                iconGlyph: 'ametysicon-body-part-eye-no',
                matchFn: this._isReadRestricted,
                applyFn: this._showReadRestrictionIcon,
                defaultValue: true
            },
            {
                id: 'modifiable-status',
                description: "{{i18n PLUGINS_FORMS_TOOL_INDICATOR_MODIFIABLE_RESTRICTION_MSG}}",
                label: "{{i18n PLUGINS_FORMS_TOOL_INDICATOR_MODIFIABLE_RESTRICTION}}",
                iconGlyph: 'ametysicon-edit45',
                matchFn: this._isModifiable,
                applyFn: this._showModifiableRestrictionIcon,
                defaultValue: true
            }
        ]
    },
    
    /**
     * @private
     * Return true if node needs further configuration
     */
    _needConfiguration: function(node)
    {
        return !node.getData().root && !node.getData().isConfigured; 
    },
    
    /**
     * @private
     * Return content for rendering tree when node needs further configuration
     */
    _showConfigurationStatus: function(node)
    {
        switch (node.getData().type)
        {
            case 'root':
                return '<span title="{{i18n PLUGINS_FORMS_TREE_INDICATORS_CONFIG_STATUS_FORM}}" class="orange-color form-indicator ametysicon-sign-caution"></span>';
            case 'page':
                return '<span title="{{i18n PLUGINS_FORMS_TREE_INDICATORS_CONFIG_STATUS_PAGE}}" class="orange-color form-indicator ametysicon-sign-caution"></span>';
            case 'question':
                return '<span title="{{i18n PLUGINS_FORMS_TREE_INDICATORS_CONFIG_STATUS_QUESTION}}" class="orange-color form-indicator ametysicon-sign-caution"></span>';
            default:
                return '';
        }
    },
    
    /**
     * @private
     * Return true if node needs has any rule
     */
    _hasRules: function(node)
    {
        return node.getData().hasRule; 
    },
    
    /**
     * @private
     * Return true if node is read restricted
     */
    _isReadRestricted: function(node)
    {
        return node.getData().isReadRestricted; 
    },
    
    /**
     * @private
     * Return true if node needs is modifiable
     */
    _isModifiable: function(node)
    {
        return node.getData().isModifiable; 
    },
    
    /**
     * @private
     * Return content for rendering tree when node needs further configuration
     */
    _showRulesIcon: function(node)
    {
        var data = node.getData();
        switch (data.type)
        {
            case 'question':
                var title = "";
                if (data.hasTerminalRule)
                {
                    title += "{{i18n PLUGINS_FORMS_TREE_INDICATORS_RULES_STATUS_TERMINAL}}";
                }
                var pageTitles = data.pageTitlesWithRule;
                if (pageTitles.length > 0)
                {
                    if (title != "") { title += "\n";}
                    title += "{{i18n PLUGINS_FORMS_TREE_INDICATORS_RULES_STATUS_PAGE}}\n- " + pageTitles.join("\n- ");
                }
                var questionTitles = data.questionTitlesWithRule;
                if (questionTitles.length > 0)
                {
                    if (title != "") { title += "\n";}
                    title += "{{i18n PLUGINS_FORMS_TREE_INDICATORS_RULES_STATUS_QUESTION}}\n- " + questionTitles.join("\n- ");
                }
                return '<span title="' + title + '" class="form-indicator ametysicon-arrow-up-right-curve"></span>';
            default:
                return '';
        }
    },
    
    /**
     * @private
     * Return content for rendering tree when node is modifiable
     */
    _showModifiableRestrictionIcon: function(node)
    {
        var data = node.getData();
        switch (data.type)
        {
            case 'question':
                var title = "{{i18n PLUGINS_FORMS_TREE_INDICATORS_MODIFIABLE_RESTRICTION_QUESTION}}";
                return '<span title="' + title + '" class="form-indicator ametysicon-edit45"></span>';
            default:
                return '';
        }
    },
    
    /**
     * @private
     * Return content for rendering tree when node has any read restriction
     */
    _showReadRestrictionIcon: function(node)
    {
        var data = node.getData();
        switch (data.type)
        {
            case 'question':
                var title = "{{i18n PLUGINS_FORMS_TREE_INDICATORS_READ_RESTRICTION_QUESTION}}";
                return '<span title="' + title + '" class="form-indicator ametysicon-body-part-eye-no"></span>';
            default:
                return '';
        }
    },
    
    /**
     * @private
     * This event is thrown by the getDragData to add the 'source' of the drag.
     * @param {Object} item The default drag data that will be transmitted. You have to add a 'source' item in it: 
     * @param {Ametys.relation.RelationPoint} item.source The source (in the relation way) of the drag operation. 
     */
    _getDragInfo: function (item)
    {
        var targets = [];
        
        for (var i = 0; i < item.records.length; i++)
        {
            var node = item.records[i];
            if ((node.get('type') == 'page' || node.get('type') == 'question') && node.get('canWrite'))
            {
                targets.push(this._getMessageTargetConfiguration(node));
            }
        }
        
        if (targets.length > 0)
        {
            item.source = {
                relationTypes: [Ametys.relation.Relation.MOVE], 
                targets: targets
            };
        }
    },
    
    /**
     * @private
     * This event is thrown before the beforeDrop event and create the target of the drop operation relation.
     * @param {Ext.data.Model[]} targetRecords The target records of the drop operation.
     * @param {Object} item The default drag data that will be transmitted. You have to add a 'target' item in it: 
     * @param {Object} item.target The target (in the relation way) of the drop operation. A Ametys.relation.RelationPoint config.   
     * @param {String} dropPosition "before", "after" or "append" depending whether the dragged object (in item.target) should be inserted before, after the targetRecords, or appended to it.
     */ 
    _getDropInfo: function(targetRecords, item, dropPosition)
    {
        var targets = [];
        
        var positionInTargets = -1
        for (var i = 0; i < targetRecords.length; i++)
        {
            var node = targetRecords[i];
            
            if (dropPosition == 'append')
            {
                targets.push(this._getMessageTargetConfiguration(node));
            }
            else if (node.parentNode != null) // dropPosition == 'before' or 'after'
            {
                // Get node position 
                for (var i=0; i < node.parentNode.childNodes.length; i++)
                {
                    if (node.parentNode.childNodes[i].getId() == node.getId())
                    {
                        positionInTargets = i + (dropPosition == 'after' ? + 1 : 0);
                        break;
                    }
                }
                
                targets.push(this._getMessageTargetConfiguration(node.parentNode));
            }
        }

        if (targets.length > 0)
        {
            item.target = {
                relationTypes: [Ametys.relation.Relation.MOVE], 
                targets: targets,
                positionInTargets: positionInTargets
            };
        }
    },
    
    /**
     * @private
     * Get the target configuration object for given record
     * @param {Ext.data.Model} record The tree record to convert to its Ametys.message.MessageTarget configuration
     * @return {Object} The configuration to create a Ametys.message.MessageTarget. Can be null, if the record is null or not relevant to be a messagetarget.
     */
    _getMessageTargetConfiguration: function (record)
    {
        if (record == null)
        {
            // Empty selection
            return null;
        }
        else
        {
            if (record.isRoot())
            {
                return {
                    id: Ametys.message.MessageTarget.FORM_TARGET,
                    parameters: {
                        id: record.getId(),
                        hasEntries: record.get('hasEntries')
                    }
                };
            }
            else if (record.get('type') == 'page')
            {
                return {
                    id: Ametys.message.MessageTarget.FORM_PAGE,
                    parameters: {
                        id: record.getId(),
                        formId: record.get('parentId'),
                        canWrite: record.get('canWrite')
                    }
                };
            }
            else if (record.get('type') == 'question')
            {
                var pageId = record.get('parentId');
                var formId = this._tree.getStore().getNodeById(pageId).get('parentId');
                return {
                    id: Ametys.message.MessageTarget.FORM_QUESTION,
                    parameters: {
                        id: record.getId(),
                        type: record.get('questionType'),
                        pageId: pageId,
                        formId: formId,
                        canWrite: record.get('canWrite')
                    }
                };
            }
        }
    },
    
    /**
     * Listener on deletion messages
     * @param {Ametys.message.Message} message The deletion message.
     * @private
     */
    _onDeletionMessage: function (message)
    {
        var questionTarget = message.getTarget(Ametys.message.MessageTarget.FORM_QUESTION),
            pageTarget = message.getTarget(Ametys.message.MessageTarget.FORM_PAGE),
            formTarget = message.getTarget(Ametys.message.MessageTarget.FORM_TARGET);
        
        if (questionTarget)
        {
            this._tree.reloadParent(questionTarget, false, Ext.bind(this._tree._refreshIsConfiguedAttribute, this._tree));
        }
        else if (pageTarget)
        {
            this._tree.reloadParent(pageTarget, false, Ext.emptyFn);
        }
        else if (formTarget)
        {
            this._tree.reloadParent(formTarget, false, Ext.emptyFn);
        }
    },
    
    /**
     * Listener on selection preview messages
     * @param {Ametys.message.Message} message The selection message.
     * @private
     */
    _onSelectionPreviewChanged: function (message)
    {
        var targets = message.getTargets(function(target) {
            return target.getId() == Ametys.message.MessageTarget.FORM_QUESTION
        });
        
        if (targets.length > 0)
        {
            var targetParams = targets[0].getParameters();
            var formId = this._formId;
            if (targetParams.formId == formId)
            {
                this._tree.onFormComponentCreated(targetParams.id, Ext.emptyFn);                
            }
        }
    },
    
    /**
     * Listener on moved messages
     * @param {Ametys.message.Message} message The moved message.
     * @private
     */
    _onMoved: function (message)
    {
        var targets = message.getTargets(function(target) {
            return target.getId() == Ametys.message.MessageTarget.FORM_QUESTION
        });
        
        if (targets.length > 0)
        {
            var targetParams = targets[0].getParameters();
            var formId = this._formId;
            if (targetParams.formId == formId)
            {
                this._tree.refreshPageNode(targets[0].getParameters().pageId);
            }
        }
    },
    
    /**
     * Opens the preview tool for the record.
     * @param {Ext.view.View} view The view.
     * @param {Ext.data.Model} record The record that belongs to the item.
     */
    _openPreviewTool: function(view, record)
    {
        var type = record.get('type');
        switch (type) {
            case 'form':
            case 'root':
                var id = record.get('id');
                break;
            case 'page':
                var pageId = record.get('id');
                var id = record.get('parentId');
                break;
            case 'question':
                var questionId = record.get('id');
                var pageId = record.get('parentId');
                var id = this._tree.getStore().getNodeById(pageId).get('parentId');
                break;
            case 'root':
            default:
                return;
        }
        
        
        var tool = Ametys.tool.ToolsManager.getTool('uitool-form-preview$' + id);
        if (tool)
        {
            // Focus the preview tool to select and see it
            if (!tool.hasFocus())
            {
                tool.focus();
            }
            // Then refocus the forms tool
            this.focus();
        }
        else
        {
            var params = {
                id: id,
                pageId: pageId,
                questionId: questionId
            };
            Ametys.tool.ToolsManager.openTool('uitool-form-preview', params);
        }
    },
    
    /**
     * @private
     * Open the question dialog for edition
     * @param {Object} questionData The question data.
     */ 
    _openQuestionDialog: function(questionData)
    {
        Ametys.plugins.forms.helper.QuestionEditDialogHelper.open({
            questionType: questionData.questionType,
            formId: questionData.formId,
            id: questionData.id,
            dialogTitle: questionData.typeLabel,
            iconGlyph: questionData.iconGlyph,
            hasRule: questionData.hasRule
        });
    },
    
    /**
     * Listener on selection changed message.
     * @param {Ametys.message.Message} message The selection changed message.
     * @private
     */
    _onMessageSelectionChanged: function(message)
    {

        var formTarget = message.getTarget(Ametys.message.MessageTarget.FORM_TARGET);
        if (formTarget && formTarget.getParameters().id != this._formId)
        {
            this._formId = formTarget.getParameters().id;
            this.showOutOfDate();
        }
    }
});

/** Class defining target message names for form pages */
Ext.define("Ametys.message.FormMessageTarget",
{
    override: "Ametys.message.MessageTarget",
    
    statics: 
    {
        /**
         * @member Ametys.message.MessageTarget
         * @readonly
         * @property {String} FORM_PAGE The target type is a page. The expected parameters are:  
         * @property {String} FORM_PAGE.id The id of the form page
         * @property {String} FORM_PAGE.title The title of the page
         * @property {String} FORM_PAGE.formId The id of the parent form
         */
        FORM_PAGE: "form-page",
        
        /**
         * @member Ametys.message.MessageTarget
         * @readonly
         * @property {String} FORM_QUESTION The target type is a question. The expected parameters are:  
         * @property {String} FORM_QUESTION.id The id of the question 
         * @property {String} FORM_QUESTION.title The name of the question 
         * @property {String} FORM_QUESTION.formId The id of the parent form
         */
        FORM_QUESTION: "form-question"
    }
});