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

/**
 * An abstract tool that defines a grid linked to a detail view. It can easily be extended to display logs by providing data to the grid and custom formating rules.
 * @private 
 */
Ext.define('Ametys.plugins.coreui.log.AbstractLogTool', {
    extend: "Ametys.tool.Tool",
    
    /**
     * @cfg {Boolean} [vertical-panel-layout=true] Change the position of the view details. If true, the details are behing the grid, if false, they are on the right of the grid. 
     */
    
    /**
     * @cfg {String} noselect-detail-message The message displayed in the details view when no log is selected in the grid. 
     */
    
    /**
     * @property {Number} _scrollPos The current scroll position of the grid 
     * @private
     */
    
    constructor: function(config)
    {
        this.callParent(arguments);
        
        this.noselectDetailMessage = this.getInitialConfig("noselect-detail-message") || "{{i18n PLUGINS_CORE_UI_LOGTOOL_DEFAULT_DETAIL_MESSAGE}}";
        this.verticalPanelLayout = this.getInitialConfig("vertical-panel-layout") != null ? this.getInitialConfig("vertical-panel-layout") : true;
    },
    
    getMBSelectionInteraction: function()
    {
        return Ametys.tool.Tool.MB_TYPE_NOSELECTION;
    },
    
    /**
     * @protected
     * @template
     * Determine a row CSS class depending upon the record
     * @param {Ext.data.Model} record The record
     * @return {String} The CSS classname to apply
     */
    getRowClass: function(record)
    {
        return "";
    },
    
    createPanel: function()
    {
        this.grid = Ext.create("Ext.grid.Panel", {
            stateful: true,
            stateId: this.self.getName() + "$grid",
            store: this.getStore(),
            scrollable: true,
            border: true,
            flex: 0.5,
            minWidth: this.verticalPanelLayout ? 200 : 100,
            minHeight: this.verticalPanelLayout ? 100 : 200,
            cls: 'log-grid',
            plugins: 'gridfilters',
            
            columns: this.getColumns(),
            
            dockedItems: this.getDockedItems(),
            
            bbar: {
                xtype: 'statusbar',
                statusAlign: 'right'
            },
            
            viewConfig: { 
                getRowClass: Ext.bind(this.getRowClass, this)
            }, 
            
            listeners: {
                'selectionchange': Ext.bind(this._onSelectLog, this) 
            }                
        });
        
        this.detailsComponent = Ext.create("Ext.Component", {
            stateful: true,
            stateId: this.self.getName() + "$details",
            scrollable: true,
            minWidth: this.verticalPanelLayout ? 200 : 100,
            minHeight: this.verticalPanelLayout ? 100 : 200,
            split: true,
            border: true,
            flex: 0.5,
            ui: 'panel',
            cls: 'a-panel-text',
            html: this.noselectDetailMessage
        });
        
         this.gridContainer = Ext.create("Ext.container.Container", {
            layout: { 
                type: this.verticalPanelLayout != "false" ? 'vbox' : 'hbox',
                align: 'stretch'
            },
            cls: 'uitool-logtool',
            items: [ this.grid, this.detailsComponent ]
        });
         
        this._updateStatusBar();
        this.grid.store.on("add", this._updateStatusBar, this);
        this.grid.store.on("add", this._loadScrollPosition, this);
        this.grid.store.on("datachanged", this._updateStatusBar, this);
        this.grid.store.on("datachanged", this._loadScrollPosition, this);
        this.grid.store.on("remove", this._updateStatusBar, this);
        this.grid.store.on("filterchange", this._updateStatusBar, this);
        this.grid.store.on("clear", this._updateStatusBar, this);
        
        this.grid.on("viewready", this._onViewReady, this);
         
        return this.gridContainer;
    },
    
    /**
     * Create the initial store used by the log grid.
     * @return {Ext.data.Store/String/Object} store used by the log grid.
     */
    getStore: function()
    {
        throw "unimplemented method #getStore";
    },
    
    /**
     * Configure the columns of the log grid.
     * @return {Object[]} The columns configuration
     */
    getColumns: function()
    {
        throw "unimplemented method #getColumns";
    },
    
    /**
     * Configure the docked items of the log grid.
     * @return {Object[]} The docked items.
     */
    getDockedItems: function()
    {
        return [];
    },
    
    /**
     * Listener on the grid for the select change event. Update the details view.
     * @param {Ext.grid.Panel} panel The grid panel.
     * @param {Ext.data.Model[]} records The selected records.
     * @param {Object} eOpts Options passed when configuring the listener.
     * @private
     */
    _onSelectLog: function(panel, records, eOpts)
    {
        if (records.length > 0)
        {
            this.detailsComponent.update(this.getDetailsText(records));
        }
        else
        {
            this.detailsComponent.update(this.noselectDetailMessage);
        }
    },
    
    /**
     * Format and return the text displayed in the details for the current selected records.
     * @param {Ext.data.Model[]} records The selected records.
     * @return {String} The formated text to display.
     */
    getDetailsText: function(records)
    {
        throw "unimplemented method #getDetailsText";
    },
    
    /**
     * Listener on the store count update. Automatically update the status bar.
     * @private
     */
    _updateStatusBar: function()
    {
        var statusbar = this.grid.getDockedItems('statusbar[dock="bottom"]')[0];
        var count = this.grid.getStore().getCount();
        if (count > 0)
        {
            statusbar.setStatus(Ext.String.format("{{i18n PLUGINS_CORE_UI_TOOLS_SERVERLOGS_GRID_STATUSBAR}}", count));
        }
        else
        {
            statusbar.setStatus("{{i18n PLUGINS_CORE_UI_TOOLS_SERVERLOGS_GRID_STATUSBAR_EMPTY}}");
        }
    },
    
    /**
     * Listener on the view ready. Add an interceptor to the scroll move event to save its position
     */
    _onViewReady: function(grid)
    {
        var onScrollMove = Ext.bind(this._saveScrollPosition, this);
        grid.view.onScrollMove = grid.view.onScrollMove ? Ext.Function.createInterceptor(grid.view.onScrollMove, onScrollMove, grid) : onScrollMove;
        
        // save the initial scroll position
        this._saveScrollPosition();
    },
    
    /**
     * This listener saves the current scroll position of the grid, to be able to restore it on store modifications.
     * @private
     */
    _saveScrollPosition: function()
    {
        if (this.grid.getView() == null || this.grid.getView().getEl() == null || this.grid.getView().getEl().down("*") == null)
        {
            this._scrollPos = null;
            return;
        }
        
        var gridEl = this.grid.getView().getEl();
        var tableEl = gridEl.down("*"); 
        
        if (gridEl.getScrollTop() == 0)
        {
            this._scrollPos = "top";
        }
        else if (Math.abs(tableEl.getBottom() - gridEl.getBottom()) <= 1)  
        {
            this._scrollPos = "bottom";
        }
        else
        {
            this._scrollPos = null;
        }
    },
    
    /**
     * Load the scroll position of the grid with the position returned by the {#saveScrollPosition}.
     * @private
     */
    _loadScrollPosition: function()
    {
        if (this._scrollPos == null)
        {
            return;
        }
        
        var gridEl = this.grid.getView().getEl();
        
        if (this._scrollPos == "top")
        {
            this.grid.getView().scrollTo(0, 0, false);
        }
        
        if (this._scrollPos == "bottom")
        {
            this.grid.getView().scrollBy(0, gridEl.down("*").getBottom(), false);
        }
    },
    
    onClose: function()
    {
        this.grid.store.un("add", this._updateStatusBar, this);
        this.grid.store.un("add", this._loadScrollPosition, this);
        this.grid.store.un("datachanged", this._updateStatusBar, this);
        this.grid.store.un("datachanged", this._loadScrollPosition, this);
        this.grid.store.un("remove", this._updateStatusBar, this);
        this.grid.store.un("filterchange", this._updateStatusBar, this);
        this.grid.store.un("clear", this._updateStatusBar, this);
        
        this.callParent(arguments);
    }
    
});