/**
 * Controller controls process of changing sections and provides API for doing it programmatically.
 *
 * Will dispatch these events through EventBus:
 *
 * **runtime.before_section_changed**
 *
 * **runtime.section_changed**
 *
 * @see MF.runtime.applications.authenticationaware.controller.Workbench
 *
 * @author Sergei Lissovski <sergei.lissovski@modera.net>
 */
Ext.define('MF.runtime.applications.WorkbenchController', {
    extend: 'Ext.app.Controller',

    requires: [
        'MF.Util'
    ],

    /**
     * @private {MF.runtime.Section} currentlyLoadedSection
     */

    // override
    init: function() {
        this.control({
            'component[runtimerole=workbench]': {
                changesection: this.onSectionChange
            }
        });

        this.getContainer().get('root_execution_context').on('statechanged', this.onExecutionContextStateChanged, this);

        this.callParent(arguments);
    },

    // private
    onExecutionContextStateChanged: function(ec, diff, oldValues, newValues) {
        // if section is changed
        if (oldValues.section != newValues.section) {
            this.changeSection(newValues.section, newValues.params, Ext.emptyFn, true);

            return false;
        }
    },

    // private
    getUi: function() {
        return Ext.ComponentQuery.query('component[runtimerole=workbench]')[0];
    },

    // private
    getContainer: function() {
        return this.application.getContainer();
    },

    /**
     * @private
     * @param {MF.runtime.Section} section
     * @param {String} sectionName  This will be published to execution context
     * @param {Function} sectionActivatedCallback  Optional callback. If given then it will be invoked when section has been fully
     *                                             initialized and user can start interacting with it
     * @param {Object} params (optional)
     */
    activateSection: function(section, sectionName, params, sectionActivatedCallback) {
        var me = this;

        var eventBus = this.getContainer().get('event_bus'),
            workbench = this.getContainer().get('workbench'),
            executionContext = this.getContainer().get('root_execution_context');

        if (false !== eventBus.fireEvent('runtime.before_section_changed', me, me.currentlyLoadedSection, section)) {
            var previouslyLoadedSectionController = me.currentlyLoadedSection;

            me.getUi().showSectionBeingChangedIndicator(function() {
                section.activate(workbench, function(onSectionUiCreatedCallback) {
                    executionContext.setSectionName(sectionName); // this will result in having all Activity parameters reset

                    workbench.getActivitiesManager().bootstrap(params, function() {
                        me.currentlyLoadedSection = section;

                        me.getUi().hideSectionBeingChangedIndicator(function() {
                            me.fireEvent('runtime.section_changed', me, previouslyLoadedSectionController, section);

                            if (Ext.isFunction(onSectionUiCreatedCallback)) {
                                onSectionUiCreatedCallback(me.getUi());
                            }

                            if (Ext.isFunction(sectionActivatedCallback)) {
                                sectionActivatedCallback(section);
                            }

                            // Usually this will update navigation, like highlighting a newly loaded section
                            me.getUi().sectionChanged(MF.Util.resolveOwningSectionName(sectionName), params, sectionName);
                        });
                    });
                });
            });
        }
    },

    /**
     * @private
     * @param {Object} section
     */
    onSectionChange: function(section) {
        if (!section.hasOwnProperty('controller')) {
            throw this.$className + ".onSectionChange(section): section parameter doesn't have 'controller' property!";
        }

        this.changeSection(section.id, section.metadata['activation_params'] || null);
    },

    /**
     * Returns a currently loaded section.
     *
     * @return {MF.runtime.Section}
     */
    getCurrentlyLoadedSection: function() {
        return this.currentlyLoadedSection;
    },

    /**
     * Do not rely on this method existence, it is not public API and may be gone during refactoring.
     *
     * @internal
     * @private
     * @param {MF.runtime.Section} section
     */
    setCurrentlyLoadedSection: function(section) {
        this.currentlyLoadedSection = section;
    },

    /**
     * Allows to change a section.
     *
     * Parameter `handleSectionNotFound` has been added in v0.5.0.
     *
     * @param {String} sectionName  A short section name
     * @param {Object} params  Optional. Parameters to activate a new section with
     * @param {Function} sectionChangedCallback  Optional. Callback to invoke when section is changed, the callback will
     *                                           receive an argument indicating if a section has been successfully changed.
     *                                           If something went wrong then usually an exception is thrown, but you have
     *                                           a chance to avoid having an exception thrown if a section is not found
     *                                           by using a `handleSectionNotFound` parameter of this method.
     * @param {Boolean} handleSectionNotFound Optional. Defaults to FALSE. If set to TRUE and given section is not found
     *                                        then a nice message will be presented to a user instead of throwing an
     *                                        exception which can be seen only in the developers console. If you have
     *                                        provided `sectionChangedCallback` and section could not have been loaded,
     *                                        then the callback will receive FALSE.
     */
    changeSection: function(sectionName, params, sectionChangedCallback, handleSectionNotFound) {
        var me = this;

        console.debug(
            '%s.changeSection(sectionName="%s", params=%s, sectionChangeCallback): Changing section to "%s", params: %s',
            this.$className, sectionName, Ext.encode(params), sectionName, Ext.encode(params)
        );

        params = params || {};

        var workbench = this.getContainer().get('workbench'),
            ec = this.getRootExecutionContext();

        var sectionController = null;
        if (handleSectionNotFound) {
            sectionController = workbench.getSection(sectionName);

            if (!sectionController) {
                // It is not an ideal solution because previously loaded section is still loaded as such,
                // but at the same by not "deactivating" currently loaded section things like SharedActivitiesProvider
                // will still work properly (see below how "deactivate" method is used)
                // See MF.runtime.applications.authenticationaware.controller.Viewport.initializeWorkbenchAndShowActivities()
                // method, while we here simply show an error message, the "initializeWorkbenchAndShowActivities" creates
                // a dummy "section" and loads it (otherwise SharedActivitiesProvider) doesn't work, here we can't
                // really easily implement this approach because when a section is activated using "activateSection"
                // method the latter will automatically switch the UI from displaying an error to a container
                // that is used to present Activity's UI
                this.getUi().showSectionNotFoundError(sectionName);

                // We could have updated then UI to remove highlighting from the menu here using
                // me.getUi().sectionChanged(), but we will not do that because technically speaking current
                // section is still loaded into runtime (see a comment above)

                sectionChangedCallback(false);

                return;
            }
        } else {
            sectionController = workbench.getSectionOrDie(sectionName);
        }

        ec.startTransaction();

        var onSectionActivated = function() {
            ec.commitTransaction();

            if (Ext.isFunction(sectionChangedCallback)) {
                sectionChangedCallback(true);
            }
        };

        // if there's already active section we will at first deactivate it
        if (this.currentlyLoadedSection) {
            this.currentlyLoadedSection.deactivate(function() {
                me.activateSection(sectionController, sectionName, params, onSectionActivated);
            });
        } else {
            this.activateSection(sectionController, sectionName, params, onSectionActivated);
        }
    },

    // private
    getRootExecutionContext: function() {
        return this.getContainer().get('root_execution_context');
    },

    halt: function() {
        return this.getRootExecutionContext().removeListener('statechanged', this.onExecutionContextStateChanged, this);
    }
});