/**
 * This class remodels the grid store when required.
 *
 * @private
 */
Ext.define('Ext.pivot.feature.PivotStore', {
 
    config: {
        store:          null,
        grid:           null,
        matrix:         null,
        clsGrandTotal:  '',
        clsGroupTotal:  '',
        summaryDataCls: '',
        rowCls:         ''
    },
 
    constructor: function(config) {
        this.initConfig(config);
        return this.callParent(arguments);
    },
    
    destroy: function(){
        var me = this;
 
        Ext.destroy(me.storeListeners, me.matrixListeners);
 
        me.setConfig({
            store:  null,
            matrix: null,
            grid:   null
        });
        me.storeInfo = me.storeListeners = null;
        
        me.callParent(arguments);
    },
    
    updateStore: function(store) {
        var me = this;
 
        Ext.destroy(me.storeListeners);
        if (store) {
            me.storeListeners = store.on({
                // this event is fired by the pivot grid for private use 
                pivotstoreremodel:  me.processStore,
                scope:              me,
                destroyable:        true
            });
        }
    },
 
    updateMatrix: function(matrix){
        var me = this;
 
        Ext.destroy(me.matrixListeners);
        if (matrix) {
            me.matrixListeners = matrix.on({
                // this event is fired by the pivot grid for private use 
                groupexpand:    me.onGroupExpand,
                groupcollapse:  me.onGroupCollapse,
                scope:          me,
                destroyable:    true
            });
        }
    },
    
    processStore: function(){
        var me = this,
            store = me.getStore(),
            matrix = me.getMatrix(),
            records = [],
            isClassic = Ext.toolkit == 'classic',
            items, length, i, group, fields;
 
        if(!matrix || !store){
            return;
        }
 
        fields = matrix.getColumns();
 
        store.model.replaceFields(fields, true);
        store.removeAll(true);
 
        me.storeInfo = {};
 
        if(matrix.rowGrandTotalsPosition == 'first'){
            records.push.apply(records, me.processGrandTotal() || []);
        }
 
        items = matrix.leftAxis.getTree();
        length = items.length;
 
        for(= 0; i < length; i++){
            group = items[i];
            records.push.apply(records, me.processGroup({
                    group:              group,
                    previousExpanded:   (> 0 ? items[i-1].expanded : false)
                }) || []);
        }
 
        if(matrix.rowGrandTotalsPosition == 'last'){
            records.push.apply(records, me.processGrandTotal() || []);
        }
 
        store.loadData(records);
        if(!isClassic) {
            store.fireEvent('load', store);
        }
    },
    
    processGroup: function(config){
        var me = this,
            fn = me['processGroup' + Ext.String.capitalize(me.getMatrix().viewLayoutType)];
        
        if(!Ext.isFunction(fn)){
            // specified view type doesn't exist so let's use the outline view 
            fn = me.processGroupOutline;
        }
 
        return fn.call(me, config);
    },
    
    processGrandTotal: function(){
        var me = this,
            found = false,
            matrix = me.getMatrix(),
            group = {
                key:    matrix.grandTotalKey
            },
            records = [],
            lenT = matrix.totals.length,
            dimensions = matrix.leftAxis.dimensions.items,
            lenD = dimensions.length,
            i, j, k, total, column, record, key;
 
        for(= 0; i < lenT; i++){
            total = matrix.totals[i];
            record = total.record;
            k = lenD;
 
            if(record instanceof Ext.data.Model) {
 
                me.storeInfo[record.internalId] = {
                    leftKey: group.key,
                    rowStyle: '',
                    rowClasses: [me.getClsGrandTotal(), me.getSummaryDataCls()],
                    rendererParams: {}
                };
 
                for(= 0; j < lenD; j++){
                    column = dimensions[j];
 
                    if (matrix.viewLayoutType == 'compact' || j === 0) {
                        if (matrix.viewLayoutType == 'compact') {
                            key = matrix.compactViewKey;
                            k = 1;
                        } else {
                            key = column.getId();
                        }
                        record.data[key] = total.title;
                        me.storeInfo[record.internalId].rendererParams[key] = {
                            fn: 'groupOutlineRenderer',
                            group: group,
                            colspan: k,
                            hidden: false,
                            subtotalRow: true
                        };
                        found = true;
                    } else {
                        me.storeInfo[record.internalId].rendererParams[column.id] = {
                            fn: 'groupOutlineRenderer',
                            group: group,
                            colspan: 0,
                            hidden: found,
                            subtotalRow: true
                        };
                        k--;
                    }
                }
 
                // for all top axis columns use a new renderer 
                me.storeInfo[record.internalId].rendererParams['topaxis'] = {
                    fn: 'topAxisRenderer'
                };
 
                records.push(record);
            }
        }
 
        return records;
    },
    
// Outline view functions     
 
    processGroupOutline: function(config){
        var me = this,
            group = config['group'],
            results = [];
        
        if(group.record){
            me.processRecordOutline({
                results: results,
                group: group
            });
        }else{
            me.processGroupOutlineWithChildren({
                results: results,
                group: group,
                previousExpanded: config.previousExpanded
            });
        }
        
        return results;
    },
 
    processGroupOutlineWithChildren: function(config){
        var me = this,
            matrix = me.getMatrix(),
            group = config['group'],
            previousExpanded = config['previousExpanded'],
            hasSummaryData, record, i, len;
            
        hasSummaryData = (!group.expanded || (group.expanded && matrix.rowSubTotalsPosition == 'first'));
        record = group.expanded ? group.records.expanded : group.records.collapsed;
        
        me.processGroupHeaderRecordOutline({
            results: config.results,
            group: group,
            record: record,
            previousExpanded: previousExpanded,
            hasSummaryData: hasSummaryData
        });
 
        if(group.expanded){
            if(group.children){
                len = group.children.length;
                for(= 0; i < len; i++){
                    if(group.children[i]['children']){
                        me.processGroupOutlineWithChildren({
                            results: config.results,
                            group: group.children[i]
                        });
                    }else{
                        me.processRecordOutline({
                            results: config.results,
                            group: group.children[i]
                        });
                    }
                }
            }
            if(matrix.rowSubTotalsPosition == 'last'){
                record = group.records.footer;
                me.processGroupHeaderRecordOutline({
                    results: config.results,
                    group: group,
                    record: record,
                    previousExpanded: previousExpanded,
                    subtotalRow: true,
                    hasSummaryData: true
                });
            }
        }
    },
    
    processGroupHeaderRecordOutline: function(config){
        var me = this,
            matrix = me.getMatrix(),
            group = config['group'], 
            record = config['record'], 
            previousExpanded = config['previousExpanded'], 
            subtotalRow = config['subtotalRow'],
            hasSummaryData = config['hasSummaryData'],
            items = matrix.leftAxis.dimensions.items,
            len = items.length,
            k = len,
            found = false,
            i, column;
            
        me.storeInfo[record.internalId] = {
            leftKey: group.key,
            rowStyle: '',
            rowClasses: [me.getClsGroupTotal(), hasSummaryData ? me.getSummaryDataCls() : ''],
            rendererParams: {}
        };
 
        for(= 0; i < len; i++){
            column = items[i];
 
            if(column.id == group.dimension.id){
                me.storeInfo[record.internalId].rendererParams[column.id] = {
                    fn: 'groupOutlineRenderer',
                    group: group,
                    colspan: k,
                    hidden: false,
                    previousExpanded: previousExpanded,
                    subtotalRow: subtotalRow
                };
                found = true;
            }else{
                me.storeInfo[record.internalId].rendererParams[column.id] = {
                    fn: 'groupOutlineRenderer',
                    group: group,
                    colspan: 0,
                    hidden: found,
                    previousExpanded: previousExpanded,
                    subtotalRow: subtotalRow
                };
                k--;
            }
        }
 
        // for all top axis columns use a new renderer 
        me.storeInfo[record.internalId].rendererParams['topaxis'] = {
            fn: (hasSummaryData ? 'topAxisRenderer' : 'topAxisNoRenderer'),
            group: group
        };
        
        config.results.push(record);
    },
 
    processRecordOutline: function(config){
        var me = this,
            group = config['group'], 
            found = false,
            record = group.record,
            items = me.getMatrix().leftAxis.dimensions.items,
            len = items.length,
            i, column;
 
        me.storeInfo[record.internalId] = {
            leftKey: group.key,
            rowStyle: '',
            rowClasses: [me.getSummaryDataCls()],
            rendererParams: {}
        };
 
        for(= 0; i < len; i++){
            column = items[i];
 
            if(column.id == group.dimension.id){
                found = true;
            }
 
            me.storeInfo[record.internalId].rendererParams[column.id] = {
                fn: 'recordOutlineRenderer',
                group: group,
                hidden: !found
            };
        }
 
        // for all top axis columns use a new renderer 
        me.storeInfo[record.internalId].rendererParams['topaxis'] = {
            fn: 'topAxisRenderer',
            group: group
        };
 
        config.results.push(record);
    },
    
    
// Compact view functions 
    
    processGroupCompact: function(config){
        var me = this,
            group = config['group'], 
            previousExpanded = config['previousExpanded'],
            results = [];
        
        if(group.record){
            me.processRecordCompact({
                results: results,
                group: group
            });
        }else{
            me.processGroupCompactWithChildren({
                results: results,
                group: group,
                previousExpanded: previousExpanded
            });
        }
        
        return results;
    },
 
    processGroupCompactWithChildren: function(config){
        var me = this,
            matrix = me.getMatrix(),
            group = config['group'], 
            previousExpanded = config['previousExpanded'],
            hasSummaryData, i, len;
            
        hasSummaryData = (!group.expanded || (group.expanded && matrix.rowSubTotalsPosition == 'first'));
 
        me.processGroupHeaderRecordCompact({
            results: config.results,
            group: group,
            record: group.expanded ? group.records.expanded : group.records.collapsed,
            previousExpanded: previousExpanded,
            hasSummaryData: hasSummaryData
        });
 
        if(group.expanded){
            if(group.children){
                len = group.children.length;
                for(= 0; i < len; i++){
                    if(group.children[i]['children']){
                        me.processGroupCompactWithChildren({
                            results: config.results,
                            group: group.children[i]
                        });
                    }else{
                        me.processRecordCompact({
                            results: config.results,
                            group: group.children[i]
                        });
                    }
                }
            }
            if(matrix.rowSubTotalsPosition == 'last'){
                me.processGroupHeaderRecordCompact({
                    results: config.results,
                    group: group,
                    record: group.records.footer,
                    previousExpanded: previousExpanded,
                    subtotalRow: true,
                    hasSummaryData: true
                });
            }
        }
    },
    
    processGroupHeaderRecordCompact: function(config){
        var me = this,
            matrix = me.getMatrix(),
            group = config['group'],
            record = config['record'],
            previousExpanded = config['previousExpanded'],
            subtotalRow = config['subtotalRow'],
            hasSummaryData = config['hasSummaryData'];
 
        me.storeInfo[record.internalId] = {
            leftKey: group.key,
            rowStyle: '',
            rowClasses: [me.getClsGroupTotal(), hasSummaryData ? me.getSummaryDataCls() : ''],
            rendererParams: {}
        };
 
        me.storeInfo[record.internalId].rendererParams[matrix.compactViewKey] = {
            fn: 'groupCompactRenderer',
            group: group,
            colspan: 0,
            previousExpanded: previousExpanded,
            subtotalRow: subtotalRow
        };
 
        // for all top axis columns use a new renderer 
        me.storeInfo[record.internalId].rendererParams['topaxis'] = {
            fn: (hasSummaryData ? 'topAxisRenderer' : 'topAxisNoRenderer'),
            group: group
        };
 
        config.results.push(record);
    },
 
    processRecordCompact: function(config){
        var me = this,
            group = config['group'], 
            record = group.record;
            
        me.storeInfo[record.internalId] = {
            leftKey: group.key,
            rowStyle: '',
            rowClasses: [me.getSummaryDataCls()],
            rendererParams: {}
        };
        
        me.storeInfo[record.internalId].rendererParams[me.getMatrix().compactViewKey] = {
            fn: 'recordCompactRenderer',
            group: group
        }; 
        
        // for all top axis columns use a new renderer 
        me.storeInfo[record.internalId].rendererParams['topaxis'] = {
            fn: 'topAxisRenderer',
            group: group
        };
 
        config.results.push(record);
    },
 
// Tabular view functions 
 
    processGroupTabular: function(config){
        var me = this,
            group = config['group'],
            results = [];
 
        if(group.record){
            me.processRecordTabular({
                results: results,
                group: group
            });
        }else{
            me.processGroupTabularWithChildren({
                results: results,
                group: group,
                previousExpanded: config.previousExpanded
            });
        }
 
        return results;
    },
 
    processGroupTabularWithChildren: function(config, noFirstRecord){
        var me = this,
            matrix = me.getMatrix(),
            group = config['group'],
            previousExpanded = config['previousExpanded'],
            record, i, child, len;
 
        if(!noFirstRecord) {
            me.processGroupHeaderRecordTabular({
                results: config.results,
                group: group,
                previousExpanded: previousExpanded,
                hasSummaryData: !group.expanded
            });
        }
 
        if(group.expanded){
            if(group.children){
                len = group.children.length;
                for(= 0; i < len; i++){
                    child = group.children[i];
                    if(=== 0 && child.children && child.expanded){
                        me.processGroupTabularWithChildren({
                            results: config.results,
                            group: child
                        }, true);
                    }else if(> 0){
                        if(child.children){
                            me.processGroupTabularWithChildren({
                                results: config.results,
                                group: child
                            });
                        }else{
                            me.processRecordTabular({
                                results: config.results,
                                group: child
                            });
                        }
                    }
                }
            }
            if(matrix.rowSubTotalsPosition !== 'none'){
                record = group.records.footer;
                me.processGroupHeaderRecordTabular({
                    results: config.results,
                    group: group,
                    record: record,
                    previousExpanded: previousExpanded,
                    subtotalRow: true,
                    hasSummaryData: true
                });
            }
        }
    },
 
    // used in PivotEvents 
    getTabularGroupRecord: function(group){
        var record = group.record;
 
        if(!record) {
            if (!group.expanded) {
                record = group.records.collapsed;
            } else {
                record = this.getTabularGroupRecord(group.children[0]);
            }
        }
        return record;
    },
 
    processGroupHeaderRecordTabular: function(config){
        var me = this,
            matrix = me.getMatrix(),
            group = config['group'],
            record = config['record'],
            previousExpanded = config['previousExpanded'],
            subtotalRow = config['subtotalRow'],
            hasSummaryData = config['hasSummaryData'],
            dimensions = matrix.leftAxis.dimensions,
            len = dimensions.length,
            k = len,
            found = false,
            rendererParams = {},
            rowClasses = [],
            i, dim, item, keys, parentGroup, prevGroup;
 
        if(!record) {
            item = group;
            record = item.record;
            while(!record){
                rendererParams[item.dimension.id] = {
                    fn: 'groupTabularRenderer',
                    group: item,
                    colspan: 0,
                    hidden: false,
                    previousExpanded: previousExpanded,
                    subtotalRow: subtotalRow
                };
                if(item.children){
                    if(!item.expanded){
                        record = item.records.collapsed;
                        rendererParams[item.dimension.id].colspan = len - item.level;
                    }else{
                        item = item.children[0];
                    }
                }else{
                    record = item.record;
                }
            }
            //Ext.apply(record.data, item.data); 
            found = false;
            for(= 0; i < len; i++) {
                dim = matrix.leftAxis.dimensions.items[i];
                if(rendererParams[dim.id]){
                    record.data[dim.id] = rendererParams[dim.id].group.name;
                    found = true;
                }else if(found){
                    rendererParams[dim.id] = {
                        fn: 'groupTabularRenderer',
                        group: group,
                        colspan: 0,
                        hidden: true,
                        previousExpanded: previousExpanded,
                        subtotalRow: subtotalRow
                    };
                }
            }
            if(group.level > 0){
                prevGroup = group;
                keys = prevGroup.key.split(matrix.keysSeparator);
                keys.length--;
                parentGroup = matrix.leftAxis.items.getByKey(keys.join(matrix.keysSeparator));
                while(parentGroup && parentGroup.children[0] == prevGroup){
                    rendererParams[parentGroup.dimension.id] = {
                        fn: 'groupTabularRenderer',
                        group: parentGroup,
                        colspan: 0,
                        hidden: false,
                        previousExpanded: previousExpanded,
                        subtotalRow: subtotalRow
                    };
                    record.data[parentGroup.dimension.id] = parentGroup.name;
 
                    prevGroup = parentGroup;
                    keys = prevGroup.key.split(matrix.keysSeparator);
                    keys.length--;
                    parentGroup = matrix.leftAxis.items.getByKey(keys.join(matrix.keysSeparator));
                }
            }
        }else{
            for(= 0; i < len; i++){
                dim = matrix.leftAxis.dimensions.items[i];
 
                rendererParams[dim.id] = {
                    fn: 'groupTabularRenderer',
                    group: group,
                    colspan: 0,
                    hidden: false,
                    previousExpanded: previousExpanded,
                    subtotalRow: subtotalRow
                };
                if(dim.id == group.dimension.id){
                    rendererParams[dim.id].colspan = k;
                    found = true;
                }else{
                    rendererParams[dim.id].hidden = found;
                    k--;
                }
            }
            item = group;
        }
 
        if(hasSummaryData){
            rowClasses.push(me.getClsGroupTotal());
        }
        if(!config.record){
            rowClasses.push(me.getSummaryDataCls());
        }
 
        me.storeInfo[record.internalId] = {
            leftKey: group.key,
            rowStyle: '',
            rowClasses: rowClasses,
            rendererParams: rendererParams
        };
 
        // for all top axis columns use a new renderer 
        me.storeInfo[record.internalId].rendererParams['topaxis'] = {
            fn: 'topAxisRenderer',
            group: item
        };
 
        config.results.push(record);
    },
 
    processRecordTabular: function(config){
        var me = this,
            group = config['group'],
            found = false,
            record = group.record,
            items = me.getMatrix().leftAxis.dimensions.items,
            len = items.length,
            i, column;
 
        me.storeInfo[record.internalId] = {
            leftKey:            group.key,
            rowStyle:           '',
            rowClasses:         [me.getSummaryDataCls()],
            rendererParams: {}
        };
 
        for(= 0; i < len; i++){
            column = items[i];
 
            if(column.id == group.dimension.id){
                found = true;
            }
 
            me.storeInfo[record.internalId].rendererParams[column.id] = {
                fn: 'recordTabularRenderer',
                group: group,
                hidden: !found
            };
        }
 
        // for all top axis columns use a new renderer 
        me.storeInfo[record.internalId].rendererParams['topaxis'] = {
            fn: 'topAxisRenderer',
            group: group
        };
 
        config.results.push(record);
    },
 
 
// various functions 
 
    doExpandCollapse: function(key, oldRecord){
        var me = this,
            gridMaster = me.getGrid(),
            group;
 
        group = me.getMatrix().leftAxis.findTreeElement('key', key);
        if(!group){
            return;
        }
 
        me.doExpandCollapseInternal(group.node, oldRecord);
 
        gridMaster.fireEvent((group.node.expanded ? 'pivotgroupexpand' : 'pivotgroupcollapse'), gridMaster, 'row', group.node);
    },
 
    doExpandCollapseInternal: function(group, oldRecord){
        var me = this,
            store = me.getStore(),
            isClassic = Ext.toolkit == 'classic',
            items, oldItems, startIdx, len;
 
        group.expanded = !group.expanded;
 
        oldItems = me.processGroup({
            group: group,
            previousExpanded: false
        });
        
        group.expanded = !group.expanded;
        
        items = me.processGroup({
            group: group,
            previousExpanded: false
        });
 
 
        if(items.length && oldItems.length && (startIdx = store.indexOf(oldItems[0])) !== -1){
            if(isClassic) {
                store.suspendEvents();
            }
 
            if(group.expanded){
                store.remove(store.getAt(startIdx));
                store.insert(startIdx, items);
            }else{
                store.remove(oldItems);
                store.insert(startIdx, items);
 
            }
 
            me.removeStoreInfoData(oldItems);
 
            if(isClassic) {
                store.resumeEvents();
                // the replace event is better than remove and inserts 
                store.fireEvent('replace', store, startIdx, oldItems, items);
            }
        }
 
    },
    
    removeStoreInfoData: function(records){
        var len = records.length,
            record, i;
 
        for(= 0; i < len; i++){
            record = records[i];
 
            if(this.storeInfo[record.internalId]){
                delete this.storeInfo[record.internalId];
            }
        }
    },
 
    onGroupExpand: function(matrix, type, item){
        if(type == 'row') {
            if(item) {
                this.doExpandCollapseInternal(item, item.records.collapsed);
            }else{
                this.processStore();
            }
        }
    },
 
    onGroupCollapse: function(matrix, type, item){
        if(type == 'row') {
            if(item) {
                this.doExpandCollapseInternal(item, item.records.expanded);
            }else{
                this.processStore();
            }
        }
    }
});