/* Copyright 2009 Palm, Inc.  All rights reserved. */

var MyappsAssistant = Class.create({
	
    initialize: function()
	{
		this._myApps = new Object();
		this._myUpdates = new Object();
	},
	
    setup: function()
	{
		this._spinner = new Spinner(this, 'spinner', false, 'large');
        
		this._initMyApps();	
 		this.controller.setupWidget('appsList', 
		{ 
			itemTemplate:'myapps/myapps-item',
		}, this._myApps);
		this.controller.listen("appsList", Mojo.Event.listTap, this.handleAppListTap.bind(this));
		
		Mojo.Log.info("MyAppsAssistant.setup Catalog.AppDownloadMngr.myAppsListIsReady() == %s", Catalog.AppDownloadMngr.myAppsListIsReady());
		if (this._myApps.items.length == 0 && Catalog.AppDownloadMngr.myAppsListIsReady())
		{
			this.controller.get("noApps").style.display = "block";
		}
		
		// listen for myapps queue changes
		Catalog.AppDownloadMngr.attach(this);
		                                          
		// Only show "command menu" when there are apps with updates
		this._installUpdatesModel = {
			visible: false,
			items: [
				{},
			 	{},
				{
					label: $L('Install Updates'),
					command: 'installupdates',
					disabled: false,
				},
			]};              
		this.controller.setupWidget(Mojo.Menu.commandMenu, {menuClass: 'no-fade'}, this._installUpdatesModel);
		
		// Menus
		//Weave.Utilities.AppMenu.useDefault(this);
		var self = this;
		new Weave.Utilities.AppMenu(this)
			.addEdit()
			.addItem($L('Preferences & Accounts'), 
				function()
				{
					self.controller.stageController.pushScene("prefs");
				},
				function()
				{
					return myProfile.enableAcc;
				})
			.addItem($L('Pause All Downloads'), this._pauseAllDownloads.bind(this))
			.addItem($L('Delete Paused Downloads'), this._deletePausedDownloads.bind(this))
			//.addItem($L('Delete All Downloads'), this._deleteAllDownloads.bind(this))
			.addHelp('http://help.palm.com/app_catalog/index.html');
		Weave.Services.ApplicationServer.getAppCatUserFlags(function(status, response, token)
		{
			//myProfile.country = token.country;
			myProfile.carrier = token.carrier.toLowerCase();
			myProfile.email = token.email;
			if (status ) {
				if(response.enablePaymentSetup)
					myProfile.enableAcc = response.enablePaymentSetup;
				else
					menu._items.splice(1, 1);
			}
			else 
			{
				Weave.Services.DeviceProfile.getCarrierIdentification(function(status, response){
					if (status && ((response.mcc >= 310 && response.mcc <= 316) || PalmSystem.version.match("desktop"))) {
						myProfile.enableAcc = true;
					}
					else {
						// No nice way to do this - remove the prefs menu item
						menu._items.splice(1, 1);
					}
				});
			}
		});
    },
	
	cleanup: function()
	{
		Catalog.AppDownloadMngr.detach(this);
	},

	_initMyApps: function()
	{
		this._myApps.items = [];
		
		if (!Catalog.AppDownloadMngr.myAppsListIsReady()) return;
		
		this._myAppsHash = Catalog.AppDownloadMngr.getMyApps();
		for (var x in this._myAppsHash) 
		{
			this._myApps.items.push(this._myAppsHash[x]);
		}
		Mojo.Log.info("MyAppsAssistant._initMyApps apps len: %d", this._myApps.items.length);
		
		// sort apps alphabetically
		this._myApps.items.sort(function(a, b) {
			if (a.title.toLowerCase() == b.title.toLowerCase()) {
				return 0;
			} else {
				return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : 1;
			}
		});
		
		// fetch updates
		this._fetchUpdates();
	},
	
	
	_fetchUpdates: function()
	{		
		var installedAppsPackageIds = new Array();
		for (var i = 0; i < this._myApps.items.length; i++) 
		{
			if (this._myApps.items[i].isInstalled())
				installedAppsPackageIds.push(this._myApps.items[i].publicApplicationId);
		}

		if (installedAppsPackageIds.length == 0) return;
				
		var self = this;
		Weave.Services.ApplicationServer.getAppListForUpdates(installedAppsPackageIds, function(status, apps) 
		{
			if (status) 
			{
				Mojo.Log.info("MyAppsAssistant._fetchUpdates server returned apps: %j", apps);
				for (var i = 0; i < apps.length; i++) 
				{
					if (self._myAppsHash[apps[i].publicApplicationId]) 
					{
						Mojo.Log.info("MyAppsAssistant._fetchUpdates updating: %j", apps[i].publicApplicationId);
						self._myAppsHash[apps[i].publicApplicationId].updateFromServerUpdatesList(apps[i]);
					}
				}
			}
			else 
			{
				Utilities.Errors.displayError(apps);
			}
		});
	},
	
	handleAppListTap: function(e) 
	{
		// defer to appDownloadObject which will perform 
		// different action based on its current state
		var targetRow = $(e.originalEvent.target);
		Mojo.Log.info("MyAppsAssistant.handleAppListTap target ", targetRow.innerHTML)
		if (targetRow != undefined &&
			(targetRow.hasClassName('pause-button') ||
			targetRow.hasClassName('resume-button') ||
			targetRow.hasClassName('warning-icon') ||
			targetRow.hasClassName('clear-button') ||
			targetRow.hasClassName('update-button'))) 
		{
			e.item.myAppsDefaultAction();
		}
		else 
		{
			this.controller.stageController.pushScene('details', e.item.id, e.item.publicApplicationId);
		}
	},
	
	// Observer method to add/remove/update myapps changes
	updateMyApps: function(app, updateReason)
	{
		// Optimization:
		// don't process any updates until myApps list is populated
		if (!Catalog.AppDownloadMngr.myAppsListIsReady()) return;
		
		Mojo.Log.info("MyAppsAssistant.updateMyApps app %s, updateReason %s", (app? app.toString() : undefined), updateReason);
		var apps = this._myApps.items;
		var listWidget = this.controller.get("appsList"); 
		
		if (updateReason == Catalog.AppDownloadMngr.APP_ADDED)
		{
			// add it to the right spot in myApps array
			var index = this.binarySearch(this._myApps.items, app, true);
			Mojo.Log.info("MyAppsAssistant.updateMyApps APP_ADDED at index %d", index);
			
			this._myApps.items.splice(index, 0, app);		
			if (listWidget.mojo) 
				listWidget.mojo.noticeAddedItems(index, [app]);
				
			this._updateNoAppsDiv();
			this._updateMyUpdatesQueue(app);
		}
		else if (updateReason == Catalog.AppDownloadMngr.APP_DELETED)
		{
			// delete this app from myApps array
			var index = this.binarySearch(this._myApps.items, app, false);
			Mojo.assert(index >= 0, "MyAppsAssistant.updateMyApps app can't be deleted since index < 0");
			
			if (index >= 0) 
			{
				this._myApps.items.splice(index, 1);
				if (listWidget.mojo)
					listWidget.mojo.noticeRemovedItems(index, 1);
			}
			else 
			{
				// binary search is fixed, but why not have a fallback :-)
				// debug binary search for fantom error print array and search key
				Mojo.Log.error("MyAppsAssistant.updateMyApps binary search failed for %s, %s", i, app.publicApplicationId, app.title);
				for (var i = 0; i < this._myApps.items.length; i++)
					Mojo.Log.error("MyAppsAssistant.updateMyApps binary search failed in this._myApps.items[%d]=%s, %s", i, this._myApps.items[i].publicApplicationId, this._myApps.items[i].title);
			
				index = this.serialSearch(this._myApps.items, app);
				if (index >= 0) 
				{
					Mojo.Log.error("MyAppsAssistant.updateMyApps serial search found item at index %d", index);
					this._myApps.items.splice(index, 1);
					if (listWidget.mojo)
						listWidget.mojo.noticeRemovedItems(index, 1);
				}
			}
			
			this._updateNoAppsDiv();
			this._updateMyUpdatesQueue(app);
		}
		else if (updateReason == Catalog.AppDownloadMngr.APP_UPDATED)
		{
			var index = this.binarySearch(this._myApps.items, app, false);
			Mojo.assert(index >= 0, "MyAppsAssistant.updateMyApps app can't be updated since index < 0");
			if (index >= 0) 
			{
				if (listWidget.mojo)
					listWidget.mojo.noticeUpdatedItems(index, [app]);
			}
			else 
			{
				// binary search is fixed, but why not have a fallback :-)
				// debug binary search for fantom error print array and search key
				Mojo.Log.error("MyAppsAssistant.updateMyApps binary search failed for %s, %s", i, app.publicApplicationId, app.title);
				for (var i = 0; i < this._myApps.items.length; i++)
					Mojo.Log.error("MyAppsAssistant.updateMyApps binary search failed in this._myApps.items[%d]=%s, %s", i, this._myApps.items[i].publicApplicationId, this._myApps.items[i].title);
			
				index = this.serialSearch(this._myApps.items, app);
				if (index >= 0) 
				{
					Mojo.Log.error("MyAppsAssistant.updateMyApps serial search found item at index %d", index);
					if (listWidget.mojo)
						listWidget.mojo.noticeUpdatedItems(index, [app]);
				}
			}
			
			this._updateMyUpdatesQueue(app);
		}
		else if (updateReason == Catalog.AppDownloadMngr.MYAPPS_ALL)
		{
			Mojo.Log.info("MyAppsAssistant.updateMyApps MYAPPS_ALL");
			this._showInstallUpdatesMenu(false);
			this._initMyApps();
			this.controller.modelChanged(this._myApps, this);
			this._updateNoAppsDiv();
		}
	},
		
	// search(o: Array, v: Object, [insert: Boolean = false])
	binarySearch: function(o, v, insert)
	{			
	    var u = o.length - 1;
		var l = 0;
		var m;
		var searchTitle = v.title.toLowerCase();
		
	    while (l <= u) 
		{
			m = (l + u) >> 1;
			
			if (o[m].title.toLowerCase() < searchTitle)
			{
				l = m + 1;
			}
			else if (o[m].title.toLowerCase() == searchTitle) 
			{
				// make sure that ids are the same as well
				if (o[m].publicApplicationId == v.publicApplicationId)
					return m;
				
				// serial search down
				var i = m - 1;
				while (i >= 0 && o[i].title == v.title)
				{
					if (o[i].publicApplicationId == v.publicApplicationId)
						return i;
					i--;
				}
				
				// serial search up
				var i = m + 1;
				while (i < o.length && o[i].title == v.title)
				{
					if (o[i].publicApplicationId == v.publicApplicationId)
						return i;
					i++;
				}
				
				if (insert) 
					return m;
				else 
					return -1;
			}
			else 
			{
				u = m - 1;
			}
		}
				
		if (insert)
			return l;
		else
			return -1;
	},
	
	// TODO remove this when fantom binary search bug is found, that is if it exists
	serialSearch: function(arr, app)
	{
		for (var i = 0; i < arr.length; i++)
		{
			if (arr[i].publicApplicationId == app.publicApplicationId)
				return i;
		}
	},
	
	// updates the list of active updates
	_updateMyUpdatesQueue: function(app)
	{
		if (app.canInstallUpdate())
		{
			// if this app has an update we set the hash value to true
			if (!this._myUpdates[app.publicApplicationId]) 
			{
				Mojo.Log.info("MyAppsAssistant._updateMyUpdatesQueue added update to _myUpdates hash: ", app.publicApplicationId);
				this._myUpdates[app.publicApplicationId] = true;
				this._showInstallUpdatesMenu(true);
			}
		}
		// if this app does not have an update we delete the hash value
		else if (this._myUpdates[app.publicApplicationId] == true)
		{	
			delete this._myUpdates[app.publicApplicationId];
			
			// if there are any remaining updates we just return and don't hide the menu
			for (var x in this._myUpdates) 
			{
				Mojo.Log.info("MyAppsAssistant._updateMyUpdatesQueue there are more updates in the _myUpdates hash: ", x);
				return;
			}
			
			// hide the menu button
			this._showInstallUpdatesMenu(false);
		}
	},
	
	// show/hide the "install all updates" button
	_showInstallUpdatesMenu: function(visible)
	{
		if (visible == false)
		{
			this._myUpdates = new Object();
		}
		
		Mojo.Log.info("MyAppsAssistant._showInstallUpdatesMenu setVisible", visible);
		if ((visible == false && this.controller.getMenuVisible(Mojo.Menu.commandMenu)) ||
			(visible == true && !this.controller.getMenuVisible(Mojo.Menu.commandMenu)) ) 
		{
			Mojo.Log.info("MyAppsAssistant._showInstallUpdatesMenu toggling menu");
			this.controller.toggleMenuVisible(Mojo.Menu.commandMenu);
		}
	},
	
	// show/hide "no apps"
	_updateNoAppsDiv: function()
	{
		var noAppsDiv = this.controller.get("noApps");
		if (!noAppsDiv) return;
		
		// hide "no apps" div if visible
		if (this._myApps.items.length > 0 && noAppsDiv.style.display == "block") 
		{
			noAppsDiv.style.display = "none";
		}
		else if (this._myApps.items.length == 0 && noAppsDiv.style.display == "none" && 
				Catalog.AppDownloadMngr.myAppsListIsReady())
		{
			// if there are no apps show "no apps" div
			noAppsDiv.style.display = "block";
		}
	},
	
	handleCommand: function(event, app)
	{
 		if (event.type == Mojo.Event.command && event.command == 'installupdates') 
		{
			this._installAllUpdates();
		}
		else if (event.type == Mojo.Event.back) 
		{
			var sceneStack = this.controller.stageController.getScenes();
			if (sceneStack && sceneStack.size() == 1)
			{
				Mojo.Log.info("MyApps.handleCommand back: swap for main scene");
				this.controller.stageController.swapScene("main");
				event.stop();
			}
		}
 	},
	
	_pauseAllDownloads: function()
	{
		Mojo.Log.info("MyAppsAssistant._pauseAllDownloads");
		// loop over my apps and pause ones that can be paused
		for (var i = 0; i < this._myApps.items.length; i++) 
		{
			this._myApps.items[i].pauseDownload();
		}
	},
	
	_deletePausedDownloads: function()
	{
		Mojo.Log.info("MyAppsAssistant._deletePausedDownloads");
		// loop over my apps and pause ones that can be paused
		for (var i = 0; i < this._myApps.items.length; i++) 
		{
			Mojo.Log.info("MyAppsAssistant._deletePausedDownloads ", this._myApps.items[i].stateToString());
			this._myApps.items[i].cancelPausedDownload();
		}
	},
	
	_deleteAllDownloads: function()
	{
		Mojo.Log.info("MyAppsAssistant._deleteAllDownloads");
		// loop over my apps and pause ones that can be paused
		for (var i = 0; i < this._myApps.items.length; i++) 
		{
			this._myApps.items[i].cancelDownload();
		}
	},
		
	_installAllUpdates: function()
	{
		// Calculate required space
		var updateApps = new Array();
		var totalIpkgSize = 0;
		var totalUpdatesSize = 0;
		
		// loop over my apps and find the ones with updates
		for (var i = 0; i < this._myApps.items.length; i++) 
		{
			Mojo.Log.info("MyApps._installAllUpdates loop this._myApps.items[%d]=%s, %s", i, this._myApps.items[i].publicApplicationId, this._myApps.items[i].stateToString());
			if (this._myApps.items[i].canInstallUpdate()) 
			{
				Mojo.Log.info("MyApps._installAllUpdates loop added to updateApps this._myApps.items[%d]=%s, %s", i, this._myApps.items[i].publicApplicationId, this._myApps.items[i].stateToString());
				updateApps.push(this._myApps.items[i]);
				totalIpkgSize += this._myApps.items[i].packageSize;
				totalUpdatesSize += this._myApps.items[i].installSize;
			}
		}
		
		// make sure we have some updates before proceeding
		if (updateApps.length == 0) return;
		
		// hide install all updates button
		this._showInstallUpdatesMenu(false);
		
		// prepare data to send to validateInstall method
		var installApp = 
		{
			publicApplicationId: "dummy",
			packageSize: totalIpkgSize,
			installSize: totalUpdatesSize,
		}
		var showError = updateApps.length == 1 ? "validate_space_default_single" : "validate_space_default_mult";
		
		Mojo.Log.info("MyApps._installAllUpdates validateInstall sending ipkgSize: %d, installSize %d", totalIpkgSize, totalUpdatesSize);
		Catalog.appStatesCommon.validateInstallSpace(installApp, showError, function(status)
		{
			if (status) 
			{
				// validate data connection
				Catalog.appStatesCommon.validateDownloadConnection(function(status)
				{
					if (status)
					{
						// install updates
						for (var i = 0; i < updateApps.length; i++) 
						{
							Mojo.Log.info("MyApps._installAllUpdates connection and space OK: installing", updateApps[i].publicApplicationId);
							updateApps[i].installUpdate();
						}
					}
				});
			}
		});
	},
	
});