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

var AccountsAssistant = Class.create(App.Scene, {
	initialize: function($super) {
		$super();

		this.folderSubscriptions = {};
		this.folderDelayedRenderId = {};
		this.folderResponseData = {};
		this.favoritesSettingList = {};
		this.favoriteFoldersModel = {};
		this.favFolderScrollAdjust = 0;
		this.orientationString = "";
		this.accountElemState = {};
		this.filterCount = 0;
		this.stageController = Mojo.Controller.appController.getStageController("email");
		
		// Handlers. Bind but don't listen yet.
		this.renderAccountsHandler = this.renderAccounts.bind(this);
		this.accountsErrorHandler = this.accountsError.bind(this);
		this.renderFavFolderListHandler = this.renderFolderList.bind(this, AccountsAssistant.kFavoriteFoldersAccountID);
		this.boundHandleFavoritesSelection = this.handleFavoritesSelection.bind(this)
		this.boundHandleSelection = this.handleSelection.bind(this);
		this.boundKeypressHandler = this.keypressHandler.bind(this);
		this.boundHandleFoldersSearch = this.handleFoldersSearch.bind(this);
		
		// Register listener for focus and blur window events so the notification assistant knows
		// when email UI is up, but not in the foreground. 
		// Accounts is the "entry" scene so it seems like the best place to register a focus/blur event listener
		this.notificationAssistant = Mojo.Controller.getAppController().assistant.notificationAssistant;
		this.boundWindowBlurFocusHandler = this.notificationAssistant.windowBlurFocusHandler.bind(this.notificationAssistant);

	},

	setup: function() {
		
		// setup event listeners
		Mojo.Event.listen(this.stageController.document, Mojo.Event.activate, this.boundWindowBlurFocusHandler);
		Mojo.Event.listen(this.stageController.document, Mojo.Event.deactivate, this.boundWindowBlurFocusHandler);
		// We're creating the scene assistant, so assume the app is launching and focused 
		this.notificationAssistant.windowBlurFocusHandler({ type: Mojo.Event.activate, eventPhase: Event.AT_TARGET });

		this.accountsErrorCount = 0;

		this.cmdMenuModel = {
				visible:true,
				items: [
					{label:$L('Compose'), icon:'compose', command:'compose'},
					{}
				]};
		this.controller.setupWidget(Mojo.Menu.commandMenu, undefined, this.cmdMenuModel);

		this.accountsModel = { items: [] };
		this.controller.setupWidget('accounts_list',
							{ itemTemplate: 'accounts/accounts_entry' },
							this.accountsModel);

		this.accountsListElement = this.controller.get('accounts_list');
		this.accountsListElement.observe(Mojo.Event.listTap, this.boundHandleSelection);
		this.controller.listen('favorite_folders', Mojo.Event.tap, this.boundHandleFavoritesSelection);
		this.controller.listen(this.controller.sceneElement, Mojo.Event.keypress, this.boundKeypressHandler);

		EmailAppDepot.depot.isShowFavorites(AccountsAssistant.kFavoriteFoldersAccountID, this.depotGetFavExpanded.bind(this));
		
		//set up account folder's search
		this.folderFilterField = this.controller.get('account_folders_filter');
		this.controller.setupWidget('account_folders_filter',
			attributes = {
				delay: 500,
				filterFieldHeight: 15
			},
			model = {
				disabled: false
			});
		this.controller.listen('account_folders_filter', Mojo.Event.filter, this.boundHandleFoldersSearch); 
	},

	cleanup: function() {
		var renderIds = this.folderDelayedRenderId;
		$H(renderIds).keys().each(function(accountID) {
			if (renderIds[accountID]) {
				clearTimeout(renderIds[accountID]);
			}
		});
		Mojo.Event.stopListening(this.stageController.document, Mojo.Event.activate, this.boundWindowBlurFocusHandler);
		Mojo.Event.stopListening(this.stageController.document, Mojo.Event.deactivate, this.boundWindowBlurFocusHandler);
		this.accountsListElement.stopObserving(Mojo.Event.listTap, this.boundHandleSelection);
		this.controller.stopListening('favorite_folders', Mojo.Event.tap, this.boundHandleFavoritesSelection);
		this.controller.stopListening('account_folders_filter', Mojo.Event.filter, this.boundHandleFoldersSearch);
		this.controller.stopListening(this.controller.sceneElement, Mojo.Event.keypress, this.boundKeypressHandler); 		
	},
	
	aboutToActivate: function(callback) {
		this.activateCallback = callback;
		if (!this.firstActivate) {
			// in the even we are reopening the app, we may have cached data for display 
			AccountsAssistant.folderListCache.getKeys().each(function(accountID){
				this.renderFolderListDelayed(accountID, AccountsAssistant.folderListCache.get(accountID));
			}.bind(this));
			this.firstActivate = true;
		}
	},

	activate: function() {
		// If the activate callback wasn't called by now, it is too late so remove it.
		this.activateCallback = undefined;

		// clear with no parameters will clear all notifications and take down the dashboard 
		this.notificationAssistant.setIgnoreNewEmails(true);
		this.notificationAssistant.clear();
		
		// Cause any delayed folder render functions to execute as soon as possible.
		this.setupDelayedRenderings(0);
		
		Mojo.Controller.getAppController().assistant.clearDebounce('accounts');		
	},
	
	// setup delayed renderings for all accounts
	// note that delay is in seconds
	setupDelayedRenderings: function(delay) {
		var renderIds = this.folderDelayedRenderId;
		$H(renderIds).keys().each(function(accountID) {
			if (renderIds[accountID]) {
				clearTimeout(renderIds[accountID]);
				this.setAccountRenderDelay(accountID, delay);
			}
		}.bind(this));
	},
	
	// used to set the render delay for a specific account
	// note that delay is in seconds
    setAccountRenderDelay: function(accountID, delay){
        var func;
        if (accountID === AccountsAssistant.kFavoriteFoldersAccountID) {
            func = this.renderFavoritesFolderListDelayed.bind(this);
        }
        else {
            func = this.renderFolderListDelayed.bind(this, accountID);
        }
	
		this.folderDelayedRenderId[accountID] = func.delay(delay); 
    },

	deactivate: function() {
		this.notificationAssistant.setIgnoreNewEmails(false);
	},

	keypressHandler: function(e) {
		this.orientationString += String.fromCharCode(e.originalEvent.charCode);
		Mojo.Log.info(this.orientationString);
		if (this.orientationString.length === 12) {
			if (this.orientationString === "RocknRollHax") {
				Mojo.Log.info("Let's rock and roll");
				var targetWindow = this.controller.window;
				if (targetWindow.PalmSystem.setWindowOrientation) {
					targetWindow.PalmSystem.setWindowOrientation("free");
				}
		
				// Allow override of 'back' gesture in landscape mode.	
				if (targetWindow.PalmSystem.receivePageUpDownInLandscape) {
					targetWindow.PalmSystem.receivePageUpDownInLandscape(true);
				}
			} else {
				this.orientationString = "";
			}
		}
	},

	orientationChanged: function(orientation) {
		if (orientation === "left" || orientation === "right") {
			this.controller.sceneElement.addClassName('landscape');
		} else {
			this.controller.sceneElement.removeClassName('landscape');
		}
	},

	// This function only updates the folder lists every 3 seconds to avoid over taxing the cpu during
	// initial sync (or any significant account sync)
	renderFolderList: function(accountID, data) {
		//Mojo.Log.info("AccountsAssistant.renderFolderList(): accountID=%s, data=%j", accountID, data)
		// The first time the data is retrieved, the storage is undefined. In this case, 
		// display the folders immediately so the user sees something.
		if (this.folderResponseData[accountID] === undefined) {
			// NOT USING this.setAccountRenderDelay(accountID, 0) because it causes
			// problems in this case
			if (accountID === AccountsAssistant.kFavoriteFoldersAccountID) {
				this.renderFavoritesFolderListDelayed(data);
			} else {
				this.renderFolderListDelayed(accountID, data);
			}
			// Mark it as null so subsequent data will get delayed
			this.folderResponseData[accountID] = null;
		}
		// If there's no data queued up, start the delayed function
		else if (this.folderResponseData[accountID] === null) {
			this.folderResponseData[accountID] = data;
			this.setAccountRenderDelay(accountID, 2);
		}
		// a delayed function is queued up, so just update the data to the newly received. 
		else {
			this.folderResponseData[accountID] = data;
		}
	},

	renderFolderListDelayed: function(accountID, data) {
		var account;
		// The user may have more than one account, but assume that the other folder lists
		// will come very quickly after the first response, so let the system know we're
		// ready to complete the scene transition
		if (this.activateCallback !== undefined) {
			this.activateCallback();
			this.activateCallback = undefined;
		}
	
		// If the 'data' parameter wasn't used, this must be the delayed data callback so
		// grab the data from it's storage place. Set it to null to mark it as processed
		// which will allow the next folderList response to correctly delay.
		if (data === undefined) {
			data = this.folderResponseData[accountID];
			this.folderDelayedRenderId[accountID] = null;
			this.folderResponseData[accountID] = null;
			if (!data) {
				Mojo.Log.error("No folder data for account ", accountID);
				return;
			}
		}
		
		// cache account folder info
		AccountsAssistant.folderListCache.push(accountID, data);
		
		//Mojo.Log.info("AccountsAssistant.renderFolderListDelayed(): data=%j", data);
		if (data.list) {
			var folderListElem = this.controller.get('account_folders_' + accountID);
			if (!folderListElem) {
				// The account has been deleted so unsubscribe.
				Mojo.Log.warn("Unsubscribing because the account no longer exists: account_folders_", accountID);
				var sub = this.folderSubscriptions[accountID];
				if (sub) {
					sub.cancel();
					delete(this.folderSubscriptions[accountID]);
				}
			} else {
				var list = data.list;
				list.each(function(folder){
					AccountsAssistant.formatFavoriteFolders(folder, this.filterString);
				}.bind(this));
	
				// Render the collection of folders and then render that into the containing list div 
				var contentFolders = Mojo.View.render({collection: list, template: 'accounts/folder_entry'});
				contentFolders = Mojo.View.render({object: {folders:contentFolders}, template: 'accounts/folder_list'});
				folderListElem.update(contentFolders);
				// Also store this away in the model so updates to the accounts will
				// correctly include the folder list
				account = this.accountsModel.items.find(function(a) { return (a.id == accountID); });
				if (account) {
					account.folderList = contentFolders;
				}
				
				//Mojo.Log.info("AccountsAssistant.renderFolderListDelayed(): list=%j", list);
				this.handleFolderSearchResult(accountID, this.controller.get(accountID), data.count);
				
				// Style last folder in list
				if (list.length > 0 && list[list.length - 1].id) {
					this.styleFinalFolderDiv(list[list.length - 1].id);
				}
			}
		} else {
			var controller = this.controller;
			data.changeList.each(function(folder) {
				AccountsAssistant.formatFolder(folder, this.filterString);
				var elem = controller.get(folder.id);
				if (elem) {
					// Already have a div, so use the "no outer div" template
					var content = Mojo.View.render({object: folder, template: 'accounts/folder_entry'});
					elem.replace(content);
				}
			}.bind(this));
			// Also store this away in the model so updates to the accounts will
			// correctly include the folder list
			account = this.accountsModel.items.find(function(a) { return (a.id == accountID); });
			if (account) {
				var existingElem = controller.get('account_folders_'+accountID);
				account.folderList = existingElem.innerHTML;				
			}
		}
	},
	
	styleFinalFolderDiv: function(folderId) {
		if (!folderId) 
			return;
		// Style last folder in list 
		var rowBlock = this.controller.get(folderId);
		if (rowBlock.className) {
			rowBlock.className = rowBlock.className + " last";
		} else {
			rowBlock.className = "last";
		}
	},

	subscribeFavoriteFolderList: function() {
		if (this.favSubscription !== undefined) {
			this.favSubscription.cancel();
		}
		this.favSubscription = Folder.subscribeFavoriteFolderList(this.filterString, this.renderFolderList.bind(this, AccountsAssistant.kFavoriteFoldersAccountID));
	},

	/*
	 * Render the folders within the Favorites meta-account. Note, this does not participate in the 
	 * delay strategy for rendering regular accounts because there are a lot of instances when user
	 * action causes Favorites to update (and the update should be immediate, not delayed).
	 */
	renderFavoritesFolderListDelayed: function(data) {
		//Mojo.Log.info("Accounts.renderFavoritesFolderList ", $H(data).inspect())
		// This is a special account with the id 'favorite_folders'
		var accountID = AccountsAssistant.kFavoriteFoldersAccountID;
		if (data === undefined) {
			data = this.folderResponseData[accountID];
			this.folderDelayedRenderId[accountID] = null;
			this.folderResponseData[accountID] = null;
			if (!data) {
				Mojo.Log.error("No folder data for account ", accountID);
				return;
			}
		}

		if (data.list) {
			var list = data.list;
			list.each(function(folder){
				AccountsAssistant.formatFavoriteFolders(folder, this.filterString);
				// since we know the unread count for the favorite folder instance,
				// update the account instance below with the same value
				this.updateFolderReadCount(folder.id, folder.unreadCount);
			}.bind(this));

			// Render the collection of folders and then render that into the containing list div 
			var contentFolders = Mojo.View.render({collection: list, template: 'accounts/favoritefolder_entry'});
			var folderListElem = this.controller.get('account_folders_'+accountID);
			folderListElem.update(contentFolders);
			// Also store this away in the model so updates to the accounts will
			// correctly include the folder list
			this.favoriteFoldersModel.folderList = contentFolders;
			
			this.handleFolderSearchResult(accountID, this.controller.get('favorite_folders'), data.count);
			
			// Style last folder in list 
			if (list.length > 0 && list[list.length - 1].id) {
				this.styleFinalFolderDiv("fav_#{folderId}".interpolate({
					"folderId": list[list.length - 1].id
				}));
			}
		} else {
			var controller = this.controller;
			data.changeList.each(function(folder) {
				AccountsAssistant.formatFavoriteFolders(folder, this.filterString);
				var elem = controller.get('fav_'+folder.id);
				if (elem) {
					// Already have a div, so use the "no outer div" template
					var content = Mojo.View.render({object: folder, template: 'accounts/favoritefolder_entry'});
					elem.replace(content);
				}
			}.bind(this));
			// Also store this away in the model so updates to the accounts will
			// correctly include the folder list
			var existingElem = this.controller.get('account_folders_'+accountID);
			this.favoriteFoldersModel.folderList = existingElem.innerHTML;
		}

		// Adjust the scroll position if necessary to keep the current list rows within
		// the viewport.
		this.controller.getSceneScroller().mojo.adjustBy(0, this.favFolderScrollAdjust);
		this.favFolderScrollAdjust = 0; // reset this now that its been adjusted
	},

	renderAccounts: function (accountsList) {
		//Mojo.Log.info("renderAccounts(): accountsList=%j", accountsList)
		// subscription succeeded so reset the failure count
		this.accountsErrorCount = 0;
		// TODO: this check should really be in app-assistant
		if (accountsList.count <= 0) {
			//Mojo.Log.info("accountsList.count=", accountsList.count);
			// Don't push the create scene again if we get notified multiple times.
			if (this.stageController.topScene().sceneName !== 'wizard') {
				var f = function() {
					// indicate that no accounts exist for proper back-flick actions
					this.stageController.pushScene('wizard', { edit: false, type: "email", noAccounts: true });					
				}.bind(this).delay(0.5);
			}
			return;
		}
		var oldList = this.accountsModel.items;

		// First check if the accounts list is the same. The things that could change are: 
		// an account added or removed (list length changed), list rearranged (id doesn't
		// match), or the account was renamed.
		var accountsChanged = (oldList.length !== accountsList.list.length);
		if (accountsChanged === false) {
			accountsList.list.each(function (a, index) {
				if (oldList[index].id !== a.id || oldList[index].name !== EmailAccount.localizeAccountName(a.name)) {
					accountsChanged = true;
					$break;
				}
			});
		}
		// Exit the function if nothing has changed!
		if (accountsChanged === false) {
			return;
		}

		this.accountsModel.items = accountsList.list;

        // Subscribe to the each of the accounts' folders lists:
		accountsList.list.each(function (a) {
			if (a.showFavoritesFlag == 1) {
				a.palmArrowOrientation = "palm-arrow-closed";
				a.showFolders = "display:none;";
			} else {
				a.palmArrowOrientation = "palm-arrow-expanded";
			}
						
			a.name = EmailAccount.localizeAccountName(a.name);
			
			if (!this.accountElemState[a.id]) 
				this.accountElemState[a.id] = {
					"showFavoritesFlag": a.showFavoritesFlag == 0,
					"showed": true,
					"expanded": a.showFavoritesFlag == 0
				};
			else 
				this.accountElemState[a.id].showFavoritesFlag = a.showFavoritesFlag == 0; 
			//Mojo.Log.info("this.accountElemState=%j", this.accountElemState)

			if (a.accountDomain == null) {
				a.accountDomain = 'generic';
			}

			var existingElem = this.controller.get(a.id);
			if (existingElem) {
				var oldAccount = oldList.find(function(old) { return (old.id == existingElem.id); });
				// inherit the folder list from the existing account element
				a.folderList = oldAccount.folderList;
			}

			if (!this.folderSubscriptions[a.id]) {
				//bind to this as implicit, and label as the div to attach to!
				var sub = Folder.subscribeAccountFolderList(a.id, this.filterString, this.renderFolderList.bind(this, a.id));
				if (sub) {
					this.folderSubscriptions[a.id] = sub;
				}
			}
		}.bind(this));

		// Show the Favorite Folders "account" now that the other accounts have been retrieved
		// and are being shown. This way it looks like it is part of the rest of the folders 
		if (!this.favoriteFolders && this.accountsModel.items.length > 0) {
			this.favoriteFolders = this.controller.get('favorite_folders');
			this.favoriteFolders.show();
		}
		
        this.controller.modelChanged(this.accountsModel);
	},

	accountsError: function(err) {
		// Try relaunching the mail service 10 times before giving up.
		this.accountsErrorCount++;
		if (this.accountsErrorCount < 10) {
			Mojo.Log.error("accounts-assistant accountsErrorCount=" + this.accountsErrorCount + " detail=" + Object.toJSON(err));
			this.accountsRequest.cancel();
			
			// also unsubscribe from the folders so they'll get resubscribed in renderAccounts();
			var folderSubs = this.folderSubscriptions;
			Object.keys(this.folderSubscriptions).each(function (accountId) {
				folderSubs[accountId].cancel();
				delete folderSubs[accountId];
			});

			this.accountsRequest = EmailAccount.subscribeAccounts(this.renderAccountsHandler, this.accountsErrorHandler);
			this.subscribeFavoriteFolderList(); // try resubscribing to this too
		} else {
			Mojo.Log.error("This Is Bad: accounts-assistant cannot maintain accountList subscription " + $H(err).inspect());
		}
	},

	/**
	 * handle a menu command.
	 */
	handleCommand: function(event) {
		if (event.type == Mojo.Event.command) {
			switch (event.command) {
				case 'compose':
					MenuController.showComposeView(undefined, undefined, this.controller, this.cmdMenuModel);
					break;

				case Mojo.Menu.prefsCmd:
					MenuController.showPrefs(this.stageController);
					break;

				case Mojo.Menu.helpCmd:
					MenuController.showHelp();
					break;
			}
		}
		// Enable prefs & help menu items
		else if (event.type == Mojo.Event.commandEnable && 
		         (event.command == Mojo.Menu.prefsCmd || event.command == Mojo.Menu.helpCmd)) {
			event.stopPropagation();
		}
	},

	handleFavoritesSelection: function(event) {
		//Mojo.Log.info('AccountsAssistant.handleFavoritesSelection()')
		var targetRow = this.controller.get(event.target);
		if (!targetRow.hasClassName("selection_target")) {
			targetRow = targetRow.up('.selection_target');
		}

		if (targetRow) {
			if (targetRow.hasClassName("account_details")) {
				this.toggleShowHideFolders(targetRow);
			}
			else if (targetRow.hasClassName("palm-row")) {
				this.openFavoriteFolder(targetRow);
			}
		}
	},

	handleSelection: function(event) {
		//Mojo.Log.info('AccountsAssistant.handleSelection()')
		// All the targetable elements have the class "selection_target" so they are
		// easy to find.
		var targetRow = this.controller.get(event.originalEvent.target);
		if (!targetRow.hasClassName("selection_target")) {
			targetRow = targetRow.up('.selection_target');
		}

		if (targetRow && !targetRow.hasClassName("noselect")) {
			if (targetRow.hasClassName("account_details")) {
				this.toggleShowHideFolders(targetRow);
			}
			else if (targetRow.id == "favorites") {
				this.toggleFavorites(targetRow);
			}
			else if (targetRow.hasClassName("palm-row")) {
				this.openFolder(targetRow);
			}
		}
	},
	
	handleFoldersSearch: function(event) {
		var filterString = event.filterString;
		
		Mojo.Log.info('AccountsAssistant.handleFolderSearch(): this.filterString=%s, filterString=%s, this.pendingFilterString=%s', this.filterString, filterString, this.pendingFilterString);
		if (this.filterString === filterString) {
			// only update filter field's count
			this.folderFilterField.mojo.setCount(this.filterCount);
			return;
		}
		
		if (this.filterString && this.filterAccountsToProcess && this.filterAccountsToProcess > 0) {
			// previous filter search is still running
			this.pendingFilterString = filterString;
			return;
		}
		
		this.startFolderSearch(filterString);
	},
	
	startFolderSearch: function(filterString) {
		this.filterCount = 0;
		this.filterString = filterString;
		this.filterAccountsToProcess = this.accountsModel.items.length + 1;
		
		// clear old subscription
		this.cleanupSubscriptions();
		// re-setup subscription with the new filter string
		this.setupSubscriptions();
	},
	
	handleFolderSearchResult: function(accountId, accountElem, count) {
		if (this.filterString === undefined) {
			Mojo.Log.info("folder list is initializing")
			// accounts scene is initializing
			return;
		} 
		
		//Mojo.Log.info("this.accountElemeStatus[%s]=%j, count=%d", accountId, this.accountElemState[accountId], count)
		if (!this.accountElemState[accountId]) {
			Mojo.Log.error("accounts-assistant: unable to handle folder search result "
				+ "since account element state for account=%s doesn't exist.", accountId);
			return;
		}
		
		var showed = this.accountElemState[accountId].showed;
		var expanded = this.accountElemState[accountId].expanded;
		var showFavoritesFlag = this.accountElemState[accountId].showFavoritesFlag;
		
		if (!this.filterString) {
			// filter search is cleared
			Mojo.Log.info("filter search is cleared")
			if (showed === false) {
				accountElem.show();
				Mojo.Log.info("showed account element for accountId=%s", accountId);
				this.accountElemState[accountId].showed = true;
			}
			
			if (expanded === true && showFavoritesFlag === false) {
				this.toggleShowHideFolders(accountElem, AccountsAssistant.kForceClose);
				//Mojo.Log.info("closed account element for accountId=%s", accountId);
				this.accountElemState[accountId].expanded = false;
			} else if (expanded === false && showFavoritesFlag === true) {
				this.toggleShowHideFolders(accountElem, AccountsAssistant.kForceOpen);
				//Mojo.Log.info("expanded account element for accountId=%s", accountId);
				this.accountElemState[accountId].expanded = true;				
			}
			//Mojo.Log.info("this.accountElemState[%s]=%j", accountId, this.accountElemState[accountId]);
			return; 
		}
		
		Mojo.Log.info("filter search in on with value=%s", this.filterString);
		if (count === 0) {
			if (showed === true) {
				accountElem.hide();
				this.accountElemState[accountId].showed = false;
				//Mojo.Log.info("showed account element for accountId=%s", accountId);
			}
		}
		else {
			if (showed === false) {
				accountElem.show();
				//Mojo.Log.info("showed account element for accountId=%s", accountId);
				this.accountElemState[accountId].showed = true;
			}
			
			if (expanded === false) {
				this.toggleShowHideFolders(accountElem, AccountsAssistant.kForceOpen);
				//Mojo.Log.info("expanded account element for accountId=%s", accountId);
				this.accountElemState[accountId].expanded = true;
			}
			//Mojo.Log.info("this.accountElemState[%s]=%j", accountId, this.accountElemState[accountId]);
		} 
				
		this.filterCount += count;
		this.filterAccountsToProcess--;
		if (this.filterAccountsToProcess === 0) {
			this.folderFilterField.mojo.setCount(this.filterCount);
			this.handlePendingFolderSearch();
		}
	},
	
	handlePendingFolderSearch: function() {
		if (this.pendingFilterString !== undefined) {
			var filterString = this.pendingFilterString;
			this.pendingFilterString = undefined;
			
			this.startFolderSearch(filterString);
		}
	},

	openFolder: function(element) {
		var accountElem = element.up('div.account_details');
		var folderObj = { folderId: parseInt(element.id),
		                  accountId: accountElem.id,
		                  login: accountElem.readAttribute('originallogin'),
						  domain: accountElem.readAttribute('accountdomain')
		                };
		this.stageController.pushScene('list', folderObj);
	},
	
	openFavoriteFolder: function(element) {
		var fid = element.id.substring(4); // get the folder ID from the element's id 
		var folderObj = { folderId: parseInt(fid),
		                  accountId: element.readAttribute('accountId')
		                };
		this.stageController.pushScene('list', folderObj);
	},
	
	toggleFavorites: function(element) {
		var favorite = element.hasClassName('starred');
		if (favorite == false) {
			element.addClassName('starred');
		} else {
			element.removeClassName('starred');
		}

		var folderRowDiv = this.controller.get(element).up('div.selection_target');
		// If the list extends below the bottom of the screen, then keep the current list
		// items in the scroller's viewport by setting an amount for it to scroll up/down
		// when the new item is inserted.
		var viewportOffset = this.accountsListElement.viewportOffset();
		var bottom = viewportOffset.top + this.accountsListElement.getHeight();
		if (bottom > this.controller.window.innerHeight) {
			this.favFolderScrollAdjust = (favorite == true) ? folderRowDiv.getHeight() : -folderRowDiv.getHeight();
		} else {
			this.favFolderScrollAdjust = 0;
		}
		// This forces favorite folder to update immediately when the service responds with the updated folder list
		this.folderResponseData[AccountsAssistant.kFavoriteFoldersAccountID] = undefined;
		var result = Folder.setFavorite(folderRowDiv.id, !favorite);
	},
	
	/* Used to update an Account folder's readCount manually when the same folder is
	 * updated in the favorite folders list. This will prevent any visible difference 
	 * between the two counts if both folders are shown on the screen at the same time.
 	 */ 
	updateFolderReadCount: function(folderID, readCount) {
		if (!folderID || !readCount) {
			Mojo.Log.info("No folder or count to update");
			return;
		}
		
		if (folderID === this.lastRCUpdateID) {
			// certain lists can call this method multiple times in rapid succession
			// use cached reference whenever possible
			this.lastRCUpdateDiv.innerHTML = readCount;
			return;
		}
		
		try {
			var unreadDiv = this.controller.get(folderID).select('.folder-list-unread').first();
			unreadDiv.innerHTML = readCount;
			// record our success
			this.lastRCUpdateDiv = unreadDiv;
			this.lastRCUpdateID = folderID;
		} catch (e) {
			// either folder not currently loaded, or All Inboxes folder was passed
			Mojo.Log.info("No matching folder found. Cannot update readCount");
		}
	},
	
	toggleShowHideFolders: function (rowElement, forceAction) {
		if (!rowElement.hasClassName("account_details")) {
			return;
		}

		var toggleButton = rowElement.down("div.arrow_button");
		if (!toggleButton.hasClassName('palm-arrow-expanded') && !toggleButton.hasClassName('palm-arrow-closed')) {
			return;
		}

		var showFavorites = toggleButton.hasClassName('palm-arrow-closed');
		var folderContainer = this.controller.get('account_folders_'+rowElement.id);
		var maxHeight = folderContainer.getHeight();
		if (showFavorites || forceAction === AccountsAssistant.kForceOpen) {
			toggleButton.addClassName('palm-arrow-expanded');
			toggleButton.removeClassName('palm-arrow-closed');
			folderContainer.setStyle({ height:'1px' });
			folderContainer.show();

			// See if the div should scroll up a little to show the contents
			var viewPortMidway = (this.stageController.window.innerHeight)*2/3;
			var elementTop = folderContainer.viewportOffset().top;
			var scroller = Mojo.View.getScrollerForElement(folderContainer);
			if (elementTop > viewPortMidway && scroller && !forceAction) {
				//Using setTimeout to give the animation time enough to give the div enough height to scroll to
				var scrollToPos = scroller.mojo.getScrollPosition().top - (elementTop - viewPortMidway);
				setTimeout(function() {scroller.mojo.scrollTo(undefined, scrollToPos, true);}, 200);
			}
		} else if (!showFavorites || forceAction === AccountsAssistant.kForceClose) {
			folderContainer.setStyle({ height: maxHeight + 'px' });
			toggleButton.addClassName('palm-arrow-closed');
			toggleButton.removeClassName('palm-arrow-expanded');
		}
		
		// update account element state
		if (!forceAction) {
			this.accountElemState[rowElement.id].showFavoritesFlag = showFavorites;
			this.accountElemState[rowElement.id].expanded = showFavorites;
		}
		
        var options = {reverse: !showFavorites,
					   onComplete: this.animationComplete.bind(this, showFavorites, rowElement.id, maxHeight, forceAction),
					   curve: 'over-easy',
					   from: 1,
					   to: maxHeight,
					   duration: 0.4};
		Mojo.Animation.animateStyle(folderContainer, 'height', 'bezier', options);
	},

	animationComplete: function(show, accountId, listHeight, forceAction, folderContainer, cancelled) {
		if (!show) {
			folderContainer.hide();
		}
		folderContainer.setStyle({height:'auto'});
		
		if (forceAction)
			// don't update preferences if in search mode
			return;
			
		// send the preferences change to the MailService after the animation since it causes the
		// accounts to be rebuilt which cancels the animation.
		if (accountId == AccountsAssistant.kFavoriteFoldersAccountID) {
			EmailAppDepot.depot.setShowFavorites(accountId, {'showFavorites':!show});
		} else {
			EmailAccount.saveAccountPreferences(this.controller, {'account': accountId, 'showFavoritesFlag': !show});
		}
	},

	depotGetFavExpanded: function(account) {
		var folderContainer = this.controller.get('account_folders_'+AccountsAssistant.kFavoriteFoldersAccountID);
		var toggleButton = folderContainer.parentElement.down("div.arrow_button");
		var maxHeight = folderContainer.getHeight();
		var showFavorites = false;
		
		// Show==true means collapse because it used to mean "show only favorites (and hide all else)"
		if (account && account.showFavorites) {
			toggleButton.addClassName('palm-arrow-closed');
			toggleButton.removeClassName('palm-arrow-expanded');
			folderContainer.hide();
			showFavorites = false;
		} else {
			toggleButton.addClassName('palm-arrow-expanded');
			toggleButton.removeClassName('palm-arrow-closed');
			folderContainer.show();
			showFavorites = true;
		}
		
		if (!this.accountElemState[AccountsAssistant.kFavoriteFoldersAccountID]) 
			this.accountElemState[AccountsAssistant.kFavoriteFoldersAccountID] = {
				"showFavoritesFlag": showFavorites,
				"showed": true,
				"expanded": showFavorites
			};
		else 
			this.accountElemState[AccountsAssistant.kFavoriteFoldersAccountID].showFavoritesFlag = showFavorites; 
    },

    setupSubscriptions: function() {
        this.enableSubscriptions();
    },
    cleanupSubscriptions: function() {
        this.disableSubscriptions();
    },
    enableSubscriptions: function() {
		//Mojo.Log.info('AccountsAssistant.enableSubscriptions()')
		// This ensures folders update immediately when the service responds with the updated folder list
		this.folderResponseData = {};

        if (!this.favSubscription) {
            this.favSubscription = 
                Folder.subscribeFavoriteFolderList(this.filterString, this.renderFavFolderListHandler);
        }

		// Resubscribe to any accounts now so unread counts update quickly.
        var that = this;
        Object.keys(this.folderSubscriptions).each(function (accountId) {
            if (that.folderSubscriptions[accountId] === undefined) {
				//Mojo.Log.info('AccountsAssistant.enableSubscriptions(): accountId=%s', accountId)
                that.folderSubscriptions[accountId] = Folder.subscribeAccountFolderList(accountId, that.filterString, that.renderFolderList.bind(that, accountId));
            }
        });
        if (!this.accountsRequest) {
            this.accountsRequest =
                EmailAccount.subscribeAccounts(this.renderAccountsHandler, this.accountsErrorHandler);
        }
    },
    disableSubscriptions: function() {
        if (this.accountsRequest) {
            this.accountsRequest.cancel();
            this.accountsRequest = undefined;
        }
        if (this.favSubscription) {
            this.favSubscription.cancel();
            this.favSubscription = undefined;
        }
        // Unsubscribe all folders lists:
        var folderSubs = this.folderSubscriptions;
        Object.keys(folderSubs).each(function (accountId) {
            if (folderSubs[accountId]) {
                folderSubs[accountId].cancel();
                folderSubs[accountId] = undefined;
            }
        });
    },
    maximizeSubscriptions: function() {
        this.enableSubscriptions();
		// When a multi-mail notification is tapped and email is in card view,
		// the stage could just be activated (ie, maximized).
		Mojo.Controller.getAppController().assistant.clearDebounce('accounts');		
    },
    minimizeSubscriptions: function() {
        this.disableSubscriptions();
    }

});


AccountsAssistant.kFavoriteFoldersAccountID = 'favorite_folders';
AccountsAssistant.kForceOpen = 'force_open';
AccountsAssistant.kForceClose = 'force_close';

AccountsAssistant.formatFolder = function(folder, filterString) {
	folder.useIcon = Folder.getFolderType(folder.use);
	folder.starred = FolderFlags.getFavoriteStatus(folder.flags);
	if (folder.unreadCount > 0) {
		folder.badgeDisplay = "inline";
		folder.hasUnread = "has-unread";
	} else {
		folder.badgeDisplay = "none";
	}

	if (FolderFlags.isNotSelectable(folder.flags)) {
		folder.noselect = "noselect";
	}

	// this allows for the standard folder names (Inbox, Drafts, Outbox, etc.) to be displayed localized
	// if the mail server doesn't send them in the appropriate language.
	if (folder.name) {
		folder.name = $L(folder.name);
		folder.name = folder.name.escapeHTML();
		folder.name = EmailDecorator.decorateFilterResult(folder.name, filterString);
	} else if (folder.folderName) {
		folder.folderName = $L(folder.folderName);
		folder.folderName = folder.folderName.escapeHTML();
		folder.folderName = EmailDecorator.decorateFilterResult(folder.folderName, filterString);
	}
}

AccountsAssistant.formatFavoriteFolders = function(folder, filterString){
	AccountsAssistant.formatFolder(folder, filterString);
	if (folder.accountName == ServiceStrings.strings.SmartFolders ||
	    folder.accountName == "Smart Folders") { //TODO remove this once mailservice starts using ServiceStrings
		folder.accountName = "";
		folder.favoriteFoldersType = "allinboxes-type";
	}
	else {
		folder.favoriteFoldersType = "normal-type";
		if (folder.accountName) {
			folder.accountName = EmailAccount.localizeAccountName(folder.accountName);
		}
	}
}

AccountsAssistant.folderListCache = new ObjCache(64);
