/**
 * This grid extension allows users to see the grouped data as a tree with
 * groups that can be expanded or collapsed.
 *
 * The component reacts to changes in store groupers.
 *
 * **Note** It doesn't work with a {@link Ext.data.virtual.Store}
 */
Ext.define('Ext.grid.TreeGrouped', {
    extend: 'Ext.grid.Grid',
    xtype: 'treegroupedgrid',
 
    mixins: [
        'Ext.grid.mixin.Menus'
    ],
 
    requires: [
        'Ext.grid.grouped.NavigationModel',
        'Ext.grid.grouped.selection.Model',
        'Ext.grid.AdvancedGroupStore',
        'Ext.grid.column.Groups',
        'Ext.grid.row.Group',
        'Ext.grid.menu.Groups',
        'Ext.grid.menu.AddGroup',
        'Ext.grid.menu.RemoveGroup'
    ],
 
    isTreeGroupedGrid: true,
 
    /**
     * @event grouptap
     * Fires whenever a group's tap event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    /**
     * @event groupsingletap
     * Fires whenever a group's singletap event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    /**
     * @event groupdoubletap
     * Fires whenever a group's doubletap event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    /**
     * @event grouptaphold
     * Fires whenever a group's taphold event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    /**
     * @event groupcontextmenu
     * Fires whenever a group's contextmenu event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    /**
     * @event groupsummarytap
     * Fires whenever a group's summary tap event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    /**
     * @event groupsummarysingletap
     * Fires whenever a group's summary singletap event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    /**
     * @event groupsummarydoubletap
     * Fires whenever a group's summary doubletap event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    /**
     * @event groupsummarytaphold
     * Fires whenever a group's summary taphold event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    /**
     * @event groupsummarycontextmenu
     * Fires whenever a group's summary contextmenu event fires
     * @param {Ext.grid.TreeGrouped} this 
     * @param {Object} params An object with multiple keys to identify the group
     * @param {Ext.event.Event} e The event object
     */
 
    eventedConfig: {
        /**
         * @cfg {String} [summaryPosition='hidden']
         * Set the position of the summary row for the entire grid:
         *
         *  * `'hidden'`: Hide the summary row
         *  * `'top'`: Show the summary row as the first row in the grid
         *  * `'bottom'`: Show the summary row as the last row in the grid
         *  * `'docked'`: Show the summary row docked at the bottom of the grid (when
         *  the {@link Ext.grid.plugin.Summaries} plugin is used)
         */
        summaryPosition: 'bottom',
        /**
         * @cfg {String} [groupSummaryPosition='hidden']
         * Set the position of the summary row for each group:
         *
         *  * `'hidden'`: Hide the group summary row
         *  * `'top'`: If the group is expanded or collapsed the summary is shown on the
         *  group header
         *  * `'bottom'`: When the group is expanded the summary row is shown as a
         *  group footer, after all records/groups are shown
         */
        groupSummaryPosition: 'bottom'
    },
 
    config: {
        groupsColumn: {
            xtype: 'groupscolumn',
            minWidth: 150
        },
        /**
         * @cfg {Boolean} [startCollapsed=false]
         * True to start all groups collapsed when the grid is rendered for the first time.
         */
        startCollapsed: true,
 
        /**
         * @cfg {String/Array/Ext.Template} groupHeaderTpl
         * A string Template snippet, an array of strings (optionally followed by an object
         * containing Template methods) to be used to construct a Template, or a Template instance.
         *
         * - Example 1 (Template snippet):
         *
         *       groupHeaderTpl: 'Group: {name} ({group.items.length})'
         *
         * - Example 2 (Array):
         *
         *       groupHeaderTpl: [
         *           'Group: ',
         *           '<div>{name:this.formatName}</div>',
         *           {
         *               formatName: function(name) {
         *                   return Ext.String.trim(name);
         *               }
         *           }
         *       ]
         *
         * - Example 3 (Template Instance):
         *
         *       groupHeaderTpl: Ext.create('Ext.XTemplate',
         *           'Group: ',
         *           '<div>{name:this.formatName}</div>',
         *           {
         *               formatName: function(name) {
         *                   return Ext.String.trim(name);
         *               }
         *           }
         *       )
         *
         * @cfg {String}           groupHeaderTpl.groupField The field name being grouped by.
         * @cfg {String}           groupHeaderTpl.columnName The column header associated with
         * the field being grouped by *if there is a column for the field*, falls back
         * to the groupField name.
         * @cfg {String}           groupHeaderTpl.name The name of the group.
         * @cfg {Ext.util.Group}   groupHeaderTpl.group The group object.
         */
        groupHeaderTpl: '{name}',
 
        /**
         * @cfg {String/Array/Ext.Template} groupSummaryTpl
         * A string Template snippet, an array of strings (optionally followed by an object
         * containing Template methods) to be used to construct a Template, or a Template instance.
         *
         * - Example 1 (Template snippet):
         *
         *       groupSummaryTpl: 'Group: {name}'
         *
         * - Example 2 (Array):
         *
         *       groupSummaryTpl: [
         *           'Group: ',
         *           '<div>{name:this.formatName}</div>',
         *           {
         *               formatName: function(name) {
         *                   return Ext.String.trim(name);
         *               }
         *           }
         *       ]
         *
         * - Example 3 (Template Instance):
         *
         *       groupSummaryTpl: Ext.create('Ext.XTemplate',
         *           'Group: ',
         *           '<div>{name:this.formatName}</div>',
         *           {
         *               formatName: function(name) {
         *                   return Ext.String.trim(name);
         *               }
         *           }
         *       )
         *
         * @cfg {String}           groupSummaryTpl.groupField The field name being grouped by.
         * @cfg {String}           groupSummaryTpl.columnName The column header associated with
         * the field being grouped by *if there is a column for the field*, falls back
         * to the groupField name.
         * @cfg {String}           groupSummaryTpl.name The name of the group.
         * @cfg {Ext.util.Group}   groupSummaryTpl.group The group object.
         */
        groupSummaryTpl: 'Summary ({name})',
 
        /**
         * @cfg {String/Array/Ext.Template} summaryTpl
         * A string Template snippet, an array of strings (optionally followed by an object
         * containing Template methods) to be used to construct a Template, or a Template instance.
         *
         * - Example (Template snippet):
         *
         *       groupSummaryTpl: 'Summary: {store.data.length}'
         *
         * @cfg {Ext.data.Store}   summaryTpl.store The store object.
         */
        summaryTpl: 'Summary ({store.data.length})'
    },
 
    itemConfig: {
        xtype: 'gridgrouprow'
    },
 
    columnMenu: {
        items: {
            showInGroups: false,
            groups: {
                xtype: 'gridgroupsmenuitem',
                separator: true,
                weight: -70
            },
            groupByThis: {
                separator: false,
                weight: -60,
                handler: 'up.onGroupByThisMenu'
            },
            addGroup: {
                xtype: 'gridaddgroupmenuitem',
                weight: -50,
                handler: 'up.onAddGroupMenu'
            },
            removeGroup: {
                xtype: 'gridremovegroupmenuitem',
                weight: -40,
                handler: 'up.onRemoveGroupMenu'
            }
        }
    },
 
    selectionModel: 'groupedgrid',
    navigationModel: 'groupedgrid',
    cellSelector: '.' + Ext.baseCSSPrefix + 'gridcell',
    variableHeights: true,
    enableColumnMove: false,
 
    initialize: function() {
        var me = this,
            store = me.getStore(),
            groupers = store && store.getGroupers(false);
 
        me.groupingColumn = me.registerColumn(Ext.merge({
            groupHeaderTpl: me.getGroupHeaderTpl(),
            groupSummaryTpl: me.getGroupSummaryTpl(),
            summaryTpl: me.getSummaryTpl(),
            hidden: !(groupers && groupers.length)
        }, me.getGroupsColumn()));
 
        me.callParent();
    },
 
    /**
     * Expand all groups
     */
    expandAll: function() {
        var store = this.store;
 
        if (store && store.isMultigroupStore) {
            store.setStartCollapsed(false);
            store.refreshData(true);
        }
    },
 
    /**
     * Collapse all groups
     */
    collapseAll: function() {
        var store = this.store;
 
        if (store && store.isMultigroupStore) {
            store.setStartCollapsed(true);
            store.refreshData(true);
        }
    },
 
    updateSummaryPosition: function(pos) {
        var store = this.store;
 
        if (store && store.isMultigroupStore && !this.isConfiguring) {
            store.setSummaryPosition(pos);
        }
    },
 
    updateGroupSummaryPosition: function(pos) {
        var store = this.store;
 
        if (store && store.isMultigroupStore && !this.isConfiguring) {
            store.setGroupSummaryPosition(pos);
        }
    },
 
    isRTL: function() {
        if (Ext.isFunction(this.isLocalRtl)) {
            return this.isLocalRtl();
        }
 
        return false;
    },
 
    onStoreRefresh: function(store) {
        var me = this,
            col = me.groupingColumn,
            realStore = me.getStore(),
            groupers = realStore && realStore.getGroupers(false);
 
        if (col) {
            col.setHidden(!(groupers && groupers.length > 0));
        }
 
        if (store.isMultigroupStore && me.dataRange) {
            me.dataRange.records = store.getRange();
        }
 
        me.callParent(arguments);
    },
 
    onChildTap: function(context) {
        this.handleRowEvent('tap', context.event);
        this.callParent([context]);
    },
 
    onChildTapHold: function(context) {
        this.handleRowEvent('taphold', context.event);
        this.callParent([context]);
    },
 
    onChildSingleTap: function(context) {
        this.handleRowEvent('singletap', context.event);
        this.callParent([context]);
    },
 
    onChildDoubleTap: function(context) {
        this.handleRowEvent('doubletap', context.event);
        this.callParent([context]);
    },
 
    onChildContextMenu: function(context) {
        this.handleRowEvent('contextmenu', context.event);
        this.callParent([context]);
    },
 
    handleRowEvent: function(type, e) {
        var cell = Ext.fly(e.getTarget(this.cellSelector)),
            navModel = this.getNavigationModel(),
            location;
 
        // the cell type might have been changed
        if (cell && cell.component && cell.component.handleEvent) {
            cell.component.handleEvent(type, e);
 
            if (navModel) {
                location = navModel.getLocation();
 
                if (location) {
                    location.refresh();
                }
            }
        }
    },
 
    isGrouping: function() {
        return false;
    },
 
    privates: {
        applyStore: function(store, oldStore) {
            var me = this,
                ret = me.callParent([ store, oldStore ]);
 
            if (ret) {
                me.store = ret = new Ext.grid.AdvancedGroupStore({
                    fireReplaceEvent: false,
                    source: ret,
                    groupSummaryPosition: me.getGroupSummaryPosition(),
                    summaryPosition: me.getSummaryPosition(),
                    startCollapsed: me.getStartCollapsed(),
                    storeEventListeners: {
                        sort: 'onStoreSort'
                    },
                    onStoreSort: function(source, sorters) {
                        this.fireEvent('sort', source, sorters);
                    }
                });
 
                // <debug>
                if (ret.isVirtualStore) {
                    Ext.raise('Virtual stores are not supported');
                }
                // </debug>
            }
 
            return ret;
        }
    }
 
});