/**
 * This class implements the data structure required by an exporter.
 */
Ext.define('Ext.exporter.data.Table', {
    extend: 'Ext.exporter.data.Group',
 
    requires: [
        'Ext.exporter.data.Column'
    ],
 
    isDataTable: true,
 
    config: {
        /**
         * @cfg {Ext.exporter.data.Column[]} columns
         *
         * Collection of columns that need to be exported.
         *
         */
        columns: null
 
        /**
         * @cfg {Ext.exporter.data.Row[]} rows
         *
         * Table's rows
         *
         */
 
        /**
         * @cfg {Ext.exporter.data.Row[]} summaries
         *
         * Table's summaries
         *
         */
 
        /**
         * @cfg {Ext.exporter.data.Group[]} groups
         *
         * Collection of groups that need to be exported.
         *
         */
    },
 
    autoGenerateId: false,
 
    applyColumns: function(data, dataCollection) {
        return this.checkCollection(data, dataCollection, 'Ext.exporter.data.Column');
    },
 
    updateColumns: function(collection, oldCollection) {
        var me = this;
 
        if (oldCollection) {
            oldCollection.un({
                add: me.onColumnAdd,
                remove: me.onColumnRemove,
                scope: me
            });
            Ext.destroy(oldCollection.items, oldCollection);
        }
 
        if (collection) {
            collection.on({
                add: me.onColumnAdd,
                remove: me.onColumnRemove,
                scope: me
            });
            me.onColumnAdd(collection, { items: collection.getRange() });
            me.syncColumns();
        }
    },
 
    syncColumns: function() {
        var cols = this.getColumns(),
            depth = this.getColDepth(cols, -1),
            result = {},
            i, j, length, len, keys, arr, prevCol, index;
 
        if (!cols) {
            return;
        }
 
        length = cols.length;
 
        for (= 0; i < length; i++) {
            cols.items[i].sync(0, depth);
        }
 
        this.getColumnLevels(cols, depth, result);
        keys = Ext.Object.getKeys(result);
        length = keys.length;
 
        for (= 0; i < length; i++) {
            arr = result[keys[i]];
            len = arr.length;
 
            for (= 0; j < len; j++) {
                if (=== 0) {
                    index = 1;
                }
                else if (arr[- 1]) {
                    prevCol = arr[- 1].getConfig();
                    index += (prevCol.mergeAcross ? prevCol.mergeAcross + 1 : 1);
                }
                else {
                    index++;
                }
 
                if (arr[j]) {
                    arr[j].setIndex(index);
                }
            }
        }
    },
 
    getLeveledColumns: function() {
        var cols = this.getColumns(),
            depth = this.getColDepth(cols, -1),
            result = {};
 
        this.getColumnLevels(cols, depth, result, true);
 
        return result;
    },
 
    /**
     * Fetch all bottom columns from the `columns` hierarchy.
     *
     * @return {Ext.exporter.data.Column[]} 
     */
    getBottomColumns: function() {
        var result = this.getLeveledColumns(),
            keys, len;
 
        keys = Ext.Object.getKeys(result);
        len = keys.length;
 
        return len ? result[keys[keys.length - 1]] : [];
    },
 
    getColumnLevels: function(columns, depth, result, topDown) {
        var col, i, j, len, name, level, cols;
 
        if (!columns) {
            return;
        }
 
        len = columns.length;
 
        for (= 0; i < len; i++) {
            col = columns.items[i];
            level = col.getLevel();
            cols = col.getColumns();
 
            name = 's' + level;
            result[name] = result[name] || [];
            result[name].push(col);
 
            if (!cols) {
                for (= level + 1; j <= depth; j++) {
                    name = 's' + j;
                    result[name] = result[name] || [];
                    result[name].push(topDown ? col : null);
                }
            }
            else {
                this.getColumnLevels(cols, depth, result, topDown);
            }
        }
    },
 
    onColumnAdd: function(collection, details) {
        var items = details.items,
            length = items.length,
            i, item;
 
        for (= 0; i < length; i++) {
            item = items[i];
            item.setTable(this);
        }
 
        this.syncColumns();
    },
 
    onColumnRemove: function(collection, details) {
        Ext.destroy(details.items);
        this.syncColumns();
    },
 
    getColumnCount: function() {
        var cols = this._columns,
            s = 0,
            i, length;
 
        if (cols) {
            length = cols.length;
 
            for (= 0; i < length; i++) {
                s += cols.items[i].getColumnCount();
            }
        }
 
        return s;
    },
 
    getColDepth: function(columns, level) {
        var m = 0,
            i, len;
 
        if (!columns) {
            return level;
        }
 
        len = columns.length;
 
        for (= 0; i < len; i++) {
            m = Math.max(m, this.getColDepth(columns.items[i]._columns, level + 1));
        }
 
        return m;
    },
 
    /**
     * Convenience method to add columns.
     * @param {Object/Array} config
     * @return {Ext.exporter.data.Column/Ext.exporter.data.Column[]}
     */
    addColumn: function(config) {
        if (!this._columns) {
            this.setColumns([]);
        }
 
        return this._columns.add(config || {});
    },
 
    /**
     * Convenience method to fetch a column by its id.
     * @param id
     * @return {Ext.exporter.data.Column} 
     */
    getColumn: function(id) {
        return this._columns ? this._columns.get(id) : null;
    }
});