/*
 * EmaillistDataSource - list data model
 
Copyright 2009 Palm, Inc.  All rights reserved.

*/
EmaillistDataSource = Class.create({
	initialize : function(id, limit, notifyLengthListChanged, listAssistant) {
        // Handlers:
        this.messageListCallbackHandler = this.messageListCallback.bind(this);
        this.subscriptionFailureHandler = this.subscriptionFailure.bind(this);
        this.requestHandler = this.requestCallback.bind(this);
        this.getMessageListCountHandler = this.getMessageListCount.bind(this);

		this.notifyLengthListChanged = notifyLengthListChanged;

		this.pendingCountRequest = false;
		this.subscription = undefined;
		this.folderId = id;
		this.subscriptionFailureCount = 0;

        this.throttler = new Throttler(this.requestHandler);

        // Set up the subscription:
        this.offset = 0;
        this.limit = limit;
		this.lastTimeUpdateHandled = 0;
		this.listWidget = undefined;
		this.messagesFilter = undefined;
		this.gotMessagesCount = false;
		
		// record midnight for use in list formatting
		this.calcDateStart(new Date());
		this.listAssistant = listAssistant;
	},
	
	calcDateStart: function(midnightDate) {
		if (typeof midnightDate === 'number') {
			midnightDate = new Date(midnightDate);
		}
		// record midnight (0:00:00) time for provided date and day before that
		// used in bucket determination (today, yesterday, older) 
		midnightDate.setHours(0,0,0,1);
		this.midnight = midnightDate.getTime();
		// 86400000 = 24 hours in milliseconds
		this.prevMidnight = (this.midnight - 86400000); 
		this.nextMidnight = (this.midnight + 86400000);
	},
	
	/* 
	 * Used to check whether or not the currently loaded list needs to have its buckets adjusted.
	 * ie: whether the Today bucket has been invalidated due to crossing midnight.
	 */
	checkReload: function() {
		if (!this.listWidget) {
			return;
		}
				
		var currDate = Date.now();
		if (this.nextMidnight < currDate) {
			this.calcDateStart(currDate);
		 	if (this.listWidget.mojo.getLength() > 0) {
				Mojo.Log.info("Re-drawing list since the midnight time has been changed");
				this.listWidget.mojo.invalidateItems(0);
			}
		}
  	},

	cleanup: function() {
        this.disableSubscription();
		this.notifyLengthListChanged = undefined;
	},
	
	setMessagesFilter: function(filter) {
		this.messagesFilter = filter;
		this.gotMessagesCount = false;
		Mojo.Log.info("set this.messagesFilter=%s", this.messagesFilter);
	},
	
	setSortOrder: function(order) {
		this.sortOrder = order;
	},

	getListLength: function() {
		if (this.listWidget) {
			return this.listWidget.mojo.getLength();
		} else {
			return -1;
		}
	},

	/*
	 * This is the callback used by the ListWidget
	 */
	updateOffsetAndLimit: function(listWidget, offset, limit) {
		Mojo.Log.info("email updateOffsetAndLimit folder=", this.folderId, " offset=", offset, ", limit=", limit, ", subId=", this.subscriberId);
		if (!this.listWidget) {
			this.listWidget = listWidget;
		}

		if (!this.subscription) {
            this.enableSubscription();
		} else {
			// Have a valid subscription so make sure the error count is reset 
			this.subscriptionFailureCount = 0;
			var params = {'folder':this.folderId, 'messagesFilter': this.messagesFilter, 'offset':offset, 'limit':limit, 'subscriberId':this.subscriberId };
			Message.getFolderMsgListLimit(params, this.messageListCallbackHandler);
            this.throttler.notifySentRequest();
		}
	},

	messageListCallback: function(data) {
		// If the subscriberId isn't set, then this is the first time messageListCallback has been
		// called so get the subscriberId and fire off a request for the number of messages in the lsit.
		if (!this.subscriberId && data.subscriberId) {
			this.subscriberId = data.subscriberId;
			// If the list length hasn't been set yet, make an immediate request for the length
			if (this.listWidget.mojo.getLength() === 0) {
				this.countReq = Message.getFolderMsgListCount(this.folderId, this.messagesFilter, this.messageListCountCallback.bind(this, false));
			}
		}
		
		if (data.updated === true) {
			this.gotMessagesCount = false;
		}
		if (this.gotMessagesCount === false) {
			Message.getFolderMsgListCount(this.folderId, this.messagesFilter, this.messageListCountCallback.bind(this, false));
		}

		// This is a special case when the list of emails is small enough to fit within one "page" of the
		// list (so offset==0). If the limit and the number of items don't match, use the number of emails
		// as the count (this can happen when a message is deleted or moved and the number of items in the
		// list has shrunk to less than the size of the list window).
		if (data.list !== undefined && data.offset === 0 && data.limit > data.list.length) {
			this.listWidget.mojo.setLength(data.list.length);
			// If the response list has elements in it, then make sure to tell the list-assistant to
			// remove the "no emails" UI.
			if (data.list.length > 0) {
				this.notifyLengthListChanged();
			}
		}

		// service pinged us that there's new data so send a query to the service.
		if (data.updated === true) {
            this.throttler.request();
		} else if (data.list) {
			if (this.throttler.notifyRequestFulfilled()) {
				// TODO: Make checkReload rely on a date/time service
				this.checkReload(); // check for bucket adjustment.
				this.formatList(data.list, this.messagesFilter);
				this.listWidget.mojo.noticeUpdatedItems(data.offset, data.list);
			}
		}
	},

	requestCallback: function() {
		var ol = this.listWidget.mojo.getLoadedItemRange();
        // Since this is an update, send reset:true to tell the query to reset its cached info
        var params = {'reset':true, 'folder':this.folderId, 'messagesFilter': this.messagesFilter, 
                      'offset':ol.offset, 'limit':ol.limit, 'subscriberId':this.subscriberId };

        this.offset = ol.offset;
        this.limit = ol.limit;

        Message.getFolderMsgListLimit(params, this.messageListCallbackHandler);
        if (this.pendingCountRequest === false) {
            this.pendingCountRequest = true;
            this.getMessageListCountHandler.delay(1);
        }
	},

	getMessageListCount: function() {
		this.pendingCountRequest = false;
		this.countReq = Message.getFolderMsgListCount(this.folderId, this.messagesList, 
			this.messageListCountCallback.bind(this, true));
	},

	messageListCountCallback: function(showEmpty, data) {
		var widgetLen = this.listWidget.mojo.getLength();
		Mojo.Log.info("EmailListDataSource.messageListCountCallback showEmpty=%s, data=%j, widget.len=%d", showEmpty, data, widgetLen);
		var countUsed = data.count;
		
		if (widgetLen != data.count) {
			// Emails opened, and then deleted from a notification can cause list-weirdness.
			// better to show more than less. Subscription will hide mails properly afterwards
			if (widgetLen - data.count === 1) {
				countUsed = widgetLen;
			}
			this.listWidget.mojo.setLength(countUsed);
		}
		
		if (showEmpty === true && this.notifyLengthListChanged !== undefined) {
			this.notifyLengthListChanged();
		}
		
		if (this.gotMessagesCount === false) {
			this.listWidget.mojo.setCount(countUsed);
			this.gotMessagesCount = true;
		}
	},
	
	formatEmailEntry: function(email) {
			// Format date different for today's emails and older emails
			var theDate = new Date(parseInt(email.timeStamp));
			
			if (theDate.getTime() > this.midnight) {
				email.formattedDate = Mojo.Format.formatDate(theDate, {time:'short'});
				if (this.sortOrder !== undefined && (this.sortOrder === Folder.SORT_BY_SENDER_DESC || this.sortOrder === Folder.SORT_BY_SENDER_ASC))
					email.category = this.getStringDividerText(email.sortDisplayName);
				else if (this.sortOrder !== undefined && (this.sortOrder === Folder.SORT_BY_SUBJECT_DESC || this.sortOrder === Folder.SORT_BY_SUBJECT_ASC)) 
					email.category = this.getStringDividerText(email.sortSummary);
			 	else 
					email.category = EmaillistDataSource.dateCategoryToday ;
			} else {
				if (this.sortOrder !== undefined && (this.sortOrder === Folder.SORT_BY_SENDER_DESC || this.sortOrder === Folder.SORT_BY_SENDER_ASC)) 
					email.category = this.getStringDividerText(email.sortDisplayName);
				else if (this.sortOrder !== undefined && (this.sortOrder === Folder.SORT_BY_SUBJECT_DESC || this.sortOrder === Folder.SORT_BY_SUBJECT_ASC))
					email.category = this.getStringDividerText(email.sortSummary);
				else {
					if (theDate.getTime() < this.prevMidnight) {
						email.category = EmaillistDataSource.dateCategoryOlder;
					}
					else {
						email.category = EmaillistDataSource.dateCategoryYesterday;
					}
				} 
				
				// Construct date string. Reuse previous string if currently within day block
				var dateStart = theDate.setHours(0,0,0,0);
				if (this.lastDateStart === dateStart) {
					email.formattedDate = this.lastFormattedDate;
				} else {
					// okay that theDate has had hours set to 0:00. Date String will be the same.
					email.formattedDate = this.lastFormattedDate = Mojo.Format.formatDate(theDate, {
						date: 'short'
					});
				}
				
				this.lastDateStart = dateStart;
			}

			email.priority = Email.getPriorityClass(email.priority);
			var flags = parseInt(email.flags);
			email.read = EmailFlags.getReadStatus(flags);
			var emailFlagged = ((flags & EmailFlags.flaggedBit) != 0);

			// Top status icon: flagged + priority
			if (emailFlagged === true) {
				if (email.priority === Email.HIGH_PRIORITY_CLASS) {
					email.unreadFlaggedIcon = "<img src='images/list-priority-flagged.png' width='24' height='24' style='position:absolute;top:3px;left:3px;'/>";
				} else {
					email.unreadFlaggedIcon = "<img src='images/list-flagged.png' width='24' height='24' style='position:absolute;top:3px;left:3px;'/>";
				} 
			} else {
				email.unreadFlaggedIcon = "";
			}

			// 2nd level status icon: reply/forward
			var replied = (flags & EmailFlags.answeredBit);
			var forwarded = (flags & EmailFlags.forwardedBit);
			var errored = (flags & EmailFlags.errorBit);
			if (errored !== 0) {
				email.errorIcon = "<img src='images/list-error.png' width='18' height='18' style='position:absolute;top:28px;left:5px;'/>"; 
			} else if (replied !== 0 && forwarded !== 0) {
				email.replyIcon = "<img src='images/list-reply-forward.png' width='18' height='18' style='position:absolute;top:28px;left:5px;'/>";
			} else if (replied !== 0) {
				email.replyIcon = "<img src='images/list-reply.png' width='18' height='18' style='position:absolute;top:28px;left:5px;'/>";
			} else if (forwarded !== 0) {
				email.replyIcon = "<img src='images/list-forward.png' width='18' height='18' style='position:absolute;top:28px;left:5px;'/>";
			}
			else {
				email.replyIcon = "";
			}

			// 3rd level status icon: meeting invite
			if ((flags & EmailFlags.meetingBits) !== 0) {
				email.inviteIcon = "<img src='images/list-cal-invite.png' width='18' height='18' style='position:absolute;top:48px;left:5px;'/>";
			} else {
				email.inviteIcon = "";
			}

			// Attachment icon
			if (EmailFlags.hasAttachment(flags) === true) {
				email.attachmentIcon = "<img src='images/attachment-icon.png' width='22' height='22' style='position:absolute;top:6px;right:82px;'/>";
			} else {
				email.attachmentIcon = "";
			}
			
			// When escaping html, make sure to leave original data alone, so consumers can
			// handle data as they need. Instead, escape into a prep variable, pName, which 
			// can be used for display
			if (email.summary === undefined || email.summary.length === 0) {
				email.summary = Message.kNoSubjectText;
				email.pSummary = Message.kNoSubjectText;
			} else {
				email.pSummary = email.summary.escapeHTML();
				email.pSummary = EmailDecorator.decorateFilterResult(email.pSummary, this.filter);
            }
			
            if (email.displayName) {
				email.pDisplayName = email.displayName.escapeHTML();
				email.pDisplayName = EmailDecorator.decorateFilterResult(email.pDisplayName, this.filter);
            }
	},
	
	getStringDividerText: function(str) {
		if (!str || str.blank()) 
			return "";
			
		var result = str.slice(0,1).toUpperCase();
		var accentIdx = EmaillistDataSource.kAccents[0].indexOf(result);
		if (accentIdx !== -1) {
			result = EmaillistDataSource.kAccents[1].charAt(accentIdx);
		}
		
		return result;
	},
	
	formatList: function(list, filter) {	
		// TODO: List widget offers formatting callbacks. Use those?
		this.filter = filter;
		list.each(this.formatEmailEntry.bind(this));
		this.filter = undefined;
	},

	subscriptionFailure: function(err) {
		// Try relaunching the mail service 3 times before giving up
		this.subscriptionFailureCount++;
		if (this.subscriptionFailureCount < 3) {
			Mojo.Log.error("EmaillistDataSource subscriptionFailureCount=", this.subscriptionFailureCount, " detail=", $H(err).inspect());
			if (this.subscription) {
				this.subscription.cancel();
			}
            if (this.listWidget) {
                var ol = this.listWidget.mojo.getLoadedItemRange();
                if (ol.limit) {
                    this.offset = ol.offset;
                    this.limit = ol.limit;
                }
            }
			this.subscription =
                Message.subscribeFolderMsgList(this.folderId, this.sortOrder, 
                                             this.messagesFilter, this.offset, this.limit,
                                             this.messageListCallbackHandler,
                                             this.subscriptionFailureHandler);
	            this.throttler.notifySentRequest(true);
		} else {
			Mojo.Log.error("notifications FATAL: cannot maintain registerForNotifications subscription " + $H(err).inspect());
		}
	},

    // The following are for responding to Scene state changes when requested:
	enableSubscription: function() {
		if (this.listWidget && this.gotMessagesCount === true) {
            var ol = this.listWidget.mojo.getLoadedItemRange();
            if (ol.limit) {
                this.offset = ol.offset;
                this.limit = ol.limit;
            }
        }
        if (!this.subscription) {
            this.subscriberId = 0;
			this.subscription =
                Message.subscribeFolderMsgList(this.folderId, this.sortOrder,  
				    this.messagesFilter, this.offset, this.limit,
                    this.messageListCallbackHandler, this.subscriptionFailureHandler);
            this.throttler.notifySentRequest(true);
        }        
        this.throttler.enable();
	},
	disableSubscription: function() {
		this.throttler.disable();
        if (this.subscription) {
		    this.subscription.cancel();
            this.subscription = undefined;
            this.subscriberId = 0;
        }
	},
	maximizeSubscription: function() {
        this.throttler.maximize();
	},
	minimizeSubscription: function() {
        this.throttler.minimize();
	},
});

EmaillistDataSource.dateCategoryToday     = $L("Today");
EmaillistDataSource.dateCategoryYesterday = $L("Yesterday");
EmaillistDataSource.dateCategoryOlder     = $L("Older");

EmaillistDataSource.kAccents = ["ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ", "SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy"];