/**
 * This plugin allows pivot grid data export using various exporters. Each exporter should extend
 * the {@link Ext.exporter.Base Base} class.
 *
 * Two new methods are created on the pivot grid by this plugin:
 *
 *  - saveDocumentAs(config): saves the document
 *  - getDocumentData(config): returns the document content
 *
 * Example usage:
 *
 *      {
 *          xtype: 'pivot',
 *          plugins: [{
 *              ptype: 'pivotexporter'
 *          }]
 *      }
 *
 *      pivot.saveDocumentAs({
 *          type: 'excel',
 *          title: 'My export',
 *          fileName: 'myExport.xml'
 *      });
 *
 */
Ext.define('Ext.pivot.plugin.Exporter', {
    alternateClassName: [
        'Mz.pivot.plugin.ExcelExport'
    ],
 
    alias: [
        'plugin.pivotexporter',
        'plugin.mzexcelexport'
    ],
 
    extend: 'Ext.AbstractPlugin',
 
    requires: [
        'Ext.exporter.Excel'
    ],
 
    /**
     *  `"both"` (the default) - The plugin is added to both grids
     *  `"top"` - The plugin is added to the containing Panel
     *  `"locked"` - The plugin is added to the locked (left) grid
     *  `"normal"` - The plugin is added to the normal (right) grid
     *
     * @private
     */
    lockableScope:  'top',
 
    init: function (grid) {
        var me = this;
 
        //<debug> 
        // this plugin is available only for the pivot grid 
        if (!grid.isPivotGrid) {
            Ext.raise('This plugin is only compatible with Ext.pivot.Grid');
        }
        //</debug> 
 
        grid.saveDocumentAs = Ext.bind(me.saveDocumentAs, me);
        grid.getDocumentData = Ext.bind(me.getDocumentData, me);
        me.pivot = grid;
 
        return me.callParent(arguments);
    },
 
    destroy: function () {
        var me = this;
 
        me.pivot.saveDocumentAs = me.pivot.getDocumentData = me.pivot = me.matrix = null;
        return me.callParent(arguments);
    },
 
    /**
     * Save the export file. This method is added to the grid panel as "saveDocumentAs".
     *
     * Pass in exporter specific configs to the config parameter.
     *
     * @param {Ext.exporter.Base} config Config object used to initialize the proper exporter
     * @param {String} config.type Type of the exporter as defined in the exporter alias. Default is `excel`.
     * @param {Boolean} [config.onlyExpandedNodes] True to export only what is visible in the grid. False to export everything.
     * @param {Boolean} [config.showSummary] True to export group summaries
     * @param {String} [config.title] Title added to the export document
     * @param {String} [config.author] Who exported the document?
     * @param {String} [config.fileName] Name of the exported file, including the extension
     * @param {String} [config.charset] Exported file's charset
     *
     */
    saveDocumentAs: function(config){
        var exporter;
 
        if(this.disabled){
            return;
        }
 
        exporter = this.getExporter.apply(this, arguments);
        exporter.saveAs();
        Ext.destroy(exporter);
    },
 
    /**
     * Fetch the export data. This method is added to the grid panel as "getDocumentData".
     *
     * Pass in exporter specific configs to the config parameter.
     *
     * @param {Ext.exporter.Base} config Config object used to initialize the proper exporter
     * @param {String} config.type Type of the exporter as defined in the exporter alias. Default is `excel`.
     * @param {Boolean} [config.onlyExpandedNodes] True to export only what is visible in the grid. False to export everything.
     * @param {Boolean} [config.showSummary] True to export group summaries
     * @param {String} [config.title] Title added to the export document
     * @param {String} [config.author] Who exported the document?
     * @returns {String}
     *
     */
    getDocumentData: function(config){
        var exporter, ret;
 
        if(this.disabled){
            return;
        }
 
        exporter = this.getExporter.apply(this, arguments);
        ret = exporter.getContent();
        Ext.destroy(exporter);
 
        return ret;
    },
 
    /**
     * Builds the exporter object and returns it.
     *
     * @param config
     * @returns {Ext.exporter.Base}
     *
     * @private
     */
    getExporter: function(config){
        var me = this;
 
        config = config || {};
        me.matrix = me.pivot.getMatrix();
        me.onlyExpandedNodes = config.onlyExpandedNodes;
        delete(config.onlyExpandedNodes);
 
        return Ext.Factory.exporter(Ext.apply({
            type: 'excel',
            data: me.prepareData()
        }, config));
    },
 
    /**
     * This method creates the data object that will be consumed by the exporter.
     * @returns {Object}
     *
     * @private
     */
    prepareData: function(){
        var me = this,
            matrix = me.matrix,
            group, columns, headers, record, i, dataIndexes;
 
        if(!me.onlyExpandedNodes){
            me.setColumnsExpanded(matrix.topAxis.getTree(), true);
        }
 
        columns = Ext.clone(matrix.getColumnHeaders());
        headers = me.getColumnHeaders(columns);
        dataIndexes = me.getDataIndexColumns(columns);
 
        if(!me.onlyExpandedNodes){
            me.setColumnsExpanded(matrix.topAxis.getTree());
        }
 
        group = me.extractGroups(matrix.leftAxis.getTree(), dataIndexes);
 
        Ext.apply(group, {
            summary:        [],
            text:           ''
        });
 
        group.summary.push(matrix.textGrandTotalTpl);
        record = matrix.preparePivotStoreRecordData({key: matrix.grandTotalKey});
        for(= 1; i < dataIndexes.length; i++){
            group.summary.push( (Ext.isEmpty(record[dataIndexes[i]]) || (matrix.showZeroAsBlank && record[dataIndexes[i]] === 0) ) ? '' : record[dataIndexes[i]] );
        }
 
        return {
            columns:    headers,
            groups:     [group]
        };
    },
 
    /**
     * If we have to export everything then expand all top axis tree nodes temporarily
     *
     * @param items
     * @param expanded
     *
     * @private
     */
    setColumnsExpanded: function(items, expanded){
        for(var i = 0; i < items.length; i++){
            if(Ext.isDefined(expanded)){
                items[i].backupExpanded = items[i].expanded;
                items[i].expanded = expanded;
            }else{
                items[i].expanded = items[i].backupExpanded;
                items[i].backupExpanded = null;
            }
 
            if(items[i].children){
                this.setColumnsExpanded(items[i].children, expanded);
            }
        }
    },
 
    /**
     * Returns an array of column headers to be used in the export file
     *
     * @param columns
     *
     * @returns {Array}
     *
     * @private
     */
    getColumnHeaders: function(columns){
        var cols = [],
            i, obj;
 
        for(= 0; i < columns.length; i++){
            obj = {
                text:   columns[i].text
            };
 
            if(columns[i].columns){
                obj.columns = this.getColumnHeaders(columns[i].columns);
            }
            cols.push(obj);
        }
 
        return cols;
    },
 
    /**
     * Find all columns that have a dataIndex
     *
     * @param columns
     *
     * @returns {Array}
     *
     * @private
     */
    getDataIndexColumns: function(columns){
        var cols = [], i;
 
        for(= 0; i < columns.length; i++){
            if(columns[i].dataIndex){
                cols.push(columns[i].dataIndex);
            }else if (Ext.isArray(columns[i].columns)){
                cols = Ext.Array.merge(cols, this.getDataIndexColumns(columns[i].columns));
            }
        }
 
        return cols;
    },
 
    /**
     * Extract data from left axis groups.
     *
     * @param items
     * @param columns
     *
     * @returns {Object}
     *
     * @private
     */
    extractGroups: function(items, columns){
        var matrix = this.matrix,
            group = {},
            i, j, doExtract, item, row, record;
 
        for(= 0; i < items.length; i++){
            item = items[i];
 
            if(item.record){
                group.rows = group.rows || [];
 
                row = [];
                for(= 0; j < columns.length; j++){
                    row.push( (Ext.isEmpty(item.record.get(columns[j])) || (matrix.showZeroAsBlank && item.record.get(columns[j]) === 0) ) ? '' : item.record.get(columns[j]) );
                }
                group.rows.push(row);
 
            }else if(item.children){
                group.groups = group.groups || [];
                row = {};
 
                doExtract = this.onlyExpandedNodes ? item.expanded : true;
                if(doExtract){
                    row = this.extractGroups(item.children, columns);
                }
 
                Ext.apply(row, {
                    summary:    [],
                    text:       item.name
                });
 
                row.summary.push(item.getTextTotal());
                record = matrix.preparePivotStoreRecordData(item);
                for(= 1; j < columns.length; j++){
                    row.summary.push((Ext.isEmpty(record[columns[j]]) || (matrix.showZeroAsBlank && record[columns[j]] === 0) ) ? '' : record[columns[j]]);
                }
 
                group.groups.push(row);
            }
 
        }
 
        return group;
    }
 
 
});