/**
 * @private
 * This class encapsulates a row of managed Widgets/Components when a WidgetColumn, or
 * RowWidget plugin is used in a grid.
 *
 * The instances are recycled, and this class holds instances which are derendered so that they can
 * be moved back into newly rendered rows.
 *
 * Developers should not use this class.
 *
 */
Ext.define('Ext.grid.RowContext', {
    constructor: function(config) {
        Ext.apply(this, config);
        this.widgets = {};
    },
 
    setRecord: function(record, recordIndex) {
        var viewModel = this.viewModel;
 
        this.record = record;
        this.recordIndex = recordIndex;
        if (viewModel) {
            viewModel.set('record', record);
            viewModel.set('recordIndex', recordIndex);
        }
    },
 
    free: function() {
        var me = this,
            widgets = me.widgets,
            widgetId,
            widget,
            focusEl,
            viewModel = me.viewModel;
 
        me.record = null;
        if (viewModel) {
            viewModel.set('record');
            viewModel.set('recordIndex');
        }
 
        // All the widgets this RowContext manages must be blurred 
        // and moved into the detached body to save them from garbage collection. 
        for (widgetId in widgets) {
            widget = widgets[widgetId];
 
            // Focusables in a grid must not be tabbable by default when they get put back in. 
            focusEl = widget.getFocusEl();
            if (focusEl) {
                // Widgets are reused so we must reset their tabbable state 
                // regardless of their visibility. 
                // For example, when removing rows in IE8 we're attaching 
                // the nodes to a document-fragment which itself is invisible, 
                // so isTabbable() returns false. Next time when we're reusing 
                // this widget it will be attached to the document with its 
                // tabbable state unreset, which might lead to undesired results. 
                if (focusEl.isTabbable(true)) {
                    focusEl.saveTabbableState({
                        includeHidden: true
                    });
                }
 
                // Some browsers do not deliver a focus change upon DOM removal. 
                // Force the issue here. 
                focusEl.blur();
            }
            widget.detachFromBody();
            widget.hidden = true;
        }
    },
 
    getWidget: function(ownerId, widgetCfg) {
        var me = this,
            widgets = me.widgets || (me.widgets = {}),
            result;
 
        // Only spin up an attached ViewModel when we instantiate our first managed Widget 
        // which uses binding. 
        if (widgetCfg.bind && !me.viewModel) {
            me.viewModel = Ext.Factory.viewModel({
                parent: me.ownerGrid.lookupViewModel(),
                data: {
                    record: me.record,
                    recordIndex: me.recordIndex
                }
            }, me.ownerGrid.rowViewModel);
        }
 
        if (!(result = widgets[ownerId])) {
            result = widgets[ownerId] = Ext.widget(Ext.apply({
                viewModel: me.viewModel,
                _rowContext: me
            }, widgetCfg));
 
            // Components initialize binding on render. 
            // Widgets in finishRender which will not be called in this case. 
            // That is only called when rendered by a layout. 
            if (result.isWidget) {
                result.initBindable();
            }
        } else {
            result.hidden = false;
        }
 
        return result;
    },
 
    getWidgets: function() {
        var widgets = this.widgets,
            id,
            result = [];
 
        for (id in widgets) {
            result.push(widgets[id]);
        }
        return result;
    },
 
    destroy: function() {
        var me = this,
            widgets = me.widgets,
            widgetId,
            widget;
 
        for (widgetId in widgets) {
            widget = widgets[widgetId];
            widget._rowContext = null;
            widget.destroy();
        }
        
        Ext.destroy(me.viewModel);
        
        me.callParent();
    }
});