var EXPORTED_SYMBOLS = ['AppCenter'];

const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyGetter(this, "utils", function() {
    let obj = { };
    Cu["import"]("resource://livemargins/utils.jsm", obj);
    return obj.utils;
});

XPCOMUtils.defineLazyGetter(this, "AppCenterDAO", function() {
    let obj = { };
    Cu["import"]("resource://livemargins/dao.jsm", obj);
    return obj.AppCenterDAO;
});

XPCOMUtils.defineLazyGetter(this, "builtInApps", function() {
    let obj = { };
    Cu["import"]("resource://livemargins/builtinapps.jsm", obj);
    return obj.builtInApps;
});

XPCOMUtils.defineLazyGetter(this, "Tracker", function() {
    let obj = { };
    Cu["import"]("resource://livemargins/track.jsm", obj);
    return obj.Tracker;
});

XPCOMUtils.defineLazyGetter(this, "windowMediator", function() {
    return Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
});

XPCOMUtils.defineLazyGetter(this, "logger", function() {
    return utils.logger('LM4.AppCenter');
});

// "bind" is not defined in Firefox3
if (!Function.prototype.bind) {
    Function.prototype.bind = function (context) {
        var __method = this, args = Array.prototype.slice.call(arguments, 1);
        return function() {
            return __method.apply(context, args.concat(Array.prototype.slice.call(arguments, 0)));
        }
    };
}

var appHandlers = [];
var webClipAppHandler = {
    appURL: 'chrome://livemargins/content/apps/webclipping/app.xul',
    onBeforeOpen: function(app) {
        if (!app.app_args || !app.app_args.location) {
            return false;
        }
        
        return true;
    }
};

var flashClipAppHandler = {
    appURL: 'chrome://livemargins/content/apps/flashextractor/app.xul',
    onBeforeOpen: function(app) {
        if (!app.app_args || !app.app_args.location) {
            return false;
        }
        
        return true;
    }
};

var webAppHandler = {
    appURL: 'chrome://livemargins/content/apps/webapp/app.xul',
    onBeforeOpen: function(app) {
        if (!app.app_args || !app.app_args.location) {
            return false;
        }
        
        return true;
    }
};

var shortcutAppHandler = {
    onBeforeOpen: function(app) {
        if (!app.app_args || !app.app_args.location) {
            return false;
        }
        
        return true;
    },
    
    _findAndSwitchToAppTab: function(app) {
        function _isTabForApp(browser, tab, app) {
            return tab.appUID == app.uid ||
                        browser.currentURI.specIgnoringRef == app.app_args.location;
        }
        
        function _findAndLoadAppTab(wnd,app) {
            var tabs = wnd.gBrowser.tabs;
            for (var i = 0; i < tabs.length; i++) {
                // make sure app tab is reused after restarting firefox.
                if (_isTabForApp(wnd.gBrowser.browsers[i], tabs[i], app)) {
                    tabs[i].appUID = app.uid;
                    // switch to app tab.
                    wnd.gBrowser.tabContainer.selectedIndex = i;
                    wnd.focus();
                    
                    // reload app if url is changed.
                    if (wnd.gBrowser.browsers[i].currentURI.specIgnoringRef != app.app_args.location) {
                        wnd.gBrowser.browsers[i].loadURI(app.app_args.location);
                    }
                    return tabs[i];
                }
            }
            return null;
        }
        
        var tab = null;
        var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
        var winEnum = windowMediator.getEnumerator("navigator:browser");
        while (winEnum.hasMoreElements()) {
            var browserWin = winEnum.getNext();
            if (browserWin.closed) {
                continue;
            }
        
            tab = _findAndLoadAppTab(browserWin, app);
            if (tab) {
                break;
            }
        }
        
        return tab;
    },
    
    open: function(app) {
        // open link directly.
        try {
        var tab = this._findAndSwitchToAppTab(app);
        if (tab) {
            return;
        }
        
        var wnd = findWindow();
        tab = wnd.gBrowser.loadOneTab(app.app_args.location, {inBackground: false});
        tab.appUID = app.uid;
        // only pin tab after firefox4
//        if (wnd.gBrowser.pinTab) {
//            wnd.gBrowser.pinTab(tab);
//        }
        } catch (e) {
            logger.trace("open shortcut: " + e);
        }
    }
};

function findWindow() {
    var wnd = windowMediator.getMostRecentWindow('navigator:browser');
    if (!wnd)
        wnd = windowMediator.getMostRecentWindow('');
    return wnd;
}

var faviconService = Cc['@mozilla.org/browser/favicon-service;1'].getService(Ci.nsIFaviconService);
var AppCenter = {
    TYPE: {
        'BUILT_IN': 0,
        'WEB_CLIPPING': 1,
        'WEB_APP': 2,
        'FLASH_CLIPPING': 3,
        'SHORTCUT': 4
    },
    
    // Use 5.1 as the initial version which will be updated automatically. 
    version: "5.1",
    
    addWebClipping: function(args) {
        var uuidGenerator = Components.classes["@mozilla.org/uuid-generator;1"]
                    .getService(Components.interfaces.nsIUUIDGenerator);
        var uuid = uuidGenerator.generateUUID();
        var uuidString = uuid.toString();
        
        var icon = faviconService.getFaviconImageForPage(utils.getNsiURL(args.location)).spec;
        // Because resource page have no priority to access moz-anno:favicon
        // strip "moz-anno:favicon" here
        icon = icon.replace(/moz-anno:favicon:/i, "");
        // Install App
        this.installApp({
            appUID: uuidString,
            appName: args.name,
            iconUrl: icon == 'chrome://mozapps/skin/places/defaultFavicon.png' ? '' : icon,
            appArgs: {
                location     : args.location,
                docX         : args.docX,
                docY         : args.docY,
                width        : args.width,
                height       : args.height,
                browserWidth : args.browserWidth,
                browserHeight: args.browserHeight,
                zoom         : args.zoom
            },
            type: this.TYPE.WEB_CLIPPING,
            is_local: 1
        });
        
        // Update info about web clipping app, including: screenX and screenY.
        this.updateAppStateWithArg({
            uid:   uuidString,
            screen_x:  args.screenX,
            screen_y:  args.screenY,
            thumbnail: null
        });
                
        return uuidString;
    },
    
    addVideoPlayer: function(args) {
        var uuidGenerator = Components.classes["@mozilla.org/uuid-generator;1"]
                    .getService(Components.interfaces.nsIUUIDGenerator);
        var uuid = uuidGenerator.generateUUID();
        var uuidString = uuid.toString();
        
        var icon = faviconService.getFaviconImageForPage(utils.getNsiURL(args.location)).spec;
        // Because resource page have no priority to access moz-anno:favicon
        // strip "moz-anno:favicon" here
        icon = icon.replace(/moz-anno:favicon:/i, "");
        // Install App
        this.installApp({
            appUID: uuidString,
            appName: args.name,
            iconUrl: icon == 'chrome://mozapps/skin/places/defaultFavicon.png' ? '' : icon,
            appArgs: {
                location     : args.location,
                docX         : args.docX,
                docY         : args.docY,
                width        : args.width,
                height       : args.height,
                browserWidth : args.browserWidth,
                browserHeight: args.browserHeight,
                zoom         : args.zoom,
                tagName      : args.tagName,
                elementIndex : args.elementIndex,
                elementHeight: args.elementHeight,
                elementWidth : args.elementWidth,
            },
            type: this.TYPE.FLASH_CLIPPING,
            is_local: 1
        });
        
        // Update info about web clipping app, including: screenX and screenY.
        this.updateAppStateWithArg({
            uid:   uuidString,
            screen_x:  args.screenX,
            screen_y:  args.screenY,
            thumbnail: null
        });
        
        return uuidString;
    },
    
    bookmarkApp: function(uid, bookmark) {
        AppCenterDAO.updateAppBookmark(uid, bookmark === true ? 1 : 0);
        if (true == bookmark) {
            AppCenter.appPref.updatePref('bookmark', uid);
            Tracker.track(Tracker.app.type, Tracker.app.actions.BOOKMARK_APP, uid);
        } else {
            AppCenter.appPref.updatePref('unbookmark', uid);    
            Tracker.track(Tracker.app.type, Tracker.app.actions.REMOVE_APP, uid);
        }
    },
    
    checkLocal: function(uid, isLocal) {
        AppCenterDAO.updateAppLocal(uid, isLocal == true ? 1 : 0);
        if (true == isLocal) {
            AppCenter.appPref.updatePref('local', uid);    
        } else {
            AppCenter.appPref.updatePref('unlocal', uid);
        }
    },
    
    installAndBookmarkApp: function(appInfo) {
        return this.installApp(appInfo, true);
    },
        
    installApp: function(appInfo, bookmark) {
        // Check if app is built-in
        // If true, get app info from built-in apps module.
        if (appInfo.type == this.TYPE.BUILT_IN) {
            if (!builtInApps[appInfo.appUID]) {
                throw new Error('Built-in app does not exist.');
            }
            
            appInfo = builtInApps[appInfo.appUID];
        }
        
        appInfo = utils.extend({
            appUID:  null,
            appName: null,
            iconUrl: null,
            appArgs: null,
            description: null,
            bookmark: bookmark === true ? 1 : 0,
            type: this.TYPE.WEB_APP,
            is_local: 0,
            smallIcon: null
        }, appInfo);
        
        if (!appInfo.appUID || !appInfo.appName) {
            throw new Error('Install app: Some arguments are missing.' + JSON.stringify(appInfo));
        }
        
        if (AppCenterDAO.appExists(appInfo.appUID)) {
            throw new Error('"' + appInfo.appUID + '" has been installed.');
        }
        
        return AppCenterDAO.addApp(appInfo);
    },
    
    removeApp: function(appUID) {
        if (!AppCenterDAO.appExists(appUID)) {
            return;
        }
        
        logger.trace('Remove app: ' + appUID);
        AppCenterDAO.deleteAppByUID(appUID);
        
        // close window        
        var appWindow = this.findAppWindow(appUID);
        if (appWindow){
            appWindow.close();
        }
        
        // TODO: unregister handler
    },
    
    registerHandler: function(appUID, handler) {
        handler = utils.extend({
            appURL: null,
            onload: utils.emptyFunction,
            onBeforeOpen: utils.emptyFunction
        }, handler);
        
        if (!handler.appURL) {
            throw new Error('Handler registering: Some arguments are missing.');    
        }
        
        appHandlers[appUID] = handler;
        
        logger.trace('Handler for "' + appUID + '" has been registered.\n');
        
        return true;
    },
    
    appHandlerExists: function(appUID) {
        return !!appHandlers[appUID];
    },
    
    findAppWindow: function(appUID) {
        var enumerator = windowMediator.getEnumerator("mozilla:favpart_widget");  
        while(enumerator.hasMoreElements()) {  
            var win = enumerator.getNext();
            if (win.uid == appUID){
                return win;
            }
        }
        
        return null;
    },
    
    getAllOpenedApp: function() {
        var enumerator = windowMediator.getEnumerator("mozilla:favpart_widget");
        
        var apps = [];
        while(enumerator.hasMoreElements()) {
            apps.push(enumerator.getNext().uid);
        }
        
        return apps;
    },
    
    getOpenedAppCount: function() {
        var enumerator = windowMediator.getEnumerator("mozilla:favpart_widget");
        
        var count = 0;
        while(enumerator.hasMoreElements()) {
            enumerator.getNext();
            count++;
        }
        return count;
    },
    
    appExists: function(appUID) {
        return AppCenterDAO.appExists(appUID);    
    },
    
    showApp: function(appUID) {
        var appWindow = this.findAppWindow(appUID);
        if (appWindow){
            appWindow.focus();
            return true;
        }
        
        return false;
    },
    
    checkAutoStart: function() {
        var apps = AppCenterDAO.getAppsAutoStart();    
        if (apps.length == 0) {
            return;
        }
        
        AppCenterDAO.clearAppAutoStart();
        var self = this;
        apps.forEach(function(app) {
            logger.trace("Open app: " + app.uid);
            self.openApp(app.uid);
        });
    },
    
    /**
     * Check if app exists, if not, fetch the JSON object of the app, install and open.
     * If the app exists, Open it directly.
     */
    installAndOpenApp: function(appUID) {
        if (AppCenterDAO.appExists(appUID)) {
            this.openApp(appUID);
            return;
        }
        
        if (builtInApps[appUID]) {
            logger.trace("Get app " + appUID + " in the built-in apps module.");
            this.installApp(builtInApps[appUID]);
            this.openApp(appUID);
            return;
        }
        
        logger.trace("App " + appUID + " does not exists, fetch from server.");
        let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
        xhr.mozBackgroundRequest = true;
        xhr.open("GET", utils.getPref("appinfourl") + appUID + "/");
        xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS;
        
        var self = this;
        xhr.onerror = function(req) {
            logger.info("Can not get object for app, see: " + req);
        };
        
        xhr.onload = function(req) {
            try {
                let response = JSON.parse(req.target.responseText);
                if (response.status == 200) {
                   self.installApp(response.app);
                   self.openApp(appUID);
                } else {
                    logger.info("Can not get json object for app " + appUID + " see: " + response.message);
                }
            } catch (e) {
                logger.info("Error occurs when parsing app object, see: " + e);
            }
            
        };
        
        xhr.send();
    },
    
    openApp: function(appUID, updateOpenedTime, extraWidgetArgs) {
        if (this.showApp(appUID)) {
            return;
        }
        
        var app = AppCenterDAO.getAppByUID(appUID);
        
        if (!app) {
            logger.trace('App "' + appUID + '" does not exist.');
            return;
        } else {
            try {
                // parse JSON data from app_args
                app.app_args = JSON.parse(app.app_args);
            } catch (e) {
                app.app_args = { };
            }
        }
        
        // Check if it is an web clipping app.
        var handler = null;
        
        switch (app.type) {
        case this.TYPE.WEB_CLIPPING:
            handler = webClipAppHandler;
            break;
        case this.TYPE.FLASH_CLIPPING:
            handler = flashClipAppHandler;
            break;
        case this.TYPE.WEB_APP:
            handler = webAppHandler;
            break;
        case this.TYPE.SHORTCUT:
            handler = shortcutAppHandler;
            break;
        }
        
        handler = !handler ? appHandlers[appUID] : handler;
        if (!handler) {
            logger.info('Handler for app: "' + appUID + '" does not exist.')
            return;
        }
        
        if (false == handler.onBeforeOpen(app)) {
            logger.info('onBeforeOpen checking failed for app "' + appUID + '"');
            return;
        }
        
        // if handler defines open action, then invoke it instead.
        if (typeof handler.open == 'function') {
            handler.open(app);
            return;
        }
        
        var appInfo = AppCenterDAO.getAppInfoById(app.id);
        
        var POS_NOT_SET = 9999;
        appInfo = !appInfo ? { 
            screen_x: POS_NOT_SET,
            screen_y: POS_NOT_SET
        } : appInfo;
        
        var args = utils.extend({
            id: appUID,
            name: app.name,
            bookmark: app.bookmark,
            type: app.type,
            smallIcon: this.getAppSmallIcon(app)
        }, handler.args);
        
        // pass screen pos
        args["screenX"] = appInfo.screen_x;
        args["screenY"] = appInfo.screen_y;
        
        // set widgetArgs
        if (extraWidgetArgs) {
            args['widgetArgs'] = utils.extend({}, extraWidgetArgs);
        } else {
            args['widgetArgs'] = app.app_args;
        }
        
        // restore the app arguments from appInfo, including width, height etc.
        if (appInfo.app_args) {
            try {
                utils.extend(args['widgetArgs'], JSON.parse(appInfo.app_args));
            } catch (e) {
                logger.error(e);
            }
        }
        
        // restore the app windowStates from app_status
        if (appInfo.app_status) {
            try {
                args["windowStates"] = utils.extend({}, JSON.parse(appInfo.app_status));
            } catch (e) {
                logger.error(e);
            }
        }
        
        var wnd = findWindow();
        
        // Calculate position of APP window.
        var winFeatures = ['chrome,all,dialog=no'];
        if (wnd.navigator.appVersion.indexOf('Mac') > -1 && appInfo.screen_x == POS_NOT_SET) {
            // Mac will not paint window out of screen
            winFeatures.push('left=' + Math.max(wnd.screen.availWidth - 300, 0));
            winFeatures.push('top=' + Math.max(wnd.screen.availHeight - 400, 0)); 
        } else {
            winFeatures.push('left=' + appInfo.screen_x);
            winFeatures.push('top=' + appInfo.screen_y); 
        }
        
        var appWindow = wnd.openDialog(handler.appURL, appUID, winFeatures.join(','), args);
        appWindow.uid = appUID;
        
        if (false != updateOpenedTime) {
            this.updateAppStateWithArg({
                uid: appUID,
                last_opened: new Date().getTime()
            });
            AppCenter.appPref.updatePref('open', appUID);
        }
        
        AppCenterDAO.increateAppVisits(appUID);
        Tracker.track(Tracker.app.type, Tracker.app.actions.OPEN_APP, appUID);
    },
    
    removeUntrackedApps: function() {
        var windowMediator = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);

        var enumerator = windowMediator.getEnumerator("mozilla:favpart_widget"); 
        var ids = [];
        while(enumerator.hasMoreElements()) {  
            var win = enumerator.getNext();
            ids.push(win.uid);
        }        
        
        // Keep history list less than 100
        AppCenterDAO.keepHistoryList(100);
        
        var args = {};
        var sql = [];
        sql.push('DELETE FROM apps');
        sql.push('WHERE bookmark = 0 AND type != 0');
        if (ids.length > 0) {
            sql.push('AND uid NOT IN (');
            for (var i = 0; i < ids.length; i++) {
                if (i > 0) {
                    sql.push(',');
                }
                sql.push(':id' + i);
                args['id' + i] = ids[i];
            }
            sql.push(')');
        }
        sql.push('AND id IN (SELECT app_id FROM app_info WHERE last_opened IS NULL)')
        
        // remove unbookmarked apps which are not opened and not built-in app.
        AppCenterDAO.executeSQL(sql.join(' '), args);
        // remove app info refered empty app.
        AppCenterDAO.executeSQL('DELETE FROM app_info WHERE last_opened IS NULL AND app_id NOT IN (SELECT id FROM apps)')
    },
    
    updateAppThumbnail: function(uid) {
        var appWindow = this.findAppWindow(uid);
        if (!appWindow){
            return;
        }
        
        if (!AppCenterDAO.appExists(uid)) {
            logger.trace(uid + ' does not exists.');
            return;
        }
        
        this.updateAppStateWithArg({
            uid: uid,
            thumbnail: appWindow.widget.getThumbnail()
        });
    },
    
    /**
     * Update info about app, including: screenX, screenY, thumbnail etc.
     */
    updateAppState: function(appUID) {
        var appWindow = this.findAppWindow(appUID);
        if (!appWindow){
            return;
        }
        
        if (!AppCenterDAO.appExists(appUID)) {
            logger.trace(appUID + 'does not exists.');
            return;
        }
        
        this.updateAppStateWithArg({
            uid:   appUID,
            screen_x:  appWindow.screenX,
            screen_y:  appWindow.screenY
        });
    },
    
    updateAppStateWithArg: function(appInfo) {
        AppCenterDAO.addOrUpdateAppState(appInfo);
    },
    
    /**
     * Update app args.
     */
    updateAppArgs: function(uid, args) {
        var app_args = AppCenterDAO.getAppArgsByUID(uid);
        try {
            app_args = JSON.parse(app_args);
        } catch (e) {
            app_args = {};
        }
        
        utils.extend(app_args, args);
        
        AppCenterDAO.updateAppArgs(uid, JSON.stringify(app_args));
    },
    
    updateApp: function(appInfo) {
        // update infomation of app, including: iconUrl, appArgs
        // App name could be modified, so appName can not be updated.

        var app = AppCenterDAO.getAppByUID(appInfo.appUID);
        if (!app) {
            logger.trace('App "' + appInfo.appUID + '" does not exist.');
            return;
        }
        
        // Chec if iconUrl is changed, if true, reset icon_data_url
        if (appInfo.iconUrl != app.icon_url) {
            app.icon_data_url = null;
            // Update icon url only if icon url is not chrome url.
            if (app.icon_url.indexOf("chrome://") == -1) {
                app.icon_url = appInfo.iconUrl;
            }
        }
        
        app.app_args = JSON.stringify(appInfo.appArgs);
        if (appInfo.smallIcon) {
            app.small_icon = appInfo.smallIcon;
        }
        
        logger.trace('Start update app info: "' + appInfo.appUID);
        AppCenterDAO.updateApp(app);
    },
    
    getAppIconUrl: function(app) {
        var icon_url = app.icon_data_url && app.icon_data_url.indexOf("data:image") == 0 ? app.icon_data_url : app.icon_url;
        if (!icon_url || icon_url == "") {
            icon_url = "chrome://mozapps/skin/places/defaultFavicon.png";   // use default fav icon
        }
        return icon_url;
    },
    
    getAppSmallIcon: function(app) {
        return !app.small_icon ? this.getAppIconUrl(app) : app.small_icon;
    }
};

// update the version of AppCenter.
if (utils.isFFVersionGET4()) {
    Cu["import"]("resource://gre/modules/AddonManager.jsm");
    AddonManager.getAddonByID("livemargins@mozillaonline.com", function(addon) {
        AppCenter.version = addon.version;    
    });
} else {
    var em = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
    AppCenter.version = em.getItemForID("livemargins@mozillaonline.com").version;
}

/********* object for app pref change and prefObserver ***********/
AppCenter.appPref = (function() {
    var _observers = [];
    var _maps = {
        'apps.unbookmark': 'onUnbookmark',
        'apps.hideunbookmarkundomsg': 'onHideUnbookmarkUndoMsg',
        'apps.changename': 'onChangeName',
        'apps.bookmark': 'onBookmark',
        'apps.open': 'onOpenApp',
        'apps.clearhistory': 'onClearHistory',
        'apps.local': 'onSetLocal',
        'apps.unlocal': 'onSetUnLocal',
        'apps.moveapp': 'onMoveApp',
        'apps.showbookmarks': 'onShowAppBookmarks'
    };
    
    var _prefListener = new utils.prefListener('extensions.livemargins@mozillaonline.com.', function(branch, name) {
        if (!_maps[name]) {
            return;
        }
        
        var value = branch.getCharPref(name);
        _observers.forEach(function(observer) {
            if (!_check_if_trigger_(observer) && typeof observer[_maps[name]] == 'function') {
                try {
                    observer[_maps[name]](value.replace(/\d+-/i, ''));
                } catch (e) {
                    //
                }
            }
        });
    });
    _prefListener.register();
    
    var _trigger_ = null;
    function _updatePref(prefName, appUID, trigger) {
        try {
            _trigger_ = trigger;
            utils.setPref("apps." + prefName, new Date().getTime() + '-' + appUID);
        } catch (e) {
            logger.error(e);
        }
    }
    
    function _check_if_trigger_(observer) {
        if (_trigger_ == observer) {
            _trigger_ = null;
            return true;
        }
        
        _trigger_ = null;
        return false;
    }
    
    return {
        addObserver: function(observer) {
            logger.trace('register observer');
            _observers.push(observer);
        },
        
        removeObserver: function(observer) {
            logger.trace('unregister observer');
            var obs = [];
            _observers.forEach(function(ob) {
                if (observer == ob) {
                    return;
                }
                
                obs.push(ob);
            });
            _observers = obs;
        },
        
        updatePref: function(prefName, appUID, trigger) {
            _updatePref(prefName, appUID, trigger);
        }        
    };
})();
/******* end ********/

/****************************************************************
 * Object for built-in app related operations.
 ****************************************************************/
AppCenter.dnd = {
    galleryAppUID: 'app_gallery',
    searchAppUID: 'app_search',
    musicAppUID: 'app_music',
    
    doGrabPic: function(picId) {
        var appWindow = AppCenter.findAppWindow(this.galleryAppUID);
        if (appWindow){
            appWindow.widget.doGrabPic(picId);
            appWindow.focus();
            return true;
        }
        
        AppCenter.openApp(this.galleryAppUID, true, {
            picId: picId
        });
    },
    
    doSearch: function(data) {
        var appWindow = AppCenter.findAppWindow(this.searchAppUID);
        if (appWindow){
            appWindow.widget.doSearch(data);
            appWindow.focus();
            return true;
        }
        
        AppCenter.openApp(this.searchAppUID, true, {
            data: data
        });
    },
    
    playMusic: function(songId) {
        var appWindow = AppCenter.findAppWindow(this.musicAppUID);
        if (appWindow){
            appWindow.widget.playMusic(songId);
            appWindow.focus();
            return true;
        }
        
        AppCenter.openApp(this.musicAppUID, true, {
            songId: songId
        });
    }
};
/******* end *******/