/*
 *  Copyright 2018 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 widget to select one or more course parts.<br>
 * This embeds a component with multiple panels to display course parts, buttons, to edit or remove the course part for each panel and general buttons to add or search a course part.<br>
 * Advanced search through a dialog box could be enable. See #cfg-allowSearch.<br>
 * Allow content creation using #cfg-allowCreation.<br>
 * 
 * This widget is used to edit course parts in a CFP
 */
Ext.define('Ametys.odf.widget.SelectCoursePart.Repeater', {
    extend: 'Ametys.form.AbstractField',
    
    canDisplayComparisons: true,
    
    /**
     * @cfg {String} [createButtonText=""] The text of the create content button.
     */
    createButtonText: '',
    
    /**
     * @cfg {String} [createButtonIconCls="ametysicon-add64"] One or more space separated CSS classes to be applied to the icon of create button. Only used if {@link #createButtonIcon} is null.
     * value will be used instead if available.
     */
    createButtonIconCls: 'ametysicon-add64',
    
    /**
     * @cfg {String} [createButtonIcon] The button icon path for the create content button.
     */
    createButtonIcon: null,
    
    /**
     * @cfg {String} createButtonTooltip The button icon tooltip for the create button.
     */
    createButtonTooltip: "{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_CREATE_BTN_TOOLTIP}}",
    
    /**
     * @cfg {String} [searchButtonText=""] The text of the search button.
     */
    searchButtonText: '',
    
    /**
     * @cfg {String} [searchButtonIconCls="ametysicon-magnifier12"] One or more space separated CSS classes to be applied to the icon of search button. Only used if {@link #searchButtonIcon} is null.
     */
    searchButtonIconCls: 'ametysicon-magnifier12',
    
    /**
     * @cfg {String} [searchButtonIcon] The button icon path for the search button.
     */
    searchButtonIcon: null,
    
    /**
     * @cfg {String} searchButtonTooltip The button icon tooltip for the search button.
     */
    searchButtonTooltip: "{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_SEARCH_BTN_TOOLTIP}}",
    
    /**
     * @cfg {String} [editButtonText=""] The text of the edit button.
     */
    editButtonText: '',
    
    /**
     * @cfg {String} [editButtonIconCls="ametysicon-edit45"] One or more space separated CSS classes to be applied to the icon of search button. Only used if {@link #editButtonIcon} is null.
     */
    editButtonIconCls: 'ametysicon-edit45',
    
    /**
     * @cfg {String} [editButtonIcon] The button icon path for the edit content button.
     */
    editButtonIcon: null,
    
    /**
     * @cfg {String} editButtonToolTip The button icon tooltip for the edit button.
     */
    editButtonToolTip: "{{i18n plugin.cms:CONTENT_EDITION_LINK_EDIT_LABEL}}",
    
    /**
     * @cfg {String} [removeButtonText=""] The text of the remove button.
     */
    removeButtonText: '',
    
    /**
     * @cfg {String} [removeButtonIconCls="ametysicon-delete30"] One or more space separated CSS classes to be applied to the icon of search button. Only used if {@link #removeButtonIcon} is null.
     */
    removeButtonIconCls: 'ametysicon-delete30',
    
    /**
     * @cfg {String} [removeButtonIcon] The button icon path for the remove content button.
     */
    removeButtonIcon: null,
    
    /**
     * @cfg {String} removeButtonToolTip The button icon tooltip for the remove button.
     */
    removeButtonToolTip: "{{i18n plugin.cms:CONTENT_EDITION_LINK_REMOVE_LABEL}}",
    
    /**
     * @cfg {String} [emptyText] The message displayed when there is no elements selected.
     */
    emptyText: "{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_EMPTY}}",
    
    /**
     * @cfg {String} [hintText] The message displayed to select or create a new course part
     */
    hintText: "{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_HINT}}",
    
    /**
     * @cfg {String} [helpMsg1] The message displayed at the top of the first card of search dialog box
     */
    helpMsg1: "{{i18n plugin.cms:PLUGINS_CMS_HELPER_SELECTCONTENTBYSEARCH_SEARCH_HINT}}",
    /**
     * @cfg {String} [helpMsg2] The message displayed at the top of the second card of search dialog box
     */
    helpMsg2: "{{i18n plugin.cms:PLUGINS_CMS_HELPER_SELECTCONTENTBYSEARCH_RESULT_HINT_MULTIPLE}}",
    
    /**
     * @cfg {String} [boxTitle] The title of the dialog box.
     */
    boxTitle: "{{i18n plugin.cms:PLUGINS_CMS_HELPER_SELECTCONTENTBYSEARCH_TITLE}}", 
    /**
     * @cfg {String} [boxIcon=null] The path of the icon of the dialog box.
     */
    boxIcon: null,
    
    /**
     * @cfg {String} [boxIconCls] The icon cls
     */
    boxIconCls: 'ametysicon-document209 decorator-ametysicon-magnifier12',
    
    /**
     * @cfg {Boolean} [allowSearch=true] Set to `false` to disable advanced search.
     */
    allowSearch: true,
    
    /**
     * @cfg {Boolean} [allowCreation=true] Set to `true` allow content creation. Note that if {@link #creatingContent} is set to 'true', creation will be always disallowed.
     */
    allowCreation: true,
    
    /**
     * @cfg {Boolean} [readOnly=false] True to disallow add, delete and move actions
     */
    readOnly: false,
    
    /**
     * @cfg {Boolean} [creatingContent=false] Set to `true` if a content is already being creating. 
     */
    creatingContent: false,
    
    /**
     * cfg {Boolean} [checkCreationRights=true] Set to `false` to not check rights on content types for content creation
     */
    checkCreationRights: true,
    
    /**
     * @cfg {String} [contentType] The content type to allow search on. If not null, the search will be restricted the search to this content type (and its sub-types).
     */
    contentType: null, 
    
    /**
     * @property {Ametys.cms.content.ContentType/Ametys.cms.content.ContentType[]} _contentTypes Array of content type displayed in the drop down list of the create content dialog box.
     * Only used if the create content button is displayed.
     * @protected
     */
    
    /**
     * @cfg {String} workflowName The id of the workflow initialization function. Only used if the create content button is displayed.
     */
    
    /**
     * @cfg {String} [viewForCreation=creation] The id of the view used for creation. Only used if the create content button is displayed.
     */
    viewForCreation: 'creation',
    
    /**
     * @cfg {String} [viewForModification=default-edition] The id of the view used for modification. Only used if the edit content button is displayed.
     */
    viewForModification: 'default-edition',
    
    /**
     * @cfg {String} [viewToDisplay=form] The id of the view used to display the content.
     */
    viewToDisplay: 'form',
    
    /**
     * @cfg {Number} [initWorkflowActionId=1] The id of the initialize workflow action to create the content. Only used if the create content button is displayed.
     */
    initWorkflowActionId: 1,
    /**
     * @cfg {Number} [initAndEditWorkflowActionId=1] The id of the initialize workflow action to create and edit the content. Only used if the create content button is displayed.
     */
    initAndEditWorkflowActionId: 1,
    /**
     * @cfg {Number} [editWorkflowActionId=2] The id of the edition workflow action to edit the content. Only used if the create content button is displayed.
     */
    editWorkflowActionId: 2,
     
    /**
     * @cfg {String} [modelId=search-ui.coursepart] The id of model to use for search
     */
    modelId: 'search-ui.coursepart',
    
    /**
     * @cfg {Boolean} [multiple=true] True to allow multiple selection.
     */
    multiple: true,
    
    /**
     * @property {String} _contentLanguage The language key of the content to create.
     * Only used if the create content button is displayed.
     * @protected
     */
    
    /**
     * @property {String} _contentCatalog The catalog key of the content to create.
     * Only used if the create or search content button is displayed.
     * @protected
     */
    
    /**
     * @private
     * @property {Ext.panel.Panel} _repeaterPanel The panel displaying the values.
     */
    
    /**
     * @private
     * @property {Ext.Component} displayField Component to display hint messages.
     */
    
    /**
     * @private
     * @property {Boolean} _initialized True if the widget creation process is finished
     */
    
    getValue: function ()
    {
        if (this._initialized)
        {
            var value = [];
            this._repeaterPanel.items.each(function(item) {
                value.push(item.getComponent('id').value);
            });
            return this.multiple ? value : (value && value.length > 0 ? value[0] : null);
        }
        else
        {
            return this.callParent(arguments);
        }
    },
    
    setValue: function (value) 
    {   
        this._rawValue = value;
        
        var me = this;
        
        var ids = [];
        
        value = Ext.isEmpty(value) ? [] : Ext.Array.from(value);
        
        if (value.length == 0
            || this._baseValue !== undefined || this._futureValue !== undefined)
        {
            this._initialized = true;
            if (this._repeaterPanel)
            {
                this._repeaterPanel.removeAll();
            }
            if (this._baseValue !== undefined || this._futureValue !== undefined)
            {
                this._repeaterPanel.getDockedComponent('comparison-message').show();
            }
        }
        else
        {
                this._initialized = false;
                this._repeaterPanel.getDockedComponent('comparison-message').hide();
                
                // Values can be either String (of id) or the model
                this._initializedCount = value.length;
                Ext.Array.forEach(value, function(v) {
                    if (!Ext.isString(v))
                    {
                        v = v.id;
                    }
                    ids.push(v);
                    me._addItem(v);
                });
        }
        
        me.callParent([ids]);
    },
    
    /**
      * When used in readonly mode, settting the comparison value will display ins/del tags
      * @param {String} otherValue The value to compare the current value with
      * @param {boolean} base When true, the value to compare is a base version (old) ; when false it is a future value
      */
    setComparisonValue: function(otherValue, base)
    {
         if (base)
         {
             this._baseValue = otherValue || null;
             this._futureValue = undefined;
         }
         else
         {
             this._baseValue = undefined;
             this._futureValue = otherValue || null;
         }
         this.setValue(this._rawValue);
    },
    
    getSubmitValue: function ()
    {
        return this.multiple ? Ext.encode(this.getValue()) : (this.getValue() || '');
    },
    
    initComponent: function()
    {
        this._initialized = false;
        this.cls = 'x-form-selectcontent-widget';
        // This widget is desigend solely to work in a CFP when editing a course
        // We can set the context once and for all instead of using a updateAdditionalWidgetsConf method
        this._initiliazeContentInfo();
        this.items = this.getItems();
        this.callParent(arguments);
    },
    
    /**
     * Initialize some informations from the current content to create and get contents with similar informations (language and catalog).
     */
    _initiliazeContentInfo: function()
    {
        if (this.contentInfo && this.contentInfo.contentId != null)
        {
            // Get the catalog of the current content
            Ametys.data.ServerComm.callMethod({
                role: "org.ametys.odf.catalog.CatalogsManager",
                methodName: "getContentCatalog",
                parameters: [this.contentInfo.contentId],
                callback: {
                    handler: this._setContentCatalogCb,
                    scope: this
                },
                waitMessage: false
            });
            
            // If a context is provided with a content id but without a lang
            // set the lang of the content as the lang for the context
            if (this.contentInfo.lang == null)
            {
                Ametys.cms.content.ContentDAO.getContent(this.contentInfo.contentId, c => {
                    this.contentInfo.lang = c.getLang();
                })
            }
        }
    },
    
    /**
     * Set the current content catalog.
     * @param {String} catalogName The catalog name
     * @private
     */
    _setContentCatalogCb: function(catalogName)
    {
        this.contentInfo.catalog = catalogName;
    },
    
    /**
     * Update the panel which displayed the content.
     * @param {Ext.Panel} item The panel to update
     * @param {String} contentId The ID of the content to update
     * @private
     */
    _updateItem: function(item, contentId)
    {
        var params = {
            contentId: contentId,
            viewName: this.viewToDisplay,
            currentParentId: this.contentInfo.contentId
        };
        
        Ametys.data.ServerComm.send({
            plugin: 'odf',
            url: 'coursepart/view.xml',
            parameters: params, 
            priority: Ametys.data.ServerComm.PRIORITY_MAJOR, 
            callback: {
                handler: this._updateItemCb,
                scope: this,
                arguments: [item]
            },
            responseType: 'xml'
        });
    },
    
    /**
     * Called after the _updateItem function, update the panel from the server response.
     * @param {HTMLElement} response The server response as XML
     * @param {Object[]} args The first arg is the panel to update
     * @param {Ext.Panel} args.0 The panel to update
     */
    _updateItemCb: function(response, args)
    {
        args[0].setTitle(Ext.dom.Query.selectValue("title", response));
        args[0].getComponent('display').update(Ext.dom.Query.selectNode("display", response).outerHTML);
    },
    
    /**
     * Open a dialog box to modify the content.
     * @param {Ext.Panel} panel The panel to update after the modification.
     * @param {Ametys.cms.content.Content} content The content to modify
     * @private
     */
    _editItem: function(panel, content)
    {
        var openParams = {
            content: content,
            editWorkflowActionId: this.editWorkflowActionId,
            workflowName: this.workflowName,
            viewName: this.viewForModification,
            helpmessage1: "{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_EDIT_ALERT}}"
        }
        
        this.triggerDialogBoxOpened = true;
        Ametys.cms.uihelper.EditContent.open(openParams, Ext.bind(this._editItemCb, this, [panel], true), this);
    },
    
    /**
     * Called after _editItem, it update the content panel.
     * @param {String} contentId The ID of the modified content
     * @param {Ext.Panel} panel The panel to update after the modification.
     */
    _editItemCb: function(contentId, panel)
    {
        if (contentId)
        {
            this._updateItem(panel, contentId);
        }
        
        this.triggerDialogBoxOpened = false;
        this.focus();
    },
    
    /**
     * Remove a content panel from the form.
     * @param {Ext.Panel} panel The panel to remove, on saving content, the linked content will be removed.
     * @private
     */
    _removeItem: function(panel)
    {
        // Confirm deletion
        Ametys.Msg.confirm(
            "{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_REMOVE_TITLE}}",
            "{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_REMOVE_CONFIRM}}",
            function (answer)
            {
                if (answer == 'yes')
                {
                    this._repeaterPanel.remove(panel);
                    if (this._repeaterPanel.items.getCount() == 0)
                    {
                        this.displayField.update(this.emptyText);
                    }
                    this.fireEvent('change');
                }
            },
            this
        );
    },
    
    /**
     * Create a panel with the displayed content and add it to the "repeater" panel.
     * @param {Ametys.cms.content.Content} content The content to display
     * @private
     */
    _addItemPanel: function(content)
    {
        if (this._repeaterPanel.getDockedComponent('comparison-message').isVisible())
        {
            return;
        }
        
        var title = content.getTitle();
        
        var item = Ext.create('Ext.Panel', {
            title: title,
            layout: {
                type: 'anchor'
            },
            anchor: '100%',
            ui: 'light',
            titleCollapse: true,
            hideCollapseTool: false,
            collapsible: true,
            header: {
                titlePosition: 1
            },
            border: true,
            collapsed: true, // Render the items collapsed by default
            // The tools
            tools: this.readOnly ? null : [{
                text: this.editButtonText,
                iconCls: this.editButtonIconCls,
                icon: this.editButtonIcon,
                tooltip: this.editButtonToolTip,
                callback: function(panel)
                {
                    this._editItem(panel, content);
                },
                scope: this
            },{
                text: this.removeButtonText,
                iconCls: this.removeButtonIconCls,
                icon: this.removeButtonIcon,
                tooltip: this.removeButtonToolTip,
                callback: this._removeItem,
                scope: this
            }],
            items: [{
                xtype: 'component',
                itemId: 'display',
                cls: 'a-panel-text-empty',
                border: false,
                padding: '0 15 0 15',
                html: ''
            }, {
                xtype: 'hidden',
                itemId: 'id',
                value: content.getId()
            }]
        });
        
        this._updateItem(item, content.getId());
        this._repeaterPanel.add(item);
        
        if (this.displayField)
        {
            this.displayField.update(this.hintText);
        }
        
        if (this._initializedCount > 0)
        {
            this._initializedCount--;
            if (this._initializedCount == 0)
            {
                this._initialized = true;
            }
        }
        else
        {
            this._initialized = true; // On first edition, setValue is not called, so _initialized may not be true, but indeed we are
            // On change only when not at startup
            this.fireEvent('change');
        }
        
    },
    
    /**
     * Create the panel with the new item.
     * @param {String} contentId The content ID of the content to add. If the selected content is already linked to the current content. It won't be added.
     * @private
     */
    _addItem: function(contentId)
    {
        var alreadyAdded = this.multiple ? this.getValue() && this.getValue().indexOf(contentId) >= 0 : this.getValue() === contentId;
        if (!alreadyAdded)
        {
            Ametys.cms.content.ContentDAO.getContent(contentId, Ext.bind(this._addItemPanel, this));
        }
    },
    
    /**
     * Get the items composing the fields
     * @return {Object[]} The items
     */
    getItems: function()
    {
        var bbarItems = [];
       
        if (!this.readOnly)
        {
	        this.displayField = Ext.create('Ext.Component', {
	            cls: Ametys.form.AbstractField.READABLE_TEXT_CLS,
	            html: this.emptyText,
	            flex: 1
	        });
            
            bbarItems.push(this.displayField);
             
            if (this.allowSearch == true || this.allowSearch == 'true')
            {
                bbarItems.push({
                    xtype: 'button',
                    text: this.searchButtonText,
                    iconCls: this.searchButtonIcon ? null : this.searchButtonIconCls,
                    icon: this.searchButtonIcon,
                    tooltip: this.searchButtonTooltip,
                    handler: this.selectContentsBySearch,
                    scope: this
                });
            }
            
            // Button that opens the create content dialog box. 
            if (this.contentType && (this.allowCreation == true || this.allowCreation == 'true') && !this.creatingContent)
            {
                // Get the content type and its sub-content types allowed to be created by the user
                var checkRights = this.checkCreationRights !== false;
                var contentTypesCollection = Ametys.cms.content.ContentTypeDAO.getContentType(this.contentType).getDescendantsOrSelf().createFiltered(function (contentType) {
                    return (contentType.hasRight() || !checkRights)
                        && !contentType.isMixin()
                        && !contentType.isAbstract();
                });
                if (contentTypesCollection.count() > 0)
                {
                    bbarItems.push({
                        xtype: 'button',
                        text: this.createButtonText,
                        iconCls: this.createButtonIcon ? null : this.createButtonIconCls,
                        icon: this.createButtonIcon,
                        tooltip: this.createButtonTooltip,
                        handler: this.openCreateContentBox,
                        scope: this,
                        hidden: false
                    });
                    this._contentTypes = contentTypesCollection.items;
                }
            }
        }
        
        var bbar = Ext.create('Ext.Panel', {
            layout: {
                type: 'hbox'
            },
            items: bbarItems
        });
        
        this._repeaterPanel = Ext.create('Ext.panel.Panel', {
            items: [],
            bbar: bbar,
            dockedItems: [{
                dock: 'top',
                itemId: "comparison-message",
                xtype: "component",
                html: "<i>{{i18n PLUGINS_ODF_WIDGET_SELECT_COURSEPART_COMPARISON}}</i>",
                hidden: true
            }]
        });
        
        var items = [this._repeaterPanel];
        
        return items;
    },
    
    /**
     * Open the dialog box to search contents.
     */
    selectContentsBySearch: function()
    {
        this.triggerDialogBoxOpened = true;
        Ametys.cms.uihelper.SelectContentBySearch.open({
            icon: this.boxIcon,
            iconCls: this.boxIconCls,
            title: this.boxTitle, 

            helpmessage1: this.helpMsg1, 
            helpmessage2: this.helpMsg2, 
            
            callback: Ext.bind(this._selectContentsCb, this),
            
            multiple: this.multiple,
            
            modelId: this.modelId,
            contentType: this.contentType,
            searchModelParameters: {"courseId": this.contentInfo.contentId},
            
            forceMode: 'disabled',
            forceValues: {
                'reference-catalog-eq': this.contentInfo.catalog,
                'reference-contentLanguage-eq': this.contentInfo.lang
            }
        });
    },
    
    /**
     * This function is called after selecting contents in the search dialog box.
     * Sets the value of the field.
     * @param {String|String[]} contentIds The identifiers of selected content 
     * @private
     */
    _selectContentsCb: function(contentIds)
    {
        if (contentIds)
        {
            Ext.Array.forEach(contentIds, this._addItem, this);
        }
        
        this.triggerDialogBoxOpened = false;
        this.focus();
    },
    
    /**
     * Open the dialog box to create a content.
     */
    openCreateContentBox: function()
    {
        var openParams = {
            parentContentType: this.contentType,
            contentTypes: this._contentTypes,
            contentLanguage: this.contentInfo.lang,
            initWorkflowActionId: this.initWorkflowActionId,
            initAndEditWorkflowActionId: this.initAndEditWorkflowActionId,
            editWorkflowActionId: this.editWorkflowActionId,
            workflowName: this.workflowName,
            viewName: this.viewForCreation,
            forceMode: 'disabled',
            forceValues: {
                catalog: this.contentInfo.catalog,
                courseHolder: this.contentInfo.contentId
            },
            additionalWorkflowParameters: {
                'org.ametys.odf.workflow.AbstractCreateODFContentFunction$catalog': this.contentInfo.catalog,
                'org.ametys.odf.workflow.AbstractCreateODFContentFunction$courseHolder': this.contentInfo.contentId
            },
            hideTitle: true,
            helpmessage1: "{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_CREATE_MSG}}"
        }
        
        this.triggerDialogBoxOpened = true;
        Ametys.cms.uihelper.CreateContent.open(openParams, this._openCreateContentBoxCb, this);
    },
    
    /**
     * This function is called after a content has been created with the create content button.
     * Add the content to the select field and open the new content in the content tool (in view mode).
     * @param {String} contentId The identifier of created content 
     * @private
     */
    _openCreateContentBoxCb: function(contentId)
    {
        if (contentId)
        {
            this._addItem(contentId);
        }
        
        this.triggerDialogBoxOpened = false;
        this.focus();
    },
});