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

var ListAssistant = Class.create(App.Scene, {
 initialize : function($super, targetFolder, delaySetup) {
    $super(delaySetup); 
  	this.folderObj = targetFolder;
	this.folderSubscription = null;
	this.folderErrorCount = 0;
	
	// flags for throttling activation 
	this.activationCallback = null;
	this.renderedFolderDetails = false;
	this.activated = false;

	// Runtime state:
	this.numberOfDeletesSent = 0;
	this.pendingComposeActions = [];

	this.currentZoomItem = undefined;
	this.currentZoomItemPreview = undefined;
	this.currentZoomItemHeight = 0;
	this.minListItemHeight = undefined;

    // Handlers:
    this.renderFolderDetailsHandler = this.renderFolderDetails.bind(this);
    this.folderErrorHandler = this.folderError.bind(this);
	this.updateFolderStatusModelHandler = this.updateFolderStatusModel.bind(this)
	this.deleteResponseHandler = this.handleDeleteResponse.bind(this);
	this.gestureStartHandler = this.handleGestureStart.bind(this);
	this.gestureHandler = this.handleGesture.bind(this);
	this.handleFilterBound = this.handleFilter.bind(this);
	this.sortTypeResponseHandler = this.handleSortTypeResponse.bind(this);
 },

  setup: function() {	
	this.notificationAssistant = Mojo.Controller.getAppController().assistant.notificationAssistant;
	Folder.getSortType(
		this.folderObj.folderId, 
		this.sortTypeResponseHandler, 
		function() {
			Mojo.Log.info("Failed to get sort type for folder=%d", this.folderObj.folderId)
		}.bind(this));
		
	this.main = this.controller.get('main');
	this.emptyMessage = this.controller.get('empty-list-message');
	this.parentAssistant = AppAssistant.getSceneAssistant('accounts'); // used for updates to parent scene

	this.cmdMenuModel = {
			visible:true,
			items: [
				{label:$L('Compose'), icon:'compose', command:'compose'},
				{},
				{label:$L('Update'), icon:'sync', command:'sync'}
			]};
	this.controller.setupWidget(Mojo.Menu.commandMenu, undefined, this.cmdMenuModel);
	
	this.appMenuModel = {
		visible:true,
		label:$L('Folder'),
		items: [
			Mojo.Menu.editItem,
			{
				label:$L('Sort ...'),
				items: [
					ListAssistant.kSortByDateMenuItem, 
					ListAssistant.kSortBySenderMenuItem, 
					ListAssistant.kSortBySubjectMenuItem
			]},
			{label:$L('Preferences & Accounts'), command:Mojo.Menu.prefsCmd, disabled: false},
			{label:$L('Help'), command:Mojo.Menu.helpCmd, disabled:false}
		]};
	this.controller.setupWidget(Mojo.Menu.appMenu, {omitDefaultItems:true}, this.appMenuModel);
  },

  // NOTE: this is called by app_scene's _setup function
  // not really delayed...just called immediately after setup.
  setupDelayed: function() {
	// service requests
  	this.dataSource = new EmaillistDataSource(this.folderObj.folderId, 5, this.checkEmptyList.bind(this), this);
  	this.accDetailsReq = Folder.getFolderAccountDetail(this.folderObj.folderId, this.renderFolderHeader.bind(this));
	this.emailListElement = this.controller.get('email_list');
  	this.folderElement = this.controller.get('folder');
  	this.spacerListDiv = this.controller.get('spacerListScene');

    // Set up the attributes & model for this widget:
	this.listAttrs = {
					lookahead: 32,
					renderLimit: 16,
					fixedHeightItems:true,
					itemTemplate: 'list/email_entry',
					swipeToDelete: true,
					autoconfirmDelete: true,
					uniquenessProperty: 'id',
					dividerTemplate: 'list/date_separator',
					dividerFunction: function(email) { return email.category; },
					filterFunction: this.fetchItems.bind(this),
					optimizedOptIn: true,
					hasNoWidgets: true,
 					delay: 500
		};
	this.controller.setupWidget('email_list', this.listAttrs, {});
	this.controller.instantiateChildWidgets(this.main);
	
	// Folder header observer
	this.folderElement.observe(Mojo.Event.tap, this.headerTapped.bind(this));
	
	// List observers
 	this.emailListElement.observe(Mojo.Event.listTap, this.handleClick.bindAsEventListener(this));
 	this.emailListElement.observe(Mojo.Event.listDelete, this.handleDelete.bindAsEventListener(this));
	this.emailListElement.observe(Mojo.Event.filterImmediate, this.handleFilterChange.bind(this));
	this.controller.listen('email_list', Mojo.Event.filter, this.handleFilterBound, true);	
	//this.emailListElement.observe("gesturestart", this.gestureStartHandler, true);
 	//this.emailListElement.observe("gesturechange", this.gestureHandler, true)
  },
  
  handleFilterChange: function(event) {
		var filter = event.filterString;			

		if (filter){
			this.spacerListDiv.hide();
		} else {
			this.spacerListDiv.show();
		}
    
  },

  isListSynced: function() {
  	return (this.firstSync !== undefined);
  },

  cleanup: function() {
  	if (this.emailListElement) {
		this.emailListElement.stopObserving(Mojo.Event.listTap, this);
		this.emailListElement.stopObserving(Mojo.Event.listDelete, this.handleDelete);
	}

	this.dataSource.cleanup();
	Folder.stopSyncFolder(this.folderObj.folderId);	
	if (this.spinExecutor) {
		this.spinExecutor.cancel(); //NOTE: this causes spinCleanup() to be executed
	}
	
	this.controller.stopListening('email_list', Mojo.Event.filter, this.handleFilterBound);
 },

  activate: function() {
		// If the scene is invalid (usually because the underlying account was deleted), 
		// just pop the scene and no more.
  		if (this.popOnActivate === true) {
			this.controller.stageController.popScene();
			return;
		}
		
		this.pauseSpinner = false;
		// Tell the notification assistant that we're in a folder
		// In the case of all inboxes, always ignore new email notifications  
		if (this.setIgnoreNewEmails === true) {
			this.notificationAssistant.setIgnoreNewEmails(true);
			this.notificationAssistant.clear();
		} else {
			this.notificationAssistant.clear(this.folderObj.accountId, this.folderObj.folderId);
		}
		
		if (this.dataSource) {
			// recalculate date start, in case app has been open overnight
			this.dataSource.checkReload();
			// fire off timer to go on the hours
			this.checkReload = function () {
				try {
					this.dataSource.checkReload();
					this.reloadTimer = setTimeout(this.checkReload, 3600000);
				} catch (e) {
					Mojo.Log.info("Timer cancelled");
				}
			}.bind(this);
			// start the timer's initial fire on the hour. 
			var startTimerMinutes = (60 - (new Date().getMinutes())) * 60 * 1000;
			this.reloadTimer = setTimeout(this.checkReload, startTimerMinutes);
		}
		
		// Delay the request for sync until late in the setup because it makes imap transport 
		// do a lot of busy work that gets in the way of returning the list of existing emails. 
		if (this.firstSync === undefined) {
			this.firstSync = true;
			// Since the user is viewing this folder, request a sync right now.
			Folder.syncFolder(this.folderObj.folderId, false);
		}
		
		Mojo.Controller.getAppController().assistant.clearDebounce('f'+this.folderObj.folderId);	
  },

	aboutToActivate: function(callback) {
		if (!this.renderedFolderDetails) {
			// rendering now would cause ui strangeness. Wait a bit.
			this.activationCallback = callback;
			return;
		}
		
		callback();
		this.activated = true;
	},

	deactivate: function() {
  		this.pauseSpinner = true;
		this.notificationAssistant.setIgnoreNewEmails(false);
		if (this.reloadTimer) {
			clearTimeout(this.reloadTimer);
			this.reloadTimer = undefined;
		}
	},

	// This is called from accountpreference assistant when the user removes the account
	accountDeletedNotification: function(accountId) {
		if (accountId === this.folderObj.accountId) {
			Mojo.Log.warn("ListAssistant is showing a deleted account, setting up for cleanup");
			this.popOnActivate = true;
		}
	},

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

  /**
   * handle a menu command.
   */
  handleCommand: function(event) {
	if (event.type == Mojo.Event.command) {
		switch (event.command) {
			case 'compose':
				this.handleSend();
				break;

			case 'sync':
				this.handleSyncFolder();
				break;
			
			case ListAssistant.kSortByDateMenuItem.command:
				this.handleSortByDate();
				break;
			
			case ListAssistant.kSortBySenderMenuItem.command:
				this.handleSortBySender();
				break;
				break;
			
			case ListAssistant.kSortBySubjectMenuItem.command:
				this.handleSortBySubject();
				break;
		
			case 'empty-folder':
				this.handleEmptyTrashFolder();
				break;

			case Mojo.Menu.prefsCmd:
				MenuController.showPrefs(this.controller.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();
	}
  },
	
  handleSend: function() {
  	var accountId = this.folderObj.accountId;
	if (accountId && accountId !== 'single') {
		this.doSend();
	} else {
		this.pendingComposeActions.push(this.doSend.bind(this))
	}
  },
  
  doSend: function() {
  	MenuController.showComposeView(null, this.folderObj.login, this.controller, this.cmdMenuModel);
  },
	
  handleSyncFolder: function() {
	Folder.syncFolder(this.folderObj.folderId, true);
	// Fake a single cycle of sync animation to make the HI guys happy
	this.simulateSyncAnimation();
  },
	
	handleSortByDate: function() {
		var sortType = undefined;
		if (this.folderObj.sortType === Folder.SORT_BY_DATE_ASC
			|| this.folderObj.sortType === Folder.SORT_BY_SENDER_ASC
			|| this.folderObj.sortType === Folder.SORT_BY_SENDER_DESC
			|| this.folderObj.sortType === Folder.SORT_BY_SUBJECT_ASC
			|| this.folderObj.sortType === Folder.SORT_BY_SUBJECT_DESC)
			sortType = Folder.SORT_BY_DATE_DESC;
		else  
			sortType = Folder.SORT_BY_DATE_ASC;
			
		this.setFolderSortType(sortType);
		this.setSortMenuItemIcon(ListAssistant.kSortByDateMenuItemIndex, sortType);
		this.handleSortOrder(sortType);
	},
	
	handleSortBySender: function() {
		var sortType = undefined;
		if (this.folderObj.sortType === Folder.SORT_BY_SENDER_DESC
			|| this.folderObj.sortType === Folder.SORT_BY_DATE_ASC
			|| this.folderObj.sortType === Folder.SORT_BY_DATE_DESC
			|| this.folderObj.sortType === Folder.SORT_BY_SUBJECT_ASC
			|| this.folderObj.sortType === Folder.SORT_BY_SUBJECT_DESC)
			sortType = Folder.SORT_BY_SENDER_ASC;
		else 
			sortType = Folder.SORT_BY_SENDER_DESC;
			
		this.setFolderSortType(sortType);
		this.setSortMenuItemIcon(ListAssistant.kSortBySenderMenuItemIndex, sortType);
		this.handleSortOrder(sortType);
	},
	
	handleSortBySubject: function() {
		var sortType = undefined;
		if (this.folderObj.sortType === Folder.SORT_BY_SUBJECT_DESC
			|| this.folderObj.sortType === Folder.SORT_BY_DATE_ASC
			|| this.folderObj.sortType === Folder.SORT_BY_DATE_DESC
			|| this.folderObj.sortType === Folder.SORT_BY_SENDER_ASC
			|| this.folderObj.sortType === Folder.SORT_BY_SENDER_DESC)
			sortType = Folder.SORT_BY_SUBJECT_ASC;
		else 
			sortType = Folder.SORT_BY_SUBJECT_DESC;
			
		this.setFolderSortType(sortType);
		this.setSortMenuItemIcon(ListAssistant.kSortBySubjectMenuItemIndex, sortType);
		this.handleSortOrder(sortType);
	},
	
	handleSortOrder: function(sortType) {
		if (this.folderObj.sortType === sortType)
			return; 
			
		Mojo.Log.info("Setting sort order to %s", sortType);
		this.folderObj.sortType = sortType;
		this.listAttrs.dividerTemplate = 
				(sortType === Folder.SORT_BY_SENDER_ASC 
				 || sortType === Folder.SORT_BY_SENDER_DESC 
				 || sortType === Folder.SORT_BY_SUBJECT_ASC
				 || sortType === Folder.SORT_BY_SUBJECT_DESC) 
				? 'list/sender_separator' : 'list/date_separator';
		this.dataSource.setSortOrder(sortType);
		this.dataSource.disableSubscription();
		this.dataSource.enableSubscription();
		this.listWidget.mojo.invalidateItems(0);
	},
	
	handleSortTypeResponse: function(resp) {
		this.handleSortOrder(resp.sortType);
	},
	
	setSortMenuItemIcon: function(itemIndex, sortType) {
		/*
		var sortMenuItems = this.appMenuModel.items[ListAssistant.kSortMenuItemIndex].items;
		if (sortType === Folder.SORT_BY_DATE_ASC || sortType === Folder.SORT_BY_SENDER_ASC || sortType === Folder.SORT_BY_SUBJECT_ASC)
			sortMenuItems[itemIndex].icon = 'sort-menu-icon ascending';
		else 
			sortMenuItems[itemIndex].icon = 'sort-menu-icon descending';
		
		for (var ndx = 0, len = sortMenuItems.length; ndx < len; ndx++) 
			if (ndx != itemIndex) 
				sortMenuItems[ndx].icon = undefined;
		*/
	},
	
	setFolderSortType: function(value) {
		Folder.setSortType(this.folderObj.folderId, value);
	},
	
  	handleEmptyTrashFolder: function() {
		this.controller.showAlertDialog({
			onChoose: function(value) {
				if (value) {
					Folder.emptyFolder(this.folderObj.folderId);
				} 
			},
			title: $L("Empty Trash?"),
			message: $L("Are you sure you want to permanently delete these items?"),
			choices: [
				{label: $L("Empty Trash"), value: true,  type: 'negative'},
				{label: $L("Cancel"),      value: false, type: 'color'}
			]
		});
  	},

	folderStatusModel: [{}],

	handleGestureStart: function(e) {
		var element = undefined;
		Event.stop(e);
		if (e.srcElement.id) {
			element = e.srcElement;
		} else {
			element = e.srcElement.parentElement;
			while (element.className.indexOf('email-entry') === -1) {
				element = element.parentElement;
			}
		}

		if (element === undefined || !element.id) {
			this.currentZoomItem = undefined;
		} else {
			if (this.minListItemHeight === undefined) {
				this.minListItemHeight = element.getHeight();
			}

			// keep zooming the current element, otherwise switch to a different element 
			if (element === this.currentZoomItem) {
				this.currentZoomItemHeight = element.getHeight();
			} else {
				this.currentZoomItem = element;
				this.currentZoomItemPreview = element.down('.list-extended-preview');
				this.currentZoomItemPreview.addClassName('zoomed');
				this.currentZoomItemHeight = element.getHeight();

				// Only request the message text for a list item once.
				if (this.currentZoomItemPreview.innerHTML === "") {
					Message.getMessageText(this.controller, element.id,
							function(resp) { element.down('.list-extended-preview').update(palmGetResource(resp.text)); },
							function() { Mojo.Log.error("messageFileText failed for id", element.id); });
				}				
			}
		}
	},

	handleGesture: function(e) {
		if (this.currentZoomItem !== undefined) {
			var element;
			Event.stop(e);
			var newHeight = this.currentZoomItemHeight * e.scale;
			if (newHeight < this.minListItemHeight) {
				this.currentZoomItem.removeClassName('zoomed');
			} else {
				this.currentZoomItem.addClassName('zoomed');
				this.currentZoomItem.setStyle({ height: newHeight + 'px' });
				this.currentZoomItemPreview.setStyle({ height: (newHeight-54) + 'px' });
			}
		}
	},
	
	handleFilter: function(event) {
		var filterString = event.filterString;
		Mojo.Log.info("ListAssistant.handleFilter(): filter=" + filterString);
		
		if (this.filter !== filterString) {
			this.disableSubscriptions();
			this.filter = filterString;
			this.dataSource.setMessagesFilter(this.filter);
			this.enableSubscriptions();
		} 
	},
	
	fetchItems: function(filter, listWidget, offset, limit) {
		Mojo.Log.info("ListAssistant.fetchItems(): filter=%s, offset=%d, limit=%d", filter, offset, limit);
		this.dataSource.updateOffsetAndLimit(listWidget, offset, limit);
		if (!this.listWidget)
			this.listWidget = listWidget;
	},

	// kicks off folder and header rendering. Actual rendering of both
	// happens through service calls in renderFolderDetails method
	renderFolderHeader: function(folder) {
		
		// Make sure the folderObj includes these bits of info
		this.folderObj.domain = folder.domain;
		this.folderObj.login = folder.login;
		this.folderObj.accountId = folder.accountId;

		// fire off request
		this.renderFolderDetails({record:folder});
		
		// do non request dependent items
		this.accDetailsReq = null;
		this.folderType = folder.use;
		this.renderEmptyFolderMessage(folder); 
		
		// handle pending compose actions if there are any
		this.pendingComposeActions.each(function(func) {
			func.apply();
		});
  	},
	
	updateItemAtIndex: function(listIndex) {
		if (!this.isValidListIndex(listIndex)) {
			return;
		}
		this.listWidget.mojo.invalidateItems(listIndex, 1);
	},
	
	removeItemAtIndex: function(listIndex) {
		if (!this.isValidListIndex(listIndex)) {
			return;
		}
		this.listWidget.mojo.noticeRemovedItems(listIndex, 1);
	},
	
	isValidListIndex: function(listIndex) {
		if (!this.listWidget || listIndex === undefined || listIndex < 0 || this.listWidget.mojo.getLength() < listIndex) {
			return false;
		}
		return true;
	},

  renderFolderDetails: function(folder) {
  	if (!folder) {
		return;
	}
	var record = folder.record;
	if (record.use === "TRASH" && !this.emptyTrashReady) {
		this.showEmptyTrashMenu();
	}
	
  	// The subscription is successful so reset the error count.
  	this.folderErrorCount = 0;
	
	// Folder details have been updated so get folder sync status.
	// Note, this may become unsafe if the transports start sending up
	// folder details multiple times per second.
	Folder.folderStatus(this.folderObj.folderId, this.filter, this.updateFolderStatusModelHandler);
	
	// The All Inboxes folder get a response with a list of folder details.
	// Aggregate this into a single folder record.
	if (folder.list) {
		folder.record = {
			unreadCount:0,
			syncStatus:''
		};
		record = folder.record; // update shortcut
		// If any of the accounts is in the process of syncing, then set the
		// aggregate syncStatus so inboxes show the animation
		folder.list.each(function(f) {
			record.unreadCount += f.unreadCount;
			if (f.syncStatus && (f.syncStatus === "Syncing..." || f.syncStatus === "Receiving changes...")) {
				record.syncStatus = f.syncStatus;
			}
		});
	}

  	record.domain = FolderFlags.getFolderHeaderIcon(this.folderObj.domain);
	if (record.unreadCount > 0) {
		record.badgeDisplay = "block";
	} else {
		record.badgeDisplay = "none";
	}

	// Only want to create this once, otherwise it will cause flickering in the sync animation
	if (this.headerContent === undefined) {
		if (record.name) {
			record.name = $L(record.name).escapeHTML();
		}

		// If the login is null then this is one of the synthetic folders (all inboxes or all unread)
		if (record.login === null) {
			record.domain = "synthetic-folder";
		}

	  	this.headerContent = Mojo.View.render({template: 'list/header_entry', object: record});
		this.folderElement.update(this.headerContent);
		this.animationDiv = this.controller.get('sync-activity-animation');
	} 
	
	if (!this.filter) {
		// Update unread count if filter string is not present; otherwise,
		// the unread count will be set in in processFolderStatus() function.
		this.updateUnreadCount(record);
	}
	Mojo.Log.info("syncStatus="+folder.record.syncStatus);
	if (record.syncStatus &&
		(record.syncStatus === "Syncing..." || record.syncStatus === "Receiving changes...")) {
		this.startSyncAnimation();
	} else {
		//"Synced at "
		//"Watching..." / "Waiting for new mail..."
		//"Login failed..."
		this.requestStopSyncAnimation();
	}
	
	// update app menu with the appropriate sort preference.
	var menuItemIndex = ListAssistant.kSortByDateMenuItemIndex;

	if (record.sortType === Folder.SORT_BY_SENDER_ASC ||
			record.sortType === Folder.SORT_BY_SENDER_DESC) {
		menuItemIndex = ListAssistant.kSortBySenderMenuItemIndex;
	}
	else 
		if (record.sortType === Folder.SORT_BY_SUBJECT_ASC ||
				record.sortType === Folder.SORT_BY_SUBJECT_DESC) {
			menuItemIndex = ListAssistant.kSortBySubjectMenuItemIndex;
		}
	this.setSortMenuItemIcon(menuItemIndex, record.sortType);
  },
  
  renderEmptyFolderMessage: function(folder) {
  	var emptyMessageSpan = this.controller.get('empty-list-message-span');
	
	if (folder.loginFailed) {
		emptyMessageSpan.innerHTML = "";
		return;
	}
		
	var lookbackInterval = folder.syncLookback; 
	if (!lookbackInterval || lookbackInterval <= 0) {
		emptyMessageSpan.innerHTML = $L("No messages in this folder.");
	} else if (lookbackInterval === 1 || (folder.domain === "eas" && folder.use === "TRASH")){
		// eas deleted folders are hardcoded to look back only one day. Indicated by 'use' property
		emptyMessageSpan.innerHTML = $L("No messages in the last day.");
	} else if (lookbackInterval <= 7){
		emptyMessageSpan.innerHTML = $L("No messages in the last #{interval} days.").interpolate({interval: lookbackInterval});
	} else if (lookbackInterval < 30) {
		emptyMessageSpan.innerHTML = $L("No messages in the last #{interval} weeks.").interpolate({interval: Math.floor(lookbackInterval / 7)});
	} else {
		emptyMessageSpan.innerHTML = $L("No messages in the last month.");
	}
  },
  
  // function to handle restraint of aboutToActivate callback until initial folder rendering has completed
  // this ensures that the header and initial list entries have been rendered before the screen is activated
  checkActivation: function() {
	if (!this.activated && this.activationCallback) {
		this.activationCallback();
		this.activationCallback = null;
	}
  },
	
	showEmptyTrashMenu: function() {
		Mojo.Log.info("showing empty trash menu")
		var trashMenuItem = {
			label: $L('Empty Trash'), command: 'empty-folder'
		};
		this.appMenuModel.items.splice(2, 0, trashMenuItem);
		this.controller.modelChanged(this.appMenuModel);
		this.emptyTrashReady = true;
	},

	headerTapped: function (event) {
		if (this.folderStatusModel.length > 1 || this.folderStatusModel[0].label !== undefined) {
			var params = { 
				placeNear:event.target, 
				items:this.folderStatusModel, 
				onChoose:this.handleStatusPopupSelect.bind(this)
			};
			this.controller.popupSubmenu(params);
		}
	},

  handleClick: function (event) {
	var id = event.item.id;
	if (id) {
		var flags = event.item.flags;
		if (this.folderType === "DRAFTS" 
				|| (this.folderType === "OUTBOX" && ((flags & EmailFlags.errorBit) !== 0))) {
			// draft emails and emails stuck in outbox that is marked as error
			// will be able to be editable when it is clicked.
			this.controller.serviceRequest('palm://com.palm.applicationManager', {
                    method: 'open',
                    parameters: {
						id: 'com.palm.app.email',
						params: { "draftId": id }
					}
                });
		} else {
			// allow the filter string to be applied to navigating previous/next email
			// in message scene
			event.item.folderFilter = this.filter;
			event.item.offset = event.index;
			this.controller.stageController.pushScene('message', id, this.folderObj.folderId, undefined, event.item);
		}
	}
  },

  handleDelete: function (event) {
	Mojo.Log.info("ListAssistant: setDeleted ", event.item.id);
	Message.setDeleted(event.item.id, true, this.deleteResponseHandler, this.deleteResponseHandler);
	
	// Disable throttler requests while data is being deleted.  The data may be stale
	// we know that the service has done executing the delete.
	if (this.numberOfDeletesSent === 0) {
		this.dataSource.throttler.disable();
	}
	this.numberOfDeletesSent++;
	Mojo.Log.info("ListAssistant.handleDelete ref count=", this.numberOfDeletesSent);
  },

  handleDeleteResponse: function (event) {
	this.numberOfDeletesSent--;
	Mojo.Log.info("ListAssistant.handleDeleteResponse ref count=", this.numberOfDeletesSent);
	if (this.numberOfDeletesSent <= 0) { // Defensive check.
		// Time to re-enable the throttler now:
		this.dataSource.throttler.enable(false);
		this.numberOfDeletesSent = 0; // in case there we get repeated deleted messages.
	}
  },
  	updateUnreadCount: function(record) {
		var unreadCount = record.unreadCount;
		if (this.headerContent && this.unreadCountLength) {
			this.unreadElement = this.controller.get('folder-header-unread');
			if (this.unreadElement) {
				this.unreadElement.removeClassName(this.unreadCountLength);
			}
		}
		
		if (!this.filter && this.parentAssistant) {
			// not a filtered list. update unread count for parent scene
			this.parentAssistant.updateFolderReadCount(record.id, unreadCount);
		}

		var prevUnreadCountLength = this.unreadCountLength;
		if (unreadCount == 0) {
			this.unreadCountLength = "count0";
			this.lastFrameSync = 10;
			this.lastFrameStop = 11;
		} else if (unreadCount < 10) {
			this.unreadCountLength = "count1";
			this.lastFrameSync = 10;
			this.lastFrameStop = 18;
		} else if (unreadCount < 100) {
			this.unreadCountLength = "count2";
			this.lastFrameSync = 12;
			this.lastFrameStop = 20;
		} else if (unreadCount < 1000) {
			this.unreadCountLength = "count3";
			this.lastFrameSync = 14;
			this.lastFrameStop = 22;
		} else {
			this.unreadCountLength = "count4";
			this.lastFrameSync = 16;
			this.lastFrameStop = 24;
		}

		if (this.unreadElement) {
			this.unreadElement.addClassName(this.unreadCountLength);
			this.unreadElement.update(unreadCount);
		
			if (unreadCount > 0) {
				this.unreadElement.show();
				
				this.animationDiv.show();
				if (prevUnreadCountLength) {
					this.animationDiv.removeClassName(prevUnreadCountLength);
				}
				this.animationDiv.addClassName(this.unreadCountLength);
			} else {
				this.unreadElement.hide();
				
				// If the animation is currently going, don't hide it.
				if (!this.spinExecutor) {
					this.animationDiv.hide();
				}
			}
		}
	},

  updateFolderStatusModel: function(response) {
	if (!response.folders) {
		Mojo.Log.warn("updateFolderStatusModel got unexpected response ", Object.toJSON(response))
		return;
	}

	// Don't show notifications for All Unread or any of the other synthetic folders.
	if (response.folders.length > 1 && this.isActivated === true && this.isVisible === true && this.isMaximized === true) {
		this.setIgnoreNewEmails = true;
		if (this.notificationAssistant === undefined) {
			this.notificationAssistant = Mojo.Controller.getAppController().assistant.notificationAssistant;
		}
		this.notificationAssistant.setIgnoreNewEmails(this.setIgnoreNewEmails);
		this.notificationAssistant.clear();
	}

	this.folderStatusModel = [];
	var isError = false;
	var f = function(folder) {
		if (this.processFolderStatus(folder) === true) {
			isError = true;
		}
	}.bind(this);
	response.folders.each(f);
	// Set the error state if any of the accounts has an error.
	this.showErrorState(isError);
	this.renderedFolderDetails = true;
	this.checkActivation();
  },

	processFolderStatus: function(folder) {
		var formattedDate;
		var isError = false;
		var lastUpdatedStr = "";
		var totalEmails = "";
		var lastSyncTime = folder.lastSyncTime;
		var popupItemCommand = 'noop';
		if (Object.isString(lastSyncTime)) {
			lastSyncTime = parseInt(lastSyncTime);
		}
		if (lastSyncTime > 0) {
			formattedDate = ListAssistant.syncDate(lastSyncTime);
			lastUpdatedStr = $L("Updated #{date}<br>").interpolate({date:formattedDate});
		} else {
			lastUpdatedStr = $L("Not yet updated") + "<br>";
		}

		if (folder.totalCount) {
			totalEmails = $L("Total emails <b>#{totalCount}</b>").interpolate(folder);
		}

		var nextUpdateStr;
		if (folder.syncType === undefined) {
			// If no syncType, then this must be the just the total count
			nextUpdateStr = "";
			if (folder.totalCount) {
				folder.domain = "generic";
				lastUpdatedStr = "";
			}
		} else {
			var syncType = (folder.syncType & 0xF); // Strip off unwanted status flags 
			if (syncType == Folder.SYNC_SCHEDULED) {
				if (folder.nextSyncTime === undefined) {
					nextUpdateStr = "";
					Mojo.Log.error("nextSyncTime missing ", $H(folder).inspect());
				} else {
					var minutes = Math.round(folder.nextSyncTime/60);
					nextUpdateStr = $L("Next update in #{nextSync} minutes<br>").interpolate({nextSync:minutes});
				}
			} else if (syncType == Folder.SYNC_ONLINE || syncType == Folder.SYNC_NEW) {
				if (lastSyncTime == 0) {
					Mojo.Log.warn("WARNING: Folder has lastSyncTime == 0, using now")
					var now = new Date();
					formattedDate = ListAssistant.syncDate(now.getTime());
					lastUpdatedStr = $L("Updated #{date}<br>").interpolate({date:formattedDate});
				}
				nextUpdateStr = "";
			} else if (syncType == Folder.SYNC_OFFLINE) {
				nextUpdateStr = $L("Press sync button to update") + "<br>";
			} else {
				nextUpdateStr = "";
				totalEmails = "";
				lastUpdatedStr = $L("No update information");
			}
		}

		/*
		 * First display any errors related to the folder
		 */
		var errorText;
		switch (folder.syncStatus) {
		case Folder.STATUS_LOGIN_FAILED:
			var handleAction = ErrorAnalyzer.analyzeLoginError({"reason": folder.syncFailedReason});
			isError = true;

			if (handleAction.action === ErrorAnalyzer.kUpdateCredentialLEAction) {
				if (handleAction.reason) 
					errorText = $L("<b>Unable To Sign In</b><br> Message from server: \"#{reason}\"").interpolate(handleAction);
				else 
					errorText = $L("<b>Login credentials rejected</b><br> Update your password");
				var account = folder.account || this.folderObj.accountId;
				popupItemCommand = "loginfailed-" + account;
			} else {
				errorText = $L("<b>Your Account Is Locked</b><br>  To unlock your account, you must sign into it via the web.  Tap to sign in.");
				popupItemCommand = "weblogin-" + handleAction.url;
			}
			
			break;

		// This was added specifically for live.com which can lock out a user for 15 minutes,
		// but could be used by other servers that have similar behavior
		case Folder.STATUS_ACCOUNT_UNAVAILABLE:
			isError = true;
			errorText = $L("The account is temporarily unavailable. Please try again in a few minutes.");
			break;

		case Folder.STATUS_TRANSIENT_ERROR:
			isError = true;
			Mojo.Log.error("Got transient error from transport")
			break;

		default:
			isError = false;
		}

		/*
		 * Second display status text for the folder
		 */
		var renderParams = {
			template: 'list/folder_details',
			object: {
				accountName: folder.accountName,
				folderStatusText: lastUpdatedStr + nextUpdateStr + totalEmails,
				errorText: errorText
			}
		};
		var statusModelEntry = {
			label: Mojo.View.render(renderParams),
 			command: popupItemCommand
		};
		if (folder.domain) {
			statusModelEntry.secondaryIcon = "folder-status-icon "+folder.domain;
		}
	  	this.folderStatusModel.push(statusModelEntry);
		
		// Update unread count and adjust the unread count pill appearance and sync 
		// animation width
		if (folder.unreadCount !== undefined) {
			this.updateUnreadCount(folder);
		}
		
		return isError;
	},

	showErrorState: function(isError) {
		//var unreadElement = this.controller.get('sync-activity-animation');
		var warningIcon = this.controller.get('header-warning-icon');
		if (isError) {
			this.isError = true;
			//unreadElement.hide();
			warningIcon.show();
		} else {
			this.isError = false;
			//unreadElement.show();
			warningIcon.hide();
		}
	},

	folderError: function(err) {
		// If there was a serious error that knocked the mailservice off dbus, stop
		// the sync animation otherwise it may go on forever.
		this.requestStopSyncAnimation();

		// Try relaunching the mail service 3 times before giving up
		this.folderErrorCount++;
		if (this.folderErrorCount < 3) {
			Mojo.Log.warn("list folderErrorCount=" + this.folderErrorCount + " detail=" + $H(err).inspect());
			this.folderSubscription.cancel();
		 	this.folderSubscription =
                Folder.subscribeFolderDetail(this.folderObj.folderId,
											 this.filter,
                                             this.renderFolderDetailsHandler,
                                             this.folderErrorHandler);
		} else {
			Mojo.Log.error("This Is Bad: list cannot maintain folderList subscription " + $H(err).inspect());
		}
	},

	handleStatusPopupSelect: function(value) {
		if (value) {
			var a = value.split("-");
			if (a[0] === "loginfailed") {
				var accountId = a[1];
				this.controller.stageController.popScene('list');
				this.controller.stageController.pushScene('create', { edit:true, id:accountId, type:"email" });
			} else if (a[0] === "weblogin") {
				var loginUrl = a[1];
				new Mojo.Service.Request('palm://com.palm.applicationManager', {
					method: 'open',
					parameters: {
						'id': 'com.palm.app.browser',
						'params': {scene: 'page', url: loginUrl}
					}
				});
			}
		}
	},

	simulateSyncAnimation: function() {
		if (this.spinExecutor) {
			Mojo.Log.info("simulatedSyncAnimation: already spinning");
		} else {
			this.unreadElement = this.controller.get('folder-header-unread');
			this.unreadElement.removeClassName("inside-pill");
			this.currentFrame = 0;
			var spinTimeout = 7200; // 2 hour is a long time for the spinner to go for syncing large email box.
			this.frameToDraw = Mojo.Animation.targetFPS/10;
	        var options = {onComplete: this.spinCleanup.bind(this),
						   from: 0,
						   to: 128,
						   duration: spinTimeout};
			this.spinExecutor = Mojo.Animation.animateValue(Mojo.Animation.queueForElement(this.animationDiv),
															'linear',
															this.spinSyncAnimation.bind(this),
															options);
			this.animationDiv.show();
		}
	},

	startSyncAnimation: function() {
		if (this.spinExecutor) {
			Mojo.Log.info("startSyncAnimation: already spinning");
			this.continueAnimation = true;
		} else {
			Mojo.Log.info("startSyncAnimation: spinning up");

			this.unreadElement = this.controller.get('folder-header-unread');
			this.unreadElement.removeClassName("inside-pill");
			this.currentFrame = 0;
			this.continueAnimation = true;
			var spinTimeout = 900; // 15 minutes is a long time for the spinner to go.
			this.frameToDraw = Mojo.Animation.targetFPS/10;
	        var options = {onComplete: this.spinCleanup.bind(this),
						   from: 0,
						   to: 128,
						   duration: spinTimeout};
			this.spinExecutor = Mojo.Animation.animateValue(Mojo.Animation.queueForElement(this.animationDiv),
															'linear',
															this.spinSyncAnimation.bind(this),
															options);
			this.animationDiv.show();
		}
	},

	/*
	 * The animation needs to complete its current cycle before stopping so
	 * this only sets continueAnimation to false 
	 */
	requestStopSyncAnimation: function() {
		Mojo.Log.info("requestStopSyncAnimation");
		this.continueAnimation = false;
	},

	spinSyncAnimation: function(value) {
		if (this.pauseSpinner) {
			if (!this.continueAnimation) {
				// Stop the spin animation
				this.stopSyncAnimation();
			}
			return;
		}

		// Only want to udate the animation every 1/10 of a second
		if (this.frameToDraw < Mojo.Animation.targetFPS/10) {
			this.frameToDraw += 1;
			return; // EXIT out because this is an intermediate call
		} else {
			this.frameToDraw = 0;
		}

		var prevFrame = this.currentFrame;
		this.currentFrame += 1;

		if (this.currentFrame > this.lastFrameSync) {
			// At the end of the animation cycle. If the animation should not continue,
			// stop the spinExecutor
			if (this.continueAnimation) {
				this.currentFrame = 1;
			} else {
				// When sync is done, there's a few frames of ending animation to display
				if (this.currentFrame > this.lastFrameStop) {
					// Stop the spin animation
					this.stopSyncAnimation();
				}
			}
		}

		if (this.animationDiv && this.unreadElement) {
			if (this.spinExecutor) {
				this.animationDiv.addClassName("frame" + this.currentFrame);
				this.unreadElement.addClassName("frame" + this.currentFrame);
			}
			this.animationDiv.removeClassName("frame" + prevFrame);
			this.unreadElement.removeClassName("frame" + prevFrame);
		}
	},

	stopSyncAnimation: function() {
		Mojo.Log.info("stopSyncAnimation");
		if (this.spinExecutor) {
			this.spinExecutor.cancel(); //NOTE: this causes spinCleanup() to be executed
			this.spinExecutor = null;
		}
	},

	spinCleanup: function() {
        this.animationDiv.removeClassName("frame" + this.currentFrame);
		this.unreadElement.removeClassName("frame" + this.currentFrame);
		this.unreadElement.addClassName("inside-pill");
		if (this.unreadCountLength == "count0") {
			this.animationDiv.hide();
		}

		// Since a synchronization just finished, check whether the list is empty.
		// The delay is necessary because the list data source also has a 1 second
		// delay before it updates its count.
		this.checkEmptyList.bind(this).delay(1);

		Mojo.Log.info("stopSyncAnimation done!");
    },

    checkEmptyList: function() {
		if (this.controller === undefined) {
			return;
		}

		// Don't show the empty message if the transport is in the middle of syncing (continueAnimation is set).
		// But immediately hide the empty message whenever the list has items in it irrespective of syncing state.
		var listLength = this.dataSource.getListLength();
		if (listLength === 0) {
			if (this.continueAnimation === false) {
				Mojo.Log.info("ListAssistant: No emails in this folder so showing the 'empty' message");
				this.emptyMessage.show();
			}
		} else {
			this.emptyMessage.hide();
		}
	},

    setupSubscriptions: function() {
		if (!this.folderSubscription) {
            this.folderSubscription =
                Folder.subscribeFolderDetail(this.folderObj.folderId,
											 this.filter,
                                             this.renderFolderDetailsHandler,
                                             this.folderErrorHandler);
        }
    },
    cleanupSubscriptions: function() {
        if (this.folderSubscription) {
            this.folderSubscription.cancel();
            this.folderSubscription = null;
        }
    },
    enableSubscriptions: function() {
        this.setupSubscriptions();
        this.dataSource.enableSubscription();
    },
    disableSubscriptions: function() {
        this.dataSource.disableSubscription();
        this.cleanupSubscriptions();
    },
    maximizeSubscriptions: function() {
        // TODO: maximize this.folderSubscription.
        this.dataSource.maximizeSubscription();
    },
    minimizeSubscriptions: function() {
        this.dataSource.minimizeSubscription();
        // TODO: minimize this.folderSubscription.
    }
});

ListAssistant.syncDate = function(timestamp) {
      	var now = new Date();
		var yesterday = new Date();
		yesterday.setHours(0, 0, 0, 0);
		var yesterdayTime = yesterday.getTime();
		var d = new Date(timestamp);
 	  	if (timestamp > yesterdayTime) {
 	  		return "<b>"+ Mojo.Format.formatDate(d, {time:'short'}) +"</b>";
 	  	}
 	  	else {
 	  		return "<b>"+ Mojo.Format.formatDate(d, {date:'short'}) +"</b>";
 	  	}
};

ListAssistant.kSortMenuItemIndex = 1;
ListAssistant.kSortByDateMenuItemIndex = 0;
ListAssistant.kSortBySenderMenuItemIndex = 1;
ListAssistant.kSortBySubjectMenuItemIndex = 2;
ListAssistant.kSortByDateMenuItem = {label:$L('By Date'), command:'sort-by-date', shortcut:'d', checkEnabled:true};
ListAssistant.kSortBySenderMenuItem = {label:$L('By Sender'), command:'sort-by-sender', shortcut:'s', checkEnabled:true};
ListAssistant.kSortBySubjectMenuItem = {label:$L('By Subject'), command:'sort-by-subject', shortcut:'t', checkEnabled:true};