/*
 *  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 is the abstract tool editing a file system online.
 * 
 * Creates your own Tool class by inheriting this one and define at least the following methods: 
 * #reloadFile, #getMessageTargetConfiguration, #getMessageTargetIdForResource, #getMessageTargetIdForCollection.
 */
Ext.define('Ametys.file.AbstractFileEditorTool', {
    extend: "Ametys.tool.Tool",


    /**
     * @private
     * @property {String} _fileName The file name
     */
    
    /**
     * @private
     * @property {String} _fileId The file identifier
     */
    
    /**
     * @private
     * @property {String} _filePath The file path
     */
    
    /**
     * @private
     * @property {Ametys.form.field.Code} _editor The editor instance
     */
    
    /**
    * @private
    * @property _loaded A flag for successful file loading
    */
    _loaded: false,
    
    constructor: function()
    {
        this.callParent(arguments);
        
        Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
        Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onDeleted, this);
    },

    createPanel: function()
    {
        this._mainPanel = new Ext.create('Ext.Container', { 
            scrollable: false,
            layout: 'fit',
            cls: 'file-editor-tool',
            
            html: '',
            listeners: {
                'resize': Ext.bind(this._onResize, this)
            }
        });
    
        return this._mainPanel; 
   },

   /**
    * Editing content in the {@link #_editor} triggers a {@link Ametys.message.Message#MODIFYING} event.
    */
   _onChange: function(cms, change)
   {
        if (this._loaded && !this.isDirty())
        {
            this.setDirty(true);
            
            Ext.create("Ametys.message.Message", {
                type: Ametys.message.Message.MODIFYING,
                targets: this.getMessageTargetConfiguration()
            });
        }
    },
    
    /**
     * Get the message target configuration
     * @return {Object} message target configuration
     */
    getMessageTargetConfiguration: function()
    {
        throw new Error("The method #getMessageTargetConfiguration is not implemented in " + this.self.getName());
    },

    getMBSelectionInteraction: function() 
    {
        return Ametys.tool.Tool.MB_TYPE_ACTIVE;
    },
    
    sendCurrentSelection: function()
    {
        Ext.create("Ametys.message.Message", {
            type: Ametys.message.Message.SELECTION_CHANGED,
            targets: this.getMessageTargetConfiguration()
        });
    },
    
    /**
     * @inheritdoc
     * @param {Object} params The parameters for the tool
     * @param {String} params.id The file id
     * @param {String} params.filename The file name
     * @param {String} params.path The file path 
     */
    setParams: function(params)
    {
        this._fileId = params['id'];
        this._fileName = params['filename'];
        this._filePath = params['path'];
        
        this.callParent(arguments);
        
        this.refresh();
    },
    
    /**
     * Get the file path
     * @return the file path
     */
    getFilePath: function()
    {
        return this._filePath;
    },
    
    /**
     * Get the file name
     * @return the file name
     */
    getFileName: function()
    {
        return this._fileName;
    },
   
   /**
    * Loads the file for internal file properties and update the tool info (i.e update the tooltip)
    * @param force Force the file load
    */
    refresh: function()
    {
        this.showRefreshing();
        
        if (!this._editor)
        {
            this._editor = Ext.create("Ametys.form.field.Code", {
                mode: this._getMode(),
                listeners: {
                    'change': Ext.bind(this._onChange, this)
                }
            });
            
            this._mainPanel.add(this._editor);
        }
        
        var extension = Ametys.file.AbstractFileExplorerTree.getFileExtension(this._fileName);
        
        // Set title, description, and icons.
        this.setTitle(this._fileName);
        this.setDescription(this._filePath); // TODO
        
        var iconGlyph = Ametys.file.AbstractFileExplorerTree.getFileIconGlyph(this._fileName);
        this.setGlyphIcon(iconGlyph);
        
        this.reloadFile();
        
        // Register the tool on the history tool
        var toolId = this.getFactory().getId();
        var toolParams = this.getParams();

        Ametys.navhistory.HistoryDAO.addEntry({
            id: this.getId(),
            objectId: this.getId(),
            label: this.getTitle(),
            description: Ext.String.format("{{i18n PLUGINS_CORE_UI_PARAMETERS_EDITOR_NAVHISTORY_DESC}}", this.getFileName(), this.getFilePath()),
            iconGlyph: this.getGlyphIcon(),
            iconSmall: this.getSmallIcon(),
            iconSmall: this.getSmallIcon(),
            iconMedium: this.getMediumIcon(),
            iconLarge: this.getLargeIcon(),
            type: Ametys.navhistory.HistoryDAO.TOOL_TYPE,
            action: Ext.bind(Ametys.tool.ToolsManager.openTool, Ametys.tool.ToolsManager, [toolId, toolParams], false)
        });
        
        /*if(!this._loaded || this._params.forceReload)
        {
            this.reloadFile();
        }
        else
        {
            this.showRefreshed();
        }*/
    },
    
    /**
     * Get CodeMirror mode from file name
     */
    _getMode: function()
    {
        var extension = Ametys.file.AbstractFileExplorerTree.getFileExtension(this._fileName).toLowerCase();
        
        switch (extension) {
            case 'xml':
                return 'xml';
    
            case 'css':
                return 'css';
                
            case 'js':
                return 'javascript';
                
            default:
                return 'htmlmixed';
        }
    },
    
    /**
     * Retrieves the file content from server (asynchronously) and update editor value.
     * Have to call #setDirty(false), #setLoaded(true) and #showRefreshed methods after load is complete.
     * @template
     */
    reloadFile: function()
    {
        throw new Error("The method #getMessageTargetConfiguration is not implemented in " + this.self.getName());
    },
    
    /**
     * Indicates the file has been loaded (or not)
     * @param {Boolean} loaded true to indicates the file is loaded. false otherwise
     */
    setLoaded: function(loaded)
    {
        this._loaded = loaded;
    },
    
    /**
     * Set the value of the editor
     * @param {String} value The value to set in the code editor
     */
    setText: function(value)
    {
        this._editor.setValue(value);
    },
    
    /**
    * Returns the content of the codeEditor
    * @return {String} The content of the codeEditor
    */
    getText: function()
    {
        return this._editor.getValue();
    },

    close: function(manual)
    {
        if (manual && this.isDirty())
        {
            var me = this;
            Ametys.Msg.confirm("{{i18n PLUGINS_CORE_UI_PARAMETERS_EDITOR_UNSAVE_LABEL}}", 
                    "{{i18n PLUGINS_CORE_UI_PARAMETERS_EDITOR_UNSAVE_CONFIRM}}", 
                    function(answer) {
                        if (answer == 'yes')
                        {
                            Ametys.file.AbstractFileEditorTool.superclass.close.call(me, [manual])
                        }
                    },
                    this
            );
            return;
        }
        
        this.callParent(arguments);
    },
    
    /**
     * @private
     * Listener when the panel is resized
     * @param {Ext.Component} cmp The component resized
     * @param {Number} width The new width
     * @param {Number} height The new height
     */
    _onResize: function(cmp, width, height)
    {
        if (this._editor)
        {
            this._editor.setSize(width, height);
        }
    },
    
    /**
     * Determines if the current file is in path
     * @param {String} path the parent path
     * @private
     */
    _isInPath: function(path)
    {
        return this._filePath.indexOf(path) == 0;
    },

    /**
     * @protected
     * @template
     * Get the id of message target for a file resource
     * @return {String|Function} the message target type or a function testing the target
     */
    getMessageTargetIdForResource: function()
    {
        throw new Error("The method #getMessageTargetIdForResource is not implemented in " + this.self.getName());
    },
    
    /**
     * @protected
     * @template
     * Get the id of message target for a folder resource
     * @return {String|Function} the message target type or a function testing the target
     */
    getMessageTargetIdForCollection: function()
    {
        throw new Error("The method #getMessageTargetIdForCollection is not implemented in " + this.self.getName());
    },
    
    /**
     * Listener when a Ametys.message.Message#MODIFIED message was received.
     * Removes the asterisk mark on editor title.
     * @param {Ametys.message.Message} message The received message
     * @private
     */
    _onModified: function(message)
    {
        var me = this;
        
        var folderTarget = message.getTarget(this.getMessageTargetIdForCollection());
        // FIXME oldpath does not exist
        /**if (folderTarget != null && folderTarget.getParameters().oldPath && this._isInPath(folderTarget.getParameters().oldPath))
        {
            this._filePath = folderTarget.getParameters().path + this._filePath.substring(folderTarget.getParameters().oldPath.length);
            this.setTitle(this._fileName);
            this.setDescription(this._filePath);
            return;
        }*/
        
        var fileTargets = message.getTargets(this.getMessageTargetIdForResource());
        
        for (var i=0; i < fileTargets.length; i++)
        {
            if (message.getParameters().major && fileTargets[i].getParameters().path == this._filePath)
            {
                // The file content has been saved
                this.setDirty(false);
            }
            // FIXME oldpath does not exist
            /*
            else if (this._filePath == fileTargets[i].getParameters().oldPath)
            {
                // The file has been renamed
                this._fileName = fileTargets[i].getParameters().name;
                this._filePath = fileTargets[i].getParameters().path;
                
                var extension = Ametys.file.AbstractFileExplorerTree.getFileExtension(this._fileName);
                
                this.setTitle(this._fileName);
                this.setDescription(this._filePath); // TODO
                this.setSmallIcon('/plugins/explorer/icon/' + extension + ".png");
                this.setMediumIcon('/plugins/explorer/icon-medium/' + extension + ".png");
                this.setLargeIcon('/plugins/explorer/icon-medium/' + extension + ".png");
            }*/
        }
    },
    
    /**
     * Listener when a Ametys.message.Message#DELETED message was received.
     * If the current file is concerned, the tool will be closed.
     * @param {Ametys.message.Message} message The received message
     * @private
     */
    _onDeleted: function(message)
    {
        var me = this;
        var fileTargets = message.getTargets(this.getMessageTargetIdForResource());
        
        for (var i=0; i < fileTargets.length; i++)
        {
            if (this._filePath == fileTargets[i].getParameters().path)
            {
                this.close();

                // Remove file from navigation history
                Ametys.navhistory.HistoryDAO.removeEntry(this.getId());
            }
        }
        
    }
   
});