/* global UIManager, LoadingOverlay, Proxy, debug,
 OTPOverlay, Notification, BlockedOverlay, AccessedOverlay, AppPairedOverlay,
 AppPairedDisabledOverlay, NotificationController, Status, ErrorManager, Config,
 NewSessionOverlay, EnabledOverlay, DisabledOverlay, Utils, OTPOverlay, 
 AppPairedDisabledOverlay, AppUnpairedOverlay, ChangedOverlay */

/**
 *  Controller.
 *  This code is the one which controls the whole flow between UIManager & Proxy
 *  Any action in the UI which involve any decission out of the visual sandbox,
 *  must be forwarded to the `Controller`.
 */

(function (exports) {

    'use strict';

    // This wrapper, and the listeners below are a
    // replacement of navigator.onLine since we cannot
    // mock it in phantomjs (is always true)
    // https://github.com/ariya/phantomjs/issues/10647
    // In the test phase this function is replaced with
    // the mock depending on what we want to test
    window.navigator.checkOnline = navigator.onLine;
    window.addEventListener('offline', function () {
        window.navigator.checkOnline = false;
    }, false);

    window.addEventListener('online', function () {
        window.navigator.checkOnline = true;
    }, false);

    // Helper boolean to know if we were initalized already
    var _isInitialized;
    // Helper boolean to know if we are logged or not
    var _isAuthenticated;
    // This var will keep the current Operation rendered.
    var _currentOperation = null;

    // Check if the app is visible to the user
    function _isAppVisible() {
        return !document.hidden;
    }

    function _getLockedAncestors(operation, list) {
        if (operation.parent && operation.parent !== null) {

            var parent = operation.parent;
            var onOffStatus = parent.status === 'interval' ? _getStatusFromInterval(parent.from, parent.to) : parent.status;
            if (onOffStatus === 'off') {
                list[operation.parentId] = parent;
            }
            _getLockedAncestors(parent, list);
        }
        else {
            Controller.getOperations(Config.environment.latchonID, function (latchonApp) {
                if (latchonApp.status === 'off') {
                    list[Config.environment.latchonID] = latchonApp;
                }
            });
        }
    }

    function _dispatchNotification(title, body, params, handler) {
        debug(' > dispatching notification for ' + title + ' : ' +
                body + ' : ' + JSON.stringify(params));
        navigator.mozApps.getSelf().onsuccess = function (evt) {
            var app = evt.target.result;
            var iconURL = app.installOrigin + app.manifest.icons['60'];

            // With the notifications API used, we need to persist paramters
            // if the application is killed.
            // The only way of doing this persistence is adding those parameters
            // as GET parameters in the icon URL, so later we can parse that
            // url and extract them
            iconURL += '?';
            var paramsArray = [];
            for (var paramName in params) {
                paramsArray.push(paramName + '=' + params[paramName]);
            }
            iconURL += paramsArray.join('&');

            params.icon = iconURL;

            var options = {
                icon: iconURL,
                body: body
            };
            var notification = new Notification(title, options);
            notification.addEventListener('click', function () {
                app.launch();
                handler();
            });
        };
    }

    /**
     *  Object with all the functions/vars to be exposed globally.
     */
    var Controller = {
        /**
         *  When all resources are loaded, we call `init` for checking
         *  if the user was logged in or not, and render the UI properly.
         */
        init: function () {
            Proxy.init(function cookieRetrieved(cookie) {
                // Udpate helpers
                _isInitialized = true;
                _isAuthenticated = !!cookie;
                debug('Is authenticated? > ' + _isAuthenticated);
                // Init the UI manager
                UIManager.init(_isAuthenticated);
            });
        },
        get isInitialized() {
            return _isInitialized;
        },
        /**
         *  From the startup panel, method which shows the login form
         */
        signin: function () {
            UIManager.load('signin');
        },
        /**
         *  Retrieve user/pass from the UI and request the server. If the user/pass
         *  were right, we will render the list of apps.
         */
        authenticate: function (credentials, callback) {
            debug('Controller.authenticate');
            Proxy.authenticate(
                    credentials.user,
                    credentials.password,
                    function (err, data) {
                        if (err) {
                            callback(err);
                            return;
                        }
                        _isAuthenticated = true;
                        callback(null, data);
                    }
            );
        },
        /**
         *  Retrieve user's last login from local storage
         */
        getLastLogin: function (callback) {
            Proxy.getLastLogin(callback);
        },
        /**
         *  Retrieve user's email from local storage
         */
        getUserEmail: function (callback) {
            Proxy.getUserEmail(callback);
        },
        
        getOrangeRibbonClosed: function(callback){
            Proxy.getOrangeRibbonClosed(callback);
        },
        
        setOrangeRibbonClosed: function(value){
            Proxy.setOrangeRibbonClosed(value);
        },
        /**
         *  Logout from the app.
         */
        logout: function () {
            debug('Controller.logout');
            _isAuthenticated = false;
            Proxy.logout(function () {
                NotificationController.reset();
                // Register again, to be ready for next login
                NotificationController.init();
            });
        },
        /**
         *  Basic tutorial (the one shown in the startup panel) handler
         */
        basicTutorial: {
            show: function () {
                UIManager.load('basic-tutorial');
            },
            hide: function () {
                if (!_isAuthenticated) {
                    UIManager.load('startup');
                } else {
                    UIManager.load('main');
                }
            }
        },
        updateStatus: function (opID, status, callback) {
            debug('Controller > Setting status of ' + opID + ' = ' + status);
            if (!this.checkOnline()) {
                callback(ErrorManager.errorList.offline);
                return;
            }
            Proxy.updateStatus(opID, status, callback);
        },
        updateSchedulingRange: function (opID, from, to, callback) {
            debug('Controller > Setting scheduling range of ' + opID + ': from = ' + from + ', to = ' + to);
            if (!this.checkOnline()) {
                callback(ErrorManager.errorList.offline);
                return;
            }
            Proxy.updateSchedulingRange(opID, from, to, callback);
        },
        updateTwoFactor: function (opID, twoFactor, callback) {
            debug('Controller > Setting two factor of ' + opID + ' = ' + twoFactor);
            if (!this.checkOnline()) {
                callback(ErrorManager.errorList.offline);
                return;
            }
            Proxy.updateTwoFactor(opID, twoFactor, callback);
        },
        updateAutoClose: function (opID, autoClose, callback) {
            debug('Controller > Setting auto close of ' + opID + ' = ' + autoClose);
            if (!this.checkOnline()) {
                callback(ErrorManager.errorList.offline);
                return;
            }
            Proxy.updateAutoClose(opID, autoClose, callback);
        },
        updateAutoCloseByUse: function (opID, autoCloseByUse, callback) {
            debug('Controller > Setting auto close by use of ' + opID + ' = ' + autoCloseByUse);
            if (!this.checkOnline()) {
                callback(ErrorManager.errorList.offline);
                return;
            }
            Proxy.updateAutoCloseByUse(opID, autoCloseByUse, callback);
        },
        updateName: function (opID, name, callback) {
            name = name.trim();
            debug('Controller > Setting name of ' + opID + ' = ' + name);
            if (!this.checkOnline()) {
                callback(ErrorManager.errorList.offline);
                return;
            }
            Proxy.updateName(opID, name, callback);
        },
        updateDetailsWithMutualExclusion: function (id, autoclose, autocloseByUse, status, callback) {
            if (!this.checkOnline()) {
                callback(ErrorManager.errorList.offline);
                return;
            }

            var updates = [];
            if (autoclose !== null) {
                updates.push({field: 'autoclose', value: autoclose});
            }
            if (autocloseByUse !== null) {
                updates.push({field: 'lock_on_request', value: autocloseByUse ? 'on' : 'off'});
            }
            if (status !== null) {
                updates.push({field: 'status', value: status});
            }

            Proxy.multiUpdate(id, updates, callback);
        },
        skipPush: function (opID, skipPush, callback) {
            debug('Controller > Setting skipPush of ' + opID + ' = ' + skipPush);

            Proxy.skipPush(opID, skipPush, function (err) {
                if (err) {
                    ErrorManager.checkError(err);
                    callback(err);
                }
            });
        },
        /**
         *  Get operation info
         *  @parameter appID Application/Operation ID
         *  @parameter callback To be called when action is done with response/error
         */
        getOperations: function (appID, callback, forceUpdate) {
            debug('Controller.getOperations of ' + appID);
            Proxy.getOperations(appID, function (err, operation) {
                if (err) {
                    debug('Error while retrieving ' + appID + ' from server');
                    ErrorManager.checkError(err);
                    return;
                }
//                debug('--- Controller received ' + JSON.stringify(operation));
                if (typeof callback === 'function') {
                    callback(operation);
                }
            }, forceUpdate);
        },
        /**
         * Get latchon stored in proxy
         */
        getLocalLatchon: function () {
            return Proxy.getLocalLatchon();
        },
        /**
         *  Get main list of operations
         *  @parameter callback To be called when action is done with response/error
         */
        getRootOperations: function (onOperationsRetrieved, onNoOperations) {
            debug('Controller.getRootOperations');
            Proxy.getOperations(null, function onServerRequest(error, data) {

                if (!ErrorManager.checkError(error)) {
                    debug('Error while retrieving apps from server: ' +
                            JSON.stringify(error));
                    return;
                }

                if (!data.operations || Object.keys(data.operations).length === 0) {
                    if (typeof onNoOperations === 'function') {
                        onNoOperations();
                    }
                    return;
                }

                if (typeof onOperationsRetrieved === 'function') {

                    onOperationsRetrieved(data.operations);
                }
            }, true);
        },
        /**
         * Get prefecences of the logged user from Proxy
         */
        getPreferences: function (callback) {
            Proxy.getPreferences(function (err, preferences) {
                if (err) {
                    ErrorManager.checkError(err);
                    return;
                }
                callback(err, preferences);
            });
        },
        createGroup: function (name, opId, callback) {
            Proxy.createGroup(name, opId, function (err, groupId) {
                if (err) {
                    ErrorManager.checkError(err);
                    return;
                }
                if (typeof callback === 'function') {
                    callback(groupId);
                }
            });
        },
        deleteGroup: function (groupId, callback) {
            Proxy.deleteGroup(groupId, function (err) {
                if (err) {
                    ErrorManager.checkError(err);
                    return;
                }

                if (typeof callback === 'function') {
                    callback();
                }
            });
        },
        getLog: function (from, to, opId, callback) {

            function translateToLocal(logEntry) {
                var action = logEntry.action;
                switch (action) {
                    case 'USER_UPDATE':
                        if (logEntry.value && logEntry.what && logEntry.what === 'status') {
                            if (logEntry.value === 'off') {
                                return 'locked';
                            }
                            else if (logEntry.value === 'on') {
                                return 'unlocked';
                            }
                        }
                        break;
                    case 'STATUS_REQUEST':
                        if (logEntry.value) {
                            if (logEntry.value === 'on') {
                                return 'accessed';
                            }
                            else if (logEntry.value === 'off') {
                                return 'attempt';
                            }
                        }
                        break;
                    case 'LATCH_DISABLED':
                        return 'disabledByProvider';
                    case 'LATCH_ENABLED':
                        return 'restored';
                    case 'DEVELOPER_UPDATE':
                        return 'modified';
                    default :
                        debug('The type of log entry received"' + action + '" is not supported');
                        return;
                }
                debug('The type of log entry received is supported but its parameters could not be understood');
            }

            function splitDateAndTime(milliseconds) {
                var dateObject = new Date(milliseconds);

                var date = Utils.dateToDayString(dateObject, '-');
                var time = Utils.dateToHourString(dateObject, ':');

                return {'date': date, 'time': time};
            }

            function filterLog(entries, filter) {

                if (!filter) {
                    filter = Controller.getLogEntryTypes();
                }


                debug('Filter stored in database: ' + filter);
                for (var i = 0; i < entries.length; i++) {
                    var currentEntry = entries[i];

                    var typeOfEntry = translateToLocal(currentEntry);
                    currentEntry.type = typeOfEntry;
                    currentEntry.dateTime = splitDateAndTime(currentEntry.t);

                    // Delete attributes no longer needed
                    delete currentEntry.t;
                    delete currentEntry.value;
                    delete currentEntry.what;
                    delete currentEntry.action;

                    currentEntry.hide = filter.indexOf(typeOfEntry) === -1;
                }
            }


            Proxy.retrieveLogFilter(function (filter) {
                var err = null;

                Proxy.getLogEntries(from, to, opId, function (err, logEntries) {

                    if (err) {
                        ErrorManager.checkError(err);
                        return;
                    }

                    debug('Ya hemos recibido los datos: ' + JSON.stringify(logEntries));

                    filterLog(logEntries.history, filter);
                    callback(err, logEntries);
                });

            });


        },
        getLogEntryTypes: function () {
            var logEntryTypes = [
                'locked',
                'unlocked',
                'accessed',
                'attempt',
                'disabledByProvider',
                'restored',
                'modified'
            ];
            return logEntryTypes;
        },
        storeLogFilter: function (filter) {
            Proxy.storeLogFilter(filter);
        },
        retrieveLogFilter: function (callback) {

            Proxy.retrieveLogFilter(function (filter) {
                if (!filter) {
                    filter = Controller.getLogEntryTypes();
                }
                callback(filter);
            });
        },
        /**
         *  Get pairing code
         *  @parameter callback To be called when action is done with response/error
         */
        getCode: function (callback) {
            if (!this.checkOnline()) {
                return;
            }
            var _ = navigator.mozL10n.get;
            LoadingOverlay.show(_('generatingCodeOverlay'));
            Proxy.getPairingToken(function (error, token) {
                debug('> Controller > Pairing > ');
                debug('> error: ' + JSON.stringify(error));
                debug('> token: ' + JSON.stringify(token));
                LoadingOverlay.hide();

                if (typeof callback === 'function') {
                    callback(error, token);
                } else {
                    console.error('Missing callback on function');
                }
            });
        },
        /**
         *  Load the parent from the tree. If there is no parent, we load the
         *  main/root list of operations
         *  @parameter callback To be called when action is done with response/error
         */
        loadParentOperation: function (onLoadOperation, onLoadGroup) {
            debug('Controller.loadParentOperation');
            var parentOperationID = _currentOperation.parentId;
            if (!parentOperationID) {

                if (_currentOperation && _currentOperation.groupId !== undefined) {
                    onLoadGroup(_currentOperation.groupId);
                    _currentOperation = null;
                    return;
                }

                _currentOperation = null;
                onLoadOperation(true);
                return;
            }
            onLoadOperation(null, parentOperationID);
        },
        addAppToGroup: function (appId, groupId) {
            Proxy.addAppToGroup(appId, groupId, function (err) {
                if (!ErrorManager.checkError(err)) {
                    return;
                }
            });
        },
        removeAppFromGroup: function (appId, groupId) {
            Proxy.removeAppFromGroup(appId, groupId, function (err) {
                if (!ErrorManager.checkError(err)) {
                    return;
                }

                debug('--Todo--');
            });
        },
        loadGroups: function (callback) {
            var forceUpdate = true;
            Proxy.getOperations(
                    null,
                    function (err, localList) {

                        if (!ErrorManager.checkError(err)) {
                            return;
                        }

                        callback(localList.tags);
                    },
                    forceUpdate);
        },
        loadGroupOperations: function (groupId, callback, forceUpdate) {

            debug('Controller > loadGroupOperations');

            Proxy.getOperations(
                    null,
                    function (err, localList) {

                        if (!ErrorManager.checkError(err)) {
                            return;
                        }

                        var opidsInGroup = localList.tags[groupId].apps;
                        var groupName = localList.tags[groupId].customName;

                        var appList = [];
                        for (var i = 0; i < opidsInGroup.length; i++) {
                            var currentAppId = opidsInGroup[i];
                            var currentAppObject = localList.operations[currentAppId];
                            appList[currentAppId] = currentAppObject;
                        }
                        callback(groupName, appList);
                    },
                    forceUpdate);
        },
        /**
         *  Load operations given a parent ID
         *  @parameter opID To be called when action is done with response/error
         *  @parameter callback To be called when action is done with response/error
         */
        loadOperations: function (opID, onOperation, onDetail, forceUpdate) {
            debug('loadOperations with ' + opID);
            this.getOperations(
                    opID,
                    function (operation) {
                        debug('loadOperations ' + opID);

                        var hasOperations = operation.operations && Object.getOwnPropertyNames(operation.operations).length > 0;
                        if (!hasOperations && typeof onDetail === 'function') {
                            debug('Last level of operations');
                            _currentOperation = operation;
                            _currentOperation.id = opID;
                            onDetail(null, operation, opID);
                            return;
                        }

                        if (typeof onOperation === 'function') {
                            _currentOperation = operation;
                            _currentOperation.id = opID;
                            onOperation(null, operation);
                        }
                    }, forceUpdate);
        },
        loadOTP: function (opId) {
            debug(' > loadOTP ' + opId);
            var _ = navigator.mozL10n.get;
            this.getOperations(opId, function onOp(op) {
                if (!Controller.checkOnline()) {
                    return;
                }
                Proxy.getSecondFactorAuth(opId, function onOTP(err, response) {
                    if (err) {
                        Status.show(_('otp-no-available'));
                        debug(' > loadOTP could not ask for otp ' + err);
                        ErrorManager.checkError(err);
                        return;
                    }

                    if (response && response.token) {
                        var name = Utils.chooseNameIncludingGlobalParent(opId, op);
                        OTPOverlay.show(name, op.globalParent.imageURL, response.token, response.notificationMessage);
                    } else {
                        Status.show(_('otp-no-available'));
                        debug(' > loadOTP wrong token format ' + JSON.stringify(response));
                    }
                });
            });
        },
        dispatchOTPNotification: function (opId) {
            debug(' > distpatch OTP');
            if (!_isAppVisible()) {
                _dispatchNotification('Latch', navigator.mozL10n.get('otpInfo'), {
                    action: 'otp',
                    opId: opId
                }, function () {
                    this.loadOTP(opId);
                }.bind(this));
            } else {
                this.loadOTP(opId);
            }
        },
        /**
         * Looks for the new operation in the local list and
         * Loads an overlay warning the user of a new paired application
         * The user can choose between setting the new operation right away
         * or later in time
         * @parameter opId The identifier of the new operation
         */
        loadNewOperationPaired: function (opId, operation) {
            debug('> loading new operation paired overlay');
            debug('> new op paired is: ' + opId);
            AppPairedOverlay.show(
                    operation.name,
                    operation.imageURL,
                    function configureNow() {
                        UIManager.updateOperation(opId);
                    },
                    function configureLater() {
                        UIManager.load('main');
                    }
            );
            UIManager.renderAllOperations();
        },
        loadNewOperationPairedDisabled: function (opId, operation) {
            debug('> loading new operation paired-disabled overlay');
            debug('> new op paired and disabled is: ' + operation.id);
            var globalParentOp = operation.globalParent;

            AppPairedDisabledOverlay.show(
                globalParentOp.name, 
                globalParentOp.imageURL,
                function configureLater() {
                    UIManager.load('main');
                },
                globalParentOp.contactPhone || operation.contactPhone || '',
                globalParentOp.contactEmail || operation.contactEmail || ''
            );
            UIManager.renderAllOperations();
        },
        loadNewOperationUnpaired: function (opId, operation) {
            debug('> loading new operation unpaired overlay');
            debug('> op unpaired is: ' + opId);

            AppUnpairedOverlay.show(
                    operation.name,
                    operation.imageURL,
                    function () {
                        UIManager.load('main');
                    }
            );
            UIManager.renderAllOperations();
        },
        /**
         * When we receive a notification of update, we need to get the new data
         * from the server side and compare it to the local data (done in Proxy).
         */
        dispatchUpdatedOperations: function () {
            debug(' > dispatch updated operations');
            // this will trigger the comparision of new data
            Proxy.getOperations(null, null, true);
            if (_isAppVisible()) {
                var opId = UIManager.getCurrentOperationId();
                if (opId) {
                    if (opId === 'main') {
                        UIManager.renderAllOperations();
                    }
                    else {
                        UIManager.updateOperation(opId);
                    }
                }
            }
        },
        dispatchPairedOperation: function (opId) {
            debug(' > dispatch updated operations');
            // this will trigger the comparision of new data
            Proxy.getOperations(null, null, true);
            if (!_isAppVisible()) {
                var _ = navigator.mozL10n.get;
                _dispatchNotification(_('newOperationPaired'), "", {
                    action: 'applicationPaired',
                    opId: opId
                }, function () {
                    this.showAppPaired(opId);
                }.bind(this));
            } else {
                this.showAppPaired(opId);
            }
        },
        dispatchUnpairedOperation: function (opId) {
            debug(' > dispatch unpaired operations');
            if (!_isAppVisible()) {
                var _ = navigator.mozL10n.get;
                _dispatchNotification(_('serviceUnpaired'), "", {
                    action: 'applicationUnpaired',
                    opId: opId
                }, function () {
                    this.showAppUnpaired(opId);
                }.bind(this));
            } else {
                this.showAppUnpaired(opId);
            }
        },
        dispatchOperationBlocked: function (opId) {
            debug(' > dispatch operation blocked');
            if (!_isAppVisible()) {
                var _ = navigator.mozL10n.get;
                _dispatchNotification(_('opBlockTitle'), _('opBlockBody'), {
                    action: 'operationBlocked',
                    opId: opId
                }, function () {
                    this.showOperationBlocked(opId);
                }.bind(this));
            } else {
                this.showOperationBlocked(opId);
            }
        },
        dispatchOperationChanged: function (opId) {
            debug(' > dispatch operation changed');
            if (!_isAppVisible()) {
                var _ = navigator.mozL10n.get;
                _dispatchNotification(_('changedNotification'), _('changedOperationInfo'), {
                    action: 'operationBlocked',
                    opId: opId
                }, function () {
                    this.showOperationChanged(opId);
                }.bind(this));
            } else {
                this.showOperationChanged(opId);
                UIManager.renderAllOperations();
            }
        },
        dispatchOperationAccessed: function (opId) {
            debug(' > dispatch operation accessed');
            if (!_isAppVisible()) {
                var _ = navigator.mozL10n.get;
                _dispatchNotification(_('opAccessedTitle'), _('opAccessedBody'), {
                    action: 'operationAccessed',
                    opId: opId
                }, function () {
                    this.showOperationAccessed(opId);
                }.bind(this));
            } else {
                this.showOperationAccessed(opId);
            }
        },
        showOperationChanged: function (opId) {
            debug(' > show operation changed');
            var self = this;

            Proxy.getOperations(opId, function (err, operation) {
                if (err || !operation) {
                    debug(' > error getting operation ' + opId + ' : ' + err);
                    ErrorManager.checkError(err);
                    return;
                }
                
                var globalParentOp = operation.globalParent;

                ChangedOverlay.show(
                        globalParentOp.imageURL,
                        Utils.chooseNameIncludingGlobalParent(opId, operation),
                        globalParentOp.contactPhone || '',
                        globalParentOp.contactEmail || '');
            },
                    true);
        },
        showOperationBlocked: function (opId) {
            debug(' > show operation blocked');
            // Fetch everything again from the server, to ensure
            // we have a fresh copy in memory
            Proxy.getOperations(null, function (err, operations) {
                if (err || !operations) {
                    debug(' > cannot get all operations');
                    ErrorManager.checkError(err);
                    return;
                }
                // Fetch the operation blocked
                Proxy.getOperations(opId, function (err, operation) {
                    if (err || !operation) {
                        debug(' > error getting operation ' + opId + ' : ' + err);
                        ErrorManager.checkError(err);
                        return;
                    }

                    var globalParentOp = operation.globalParent;
                    var globalParentId = operation.globalParentId;

                    // Name of the global operaiton, icon, name of operation blocked,
                    // onUnlock callback, phone to call for more info
                    BlockedOverlay.show(
                            globalParentOp.name, 
                            globalParentOp.imageURL,
                            Utils.chooseNameIncludingGlobalParent(opId, operation),
                            function onUnlock(opId) {

                                Controller.getOperations(opId, function (blockedOperation) {

                                    function unlock(opId, callback) {
                                        Proxy.updateStatus(opId, 'on',
                                                function (err) {
                                                    if (err) {
                                                        return;
                                                    }
                                                    else if (typeof callback === 'function') {
                                                        callback();
                                                    }
                                                });
                                    }

                                    var lockedAncestors = new Object();
                                    _getLockedAncestors(blockedOperation, lockedAncestors);

                                    var thereAreLockedAncestors = Object.keys(lockedAncestors).length > 0;
                                    if (thereAreLockedAncestors) {

                                        UIManager.showLockedAncestorsDialog(
                                                lockedAncestors,
                                                function onUnlockAncestors() {
                                                    debug('Unlocking ancestors');
                                                    Object.keys(lockedAncestors).forEach(function (ancestorOpId) {
                                                        unlock(ancestorOpId);
                                                    });
                                                    unlock(opId);
                                                    unlock(Config.environment.latchonID, function () {
                                                        BlockedOverlay.hide();
                                                        if (globalParentId === opId) {
                                                            UIManager.renderAllOperations();
                                                        } else {
                                                            UIManager.updateOperation(opId);
                                                        }
                                                    });
                                                }
                                        );
                                        return;
                                    }
                                    else {
                                        unlock(opId, function () {
                                            BlockedOverlay.hide();
                                            if (globalParentId === opId) {
                                                UIManager.renderAllOperations();
                                            } else {
                                                UIManager.updateOperation(opId);
                                            }
                                        });
                                    }
                                });
                            },
                            globalParentOp.contactPhone || operation.contactPhone || '',
                            globalParentOp.contactEmail || operation.contactEmail || '',
                            opId);
                });
            },
                    true);
        },
        showOperationAccessed: function (opId) {
            debug(' > show operation accessed');
            var self = this;
            // Fetch everything again from the server, to ensure
            // we have a fresh copy in memory
            Proxy.getOperations(null, function (err, operations) {
                if (err || !operations) {
                    debug(' > cannot get all operations');
                    ErrorManager.checkError(err);
                    return;
                }
                // Fetch the operation accessed
                Proxy.getOperations(opId, function (err, operation) {
                    if (err || !operation) {
                        debug(' > error getting operation ' + opId + ' : ' + err);
                        ErrorManager.checkError(err);
                        return;
                    }

                    var globalParentOp = operation.globalParent;
                    var globalParentId = operation.globalParentId;
                    // Name of the global operaiton, icon, name of operation blocked,
                    // onUnlock callback, phone to call for more info
                    AccessedOverlay.show(globalParentOp.name, globalParentOp.imageURL,
                            Utils.chooseNameIncludingGlobalParent(opId, operation),
                            function onLock(cb) {
                                Proxy.updateStatus(opId, 'off',
                                    function (err) {
                                        // If there is an error, just hide the overlay
                                        if (err) {
                                            if (typeof cb === 'function') {
                                                cb(err); // Usually this callback is to hide
                                            }
                                            return;
                                        }
                                        if (operation.globalParent === null) {
                                            // Update all services list
                                            UIManager.renderAllOperations();
                                        } else {
                                            // Update parent view, to show changes on child
                                            UIManager.updateOperation(opId);
                                        }
                                        if (typeof cb === 'function') {
                                            cb(); 
                                        }
                                        return;                      
                                    });
                            },
                            globalParentOp.contactPhone || operation.contactPhone || '',
                            globalParentOp.contactEmail || operation.contactEmail || '');
                });
            },
                    true);
        },
        showAppPaired: function (opId) {
            debug(' > show operation paired');
            var self = this;
            // Fetch everything again from the server, to ensure
            // we have a fresh copy in memory
            Proxy.getOperations(null, function (err, operations) {
                if (err || !operations) {
                    debug(' > cannot get all operations');
                    ErrorManager.checkError(err);
                    return;
                }
                // Fetch the operation paired
                Proxy.getOperations(opId, function (err, operation) {
                    if (err || !operation) {
                        debug(' > error getting operation ' + opId + ' : ' + err);
                        ErrorManager.checkError(err);
                        return;
                    }

                    if (operation.subscription && operation.subscription === 'disabled') {
                        Controller.loadNewOperationPairedDisabled(opId, operation);
                    }
                    else {
                        Controller.loadNewOperationPaired(opId, operation);
                    }
                });
            },
                    true);
        },
        dispatchOperationDisabled: function (opId) {
            debug(' > dispatch operation disabled');
            if (!_isAppVisible()) {
                var _ = navigator.mozL10n.get;
                _dispatchNotification(_('disabled-operation'), _('disabled-notification'), {
                    action: 'operationDisabled',
                    opId: opId
                }, function () {
                    this.showOperationDisabled(opId);
                }.bind(this));
            } else {
                this.showOperationDisabled(opId);
            }
        },
        showOperationDisabled: function (opId) {
            debug(' > show operation disabled');
            var self = this;

            Proxy.getOperations(opId, function (err, operation) {
                if (err || !operation) {
                    debug(' > error getting operation ' + opId + ' : ' + err);
                    ErrorManager.checkError(err);
                    return;
                }

                DisabledOverlay.show(
                        Utils.chooseNameIncludingGlobalParent(opId, operation),
                        operation.imageURL,
                        function () {
                            UIManager.renderAllOperations();
                            UIManager.load('main');
                        }.bind(self),
                        operation.contactPhone || '',
                        operation.contactEmail || '');
            },
                    true);
        },
        dispatchOperationEnabled: function (opId) {
            debug(' > dispatch operation enabled');
            if (!_isAppVisible()) {
                var _ = navigator.mozL10n.get;
                _dispatchNotification(_('enabled-operation'), _('enabled-op-explanation'), {
                    action: 'operationEnabled',
                    opId: opId
                }, function () {
                    this.showOperationEnabled(opId);
                }.bind(this));
            } else {
                this.showOperationEnabled(opId);
            }
        },
        showOperationEnabled: function (opId) {
            debug(' > show operation enabled');
            var self = this;

            Proxy.getOperations(opId, function (err, operation) {
                if (err || !operation) {
                    debug(' > error getting operation ' + opId + ' : ' + err);
                    ErrorManager.checkError(err);
                    return;
                }

                EnabledOverlay.show(
                        Utils.chooseNameIncludingGlobalParent(opId, operation),
                        operation.imageURL,
                        function () {
                            UIManager.renderAllOperations();
                            UIManager.load('main');
                        }.bind(self));
            },
                    true);
        },
        dispatchNewSession: function (id) {
            debug(' > dispatch new session');
            if (!_isAppVisible()) {
                var _ = navigator.mozL10n.get;
                _dispatchNotification(_('new-session-header'), _('new-session-title'), {
                    action: 'newSession'
                }, function () {
                    this.showNewSession(id);
                }.bind(this));
            } else {
                this.showNewSession(id);
            }
        },
        showNewSession: function (id) {
            debug(' > show new session');
            var self = this;

            Proxy.getAuthDetails(id, function (err, device) {

                if (err || !device) {
                    debug(' > error getting new session info ' + id + ' : ' + err);
                    ErrorManager.checkError(err);
                    return;
                }

                NewSessionOverlay.show(
                        device,
                        function () {
                            NewSessionOverlay.hide();
                        }.bind(self));
            });



        },
        showAppUnpaired: function (opId) {
            debug(' > show operation unpaired');
            var self = this;
            // Fetch everything again from the server, to ensure
            // we have a fresh copy in memory
            Proxy.getOperations(null, function (err, operations) {
                if (err || !operations) {
                    debug(' > cannot get all operations');
                    ErrorManager.checkError(err);
                    return;
                }
                // Fetch the operation paired
                Proxy.getOperations(opId, function (err, operation) {
                    if (err || !operation) {
                        debug(' > error getting operation ' + opId + ' : ' + err);
                        ErrorManager.checkError(err);
                        return;
                    }
                    Controller.loadNewOperationUnpaired(opId, operation);
                    // Refresh local list of apps
                    Proxy.getOperations(null, null, true);
                });
            },
                    /**
                     * Local storage should not be refreshed until unpaired
                     * application is loaded
                     */
                    false);
        },
        /**
         * Changes the locked state of all the services shown on the current screen
         * @parameter status The new lock state
         * @parameter callback To be called when the change is done / error detected
         */
        updateLockAllOperations: function (status, callback) {
            if (!this.checkOnline()) {
                callback(ErrorManager.errorList.offline);
                return;
            }

            Proxy.updateLockAllOperations(status, _currentOperation, function (err) {
                if (!!err) {
                    console.error('ERROR WHILE UPDATING SERVER > ' + err.message);
                    callback(err);
                    return;
                }
            });
        },
        // Check if we are online, returns true or false,
        // if false, also displays the message passed in a toast
        checkOnline: function () {
            var online = navigator.checkOnline;
            if (!online) {
                Status.show(navigator.mozL10n.get('no_network'));
            }

            return online;
        },
        storePreferences: function (id, value) {

            Proxy.getPreferences(function (err, preferences) {

                preferences[id] = value;
                Proxy.storePreferences(preferences);
            });
        },
        updateOrder: function (opIdList) {
            Proxy.updateOrder(opIdList, function (error) {
                if (!ErrorManager.checkError(error)) {
                    return;
                }
            });
        }
    };

    exports.Controller = Controller;
}(this));
