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

var MONTH_DAY_ID_FORMAT = 'MMMM dd yyyy';	// IMPORTANT: DO NOT LOCALIZE!

var MonthAssistant = Class.create({
				
	reminderMenuItemId: 3,
	
	initialize: function() {
 		this.appMenuModel = { visible:true, 
					label:$L('Calendar'), 
					items: [
						Mojo.Menu.editItem,
						{label:$L('Sync Now'), command:'sync', id: 0},
						{label:$L('Show today'), command:'today', id: 1},
						{label:$L('Jump to...'), command:'jumpto', id: 2},
						{label:$L('Missed reminders...'), command:'reminders', id: 3},
						{label:$L('Preferences & Accounts'), command:Mojo.Menu.prefsCmd, checkEnabled: true},
						{label:$L('Help'), command:Mojo.Menu.helpCmd, disabled:false}
					]
				};
				
		this.scrollerModel = { scrollbars: false, mode: "vertical-snap", snapIndex: 0, snapElements: { y: [] } };
	},
														
 	setupMonth: function(startDate, endDate, callbackfn) 
	{
		getCalendarService().getBusyDays(getCurrentCal(), startDate.getTime(), endDate.getTime(), callbackfn, this.controller);
	},
	
	buildViewHeader: function() 
	{
		//Mojo.Log.info("month-assistant: buildViewHeader");
		this.controller.get('mv_header_container1').update(Mojo.View.render({object: { viewPrefix: 'mv' }, template: 'shared/calendar_view_header'}));
		this.mvTitle = this.controller.get('mv_title');
	},
	
	buildMonthTitle: function() 
	{
		this.mvTitle.innerText = Mojo.Format.formatDate(this.monthDate, $L("MMM yyyy")); // Localize this date format string
  	},

 	buildDayHeader: function() {
		//Mojo.Log.info("month-assistant: buildDayHeader");
		var genDayHeader = '';
		
		// Set up the array of day names based on the start of the week
		var date = new Date();
		var dayValue = 0;
		if (this.savedPrefs)
			dayValue = this.savedPrefs.startOfWeek-1;
			
		var days = [];
		
		date.moveToDayOfWeek(dayValue);
		
		for (i = 0; i < 7; i++) {
			var dayName = Mojo.Format.formatDate(date, "E");
			days.push(dayName[0]);
			// Generate the html for the row containing the day header
			var dayOfWeek = date.getDay();
			if (dayOfWeek == 0 || dayOfWeek == 6) {
				// Weekend
				genDayHeader += '<div class="day weekend"> ' + days[i] + ' </div>';
			} else {
				// Weekday
				genDayHeader += '<div class="day"> ' + days[i] + ' </div>';
			}
			
			date.addDays(1);
		}	
		
		this.controller.get('mv_day_labels').update(genDayHeader);
  	},
	
	_days: [ 'one', 'two', 'three', 'four', 'five', 'six', 'seven' ],
	
	_months: null,
	
	// Create a table for the empty months.  NOTE: total weeks is always 18 (126 days).
	_createEmptyMonthTable: function()
	{
		var t = document.createElement('table');
		t.className = 'months';
		for (var w = 0; w < 18; w++)
		{
			var r = t.insertRow(w);
			r.className = 'week';
			for (var d = 0; d < 7; d++)
			{
				var c = r.insertCell(d);
				c.className = 'day';
				c.setAttribute('x-mojo-tap-highlight','momentary');
				var i = c.appendChild(document.createElement('div'));
				i.className = 'monthview-free-time';
				i = c.appendChild(document.createElement('div'));
				i.className = 'monthview-free-time';
				i = c.appendChild(document.createElement('div'));
				i.className = 'monthview-free-time';
				i = c.appendChild(document.createElement('div'));
				i.className = 'monthview-day-numeral';
			}
		}
		
		return t;
	},
	
	_token2state: function(token)
	{
		if (token >= 48 && token <= 57)
		{
			return token - 48;
		}
		else if (token >= 65 && token <= 90)
		{
			return token - 55;
		}
		else if (token >= 97 && token <= 122)
		{
			return token - 61;
		}
		else if (token == '+')
		{
			return 62;
		}
		else
		{
			return 63;
		}
	},
	
	// A dummy week - used to filling out new week views
	_dummyWeek: null,
	
	_createDummyWeek: function()
	{
		var item = { className: 'monthview-free-time' };
		item.nextSibling = item;
		var day = { firstChild: item }; 
		day.nextSibling = day;
		day.previousSibling = day;
		this._dummyWeek = { firstChild: day, lastChild: day };
	},
	
	// Days in each of 12 months (Feb is fixed up by hand as necessary)
	// We try to avoid the DateJS stuff because it's indescribably slow.
	_monthDays: [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ],
	
	// Render a week forwards - used when we're scrolling forwards in time
	_renderWeekForward: function(week, ridx, oldweek, mday)
	{
		ridx *= 7;
		var day = week.firstChild;
		var oday = oldweek.firstChild;
		for (var c = 0; c < 7; c++) 
		{
			var child = day.firstChild;
			var ochild = oday.firstChild;
			var thisday = mday.day;
			
			// Don't set CSS if the value is the same.  You'd think this wouldn't do anything
			// but WebKit considers all attribute changes to have potential side effects - so this
			// does much damage to performance and is best avoided
			for (var i = 0; i < 3; i++)
			{
				var cn = ochild.className;
				if (child.className != cn) 
				{
					child.className = cn;
				}
				child = child.nextSibling;
				ochild = ochild.nextSibling;
			}
			child.innerText = thisday;
			
			var cls = 'day ' + (thisday <= 7 ? this._days[thisday - 1] : '');
			if (ridx + c - (thisday - 1) == this.thisMonthStartIndex) 
			{
				cls += ' selected-month' + (ridx + c == this.thisDayIndex ? ' today' : '');
				if (mday.dayOfWeek == 0 || mday.dayOfWeek == 6) {
					cls += ' weekend';
				}
			}
			if (day.className != cls) 
			{
				day.className = cls;
			}
			
			// Move the day forwards, allowing for month endings
			if (thisday < mday.limit)
			{
				mday.day++;
			}
			else
			{
				if (mday.month < 11)
				{
					mday.month++;
				}
				else
				{
					mday.month = 0;
				}
				mday.limit = this._monthDays[mday.month];
				mday.day = 1;
			}
			
			if (mday.dayOfWeek == 6) {
				mday.dayOfWeek = 0;
			} else {
				mday.dayOfWeek++;
			}
			
			day = day.nextSibling;
			oday = oday.nextSibling;
		}
	},
	
	// Render a week backwards - used when we're scrolling backwards in time
	_renderWeekBackward: function(week, ridx, oldweek, mday)
	{
		ridx *= 7;
		var day = week.lastChild
		var oday = oldweek.lastChild;
		for (var c = 6; c >= 0; c--) 
		{
			var child = day.firstChild;
			var ochild = oday.firstChild;
			var thisday = mday.day;
			
			for (var i = 0; i < 3; i++)
			{
				var cn = ochild.className;
				if (child.className != cn) 
				{
					child.className = cn;
				}
				child = child.nextSibling;
				ochild = ochild.nextSibling;
			}
			child.innerText = thisday;
			
			var cls = 'day ' + (thisday <= 7 ? this._days[thisday - 1] : '');
			if (ridx + c - (thisday - 1) == this.thisMonthStartIndex) 
			{
				cls += ' selected-month' + (ridx + c == this.thisDayIndex ? ' today' : '');
				if (mday.dayOfWeek == 0 || mday.dayOfWeek == 6) {
					cls += ' weekend';
				}
			}
			if (day.className != cls) 
			{
				day.className = cls;
			}
			
			if (thisday > 1)
			{
				mday.day--;
			}
			else
			{
				if (mday.month > 0)
				{
					mday.month--;
				}
				else
				{
					mday.month = 11;
				}
				mday.limit = this._monthDays[mday.month];
				mday.day = mday.limit;
			}
			
			if (mday.dayOfWeek == 0) {
				mday.dayOfWeek = 6;
			} else {
				mday.dayOfWeek--;
			}
			
			day = day.previousSibling;
			oday = oday.previousSibling;
		}
	},
	
	renderMonth: function() 
	{
		var rows = this._months.rows;
		var date = new Date(this.firstDay);
		var r;
		
		// We use our own date calculator because the system DateJS stuff is indescribably slow (increases this methods speed by 66%).
		// Fix the leapyears so we display the correctly
		if (Date.isLeapYear(date.getFullYear() + (date.getMonth() >= 2 ? 1 : 0)))
		{
			this._monthDays[1] = 29;
		}
		else
		{
			this._monthDays[1] = 28;
		}
		
		// Calculate the difference (in weeks) between what we're rendering now, and what we did last time
		var wdiff = ((this.firstDay.getTime() - this.lastFirstDay.getTime()) / 604800000) | 0; // 1000*60*60*24*7 - 1 week in milliseconds
		// If the different is between -17 and +17 (inc) then we copy up the weeks from the last render (so keeping the busy info correct)
		// and avoiding flicker as much as we can.  We fill in any missing weeks with a dummy week (basically all days marked as free).
		if (wdiff > 0 && wdiff < 18) 
		{
			var mday = { day: date.getDate(), limit: 0, month: date.getMonth() };
			mday.limit = this._monthDays[mday.month];
			mday.dayOfWeek = date.getDay();
		
			for (r = 0; r < 18 - wdiff; r++) 
			{
				this._renderWeekForward(rows[r], r, rows[r + wdiff], mday);
			}
			for (; r < 18; r++) 
			{
				this._renderWeekForward(rows[r], r, this._dummyWeek, mday);
			}
		}
		else if (wdiff < 0 && wdiff > -18)
		{
			date.addWeeks(18).addDays(-1);
			var mday = { day: date.getDate(), limit: 0, month: date.getMonth() };
			mday.limit = this._monthDays[mday.month];
			mday.dayOfWeek = date.getDay();
		
			for (r = 17; r > -wdiff; r--)
			{
				this._renderWeekBackward(rows[r], r, rows[r + wdiff], mday);
			}
			for (; r >= 0; r--) 
			{
				this._renderWeekBackward(rows[r], r, this._dummyWeek, mday);
			}
		}
		else 
		{
			// When all else fails, we just create a free set of weeks
			var mday = { day: date.getDate(), limit: 0, month: date.getMonth() };
			mday.limit = this._monthDays[mday.month];
			mday.dayOfWeek = date.getDay();
		
			for (var r = 0; r < 18; r++) 
			{
				this._renderWeekForward(rows[r], r, this._dummyWeek, mday);
			}
		}
		
		// Remember the first day for next time
		this.lastFirstDay = new Date(this.firstDay);
	},
	
	renderMonthBusyInfo: function(response) 
	{
		var calStyle = '';
		if (getCurrentCal() != 'all') 
		{
			calStyle = getCalendarsManager().getCalColorStyle('id'+getCurrentCal());
		}

		var weekCell = this._months.rows[0];
		var dayCell = weekCell.cells[0];
		
		var days = response.days;
		var daylen = days.length - 1; // We get back more days of info that we want - 1 more in fact, because its inclusive, rather than exclusive
		for (var d = 0; d < daylen; d++) 
		{
			var day = this._token2state(days.charCodeAt(d));
			
			var child = dayCell.firstChild;
			var cls;
			for (var i = 1; i < 8; i <<= 1)
			{
				if (day & (i << 3)) 
				{
					cls = "monthview-busy-time " + calStyle;
				}
				else if (day & i) 
				{
					cls = "monthview-busy-time";
				}
				else 
				{
					cls = "monthview-free-time";
				}
				if (child.className != cls) 
				{
					child.className = cls;
				}
				child = child.nextSibling;
			}
			
			// Advance the dayCell
			if (d % 7 == 6) 
			{
				// Advance to the next week
				weekCell = weekCell.nextSibling;
				dayCell = weekCell ? weekCell.firstChild : null;
			}
			else 
			{
				dayCell = dayCell.nextSibling;
			}
		}
	},
	
 	buildDays: function() 
	{
		//Mojo.Log.info("month-assistant: buildDays");
		// Given the month and year, build a table of days.  May include
		// days from the previous and next months
		var currentDateTime = getCurrentDateTime();
		
		this.firstOfTheMonth = new Date(currentDateTime);
		this.firstOfTheMonth.moveToFirstDayOfMonth();
		this.firstOfPreviousMonth = new Date(this.firstOfTheMonth);
		this.firstOfPreviousMonth.addMonths(-1);
		this.firstOfNextMonth = new Date(this.firstOfTheMonth);
		this.firstOfNextMonth.addMonths(1);
		
		//var startOfWeek = new Date(this.firstOfTheMonth);
		var daysFromPreviousMonth = this.firstOfTheMonth.getDay();
		if (this.savedPrefs) 
		{
			//startOfWeek.moveToDayOfWeek(this.savedPrefs.startOfWeek-1);
			
			// Figure out which days from the previous month are visible
			daysFromPreviousMonth = this.firstOfTheMonth.getDay() - (this.savedPrefs.startOfWeek - 1);
			
			if (daysFromPreviousMonth < 0) 
			{
				daysFromPreviousMonth += 7;
			}
		}
		
		// 6 weeks before the first of the month
		daysFromPreviousMonth += 42;
		
		this.firstDay = new Date(this.firstOfTheMonth);
		this.firstDay.addDays(-daysFromPreviousMonth);
		
		if (!this.lastFirstDay) 
		{
			this.lastFirstDay = this.firstDay;
		}
		
		var totalDays = 126;
		this.lastDay = new Date(this.firstDay);
		this.lastDay.addDays(totalDays);
		
		// Note: 1 day == 1000*60*60*24 ms == 86400000	
		
		// Use firstDay, noon to determine what thisDayIndex is... If we don't set it to noon,
		// due to rounding thisDayIndex may get set incorrectly 
		var firstDay = new Date(this.firstDay);	
		var fidx = firstDay.set({hour: 12, minute: 0, second: 0, millisecond: 0}).getTime();
		// Use today, noon to determine what thisDayIndex is...  If we don't set it to noon,
		// due to rounding thisDayIndex may get set incorrectly if todayDate is set too early
		// or late.
		var todayDate = (new Date()).set({hour: 12, minute: 0, second: 0, millisecond: 0}).getTime();
		this.thisDayIndex = Math.round((todayDate - fidx) / 86400000);
		this.thisMonthStartIndex = Math.round((this.firstOfTheMonth.getTime() - fidx) / 86400000);
		this.prevMonthStartIndex = Math.round((this.firstOfPreviousMonth.getTime() - fidx) / 86400000);
		this.nextMonthStartIndex = Math.round((this.firstOfNextMonth.getTime() - fidx) / 86400000);
  	},

  buildMonth: function() 
  	{
		//var tstart = new Date();
		this.buildMonthTitle();
		
		if (this._scrolling)
		{
			return;
		}

		this.buildDays();
		this.renderMonth();
		this.showCurrentDay();
		//var rstart = new Date();
		this._setSnaps();
		//var rend = new Date();		
								
		var self = this;
		this.setupMonth(this.firstDay, this.lastDay, function(response)
		{
			self.renderMonthBusyInfo(response);
		});
		
		//Mojo.Log.info("JS TIME", /*rend.getTime() - rstart.getTime(), "ms",*/ rend.getTime() - tstart.getTime(), "ms");
  	},
	
	_setSnaps: function()
	{
		//Mojo.Log.info("SNAPPING");
		
		// Update the scroller widget with the new snap elements, and snap to the middle one
		var y = this.scrollerModel.snapElements.y;
		var rows = this._months.rows;
		y[0] = rows[((this.prevMonthStartIndex + 21) / 7) | 0];
		y[1] = rows[((this.thisMonthStartIndex + 21) / 7) | 0];
		y[2] = rows[((this.nextMonthStartIndex + 21) / 7) | 0];			
		this.settingSnapIndex = true;
		this.scrollContainer.mojo.setSnapIndex(1, false);
		this.settingSnapIndex = false;
	},
	
	// *** OBSERVER CALLBACKS ***
	currentDateTimeUpdated: function() 
	{
		var curDateTime = getCurrentDateTime();
		this.monthDate = curDateTime;
		
		// Check to see if we even need to update the view, since the current date could
		// still be represented by the month currently in view
		if ((this.firstOfTheMonth.getMonth() != curDateTime.getMonth()) ||
				(this.firstOfTheMonth.getYear() != curDateTime.getYear())) {
			this.buildMonth();
		}
  },
	
	currentCalendarUpdated: function() {
		//Mojo.Log.info("month-assistant: currentCalendarUpdated");
		var calMgr = getCalendarsManager();
		if (calMgr.currentCal == 'all') {
			this.controller.get('mv_current_calendar').update($L("All"));
			this.controller.get('mv_calendar_source').setAttribute('class', 'header-cal-source');
		} else {
			var syncSource = calMgr.getCalSyncSource("id"+calMgr.currentCal);
			// Special case the local calendar to always show Palm since Palm Profile always
			// gets truncated and looks ugly
			if (syncSource == "Local")
				// Special case the local calendar to always show Palm since Palm Profile always
				// gets truncated and looks ugly
				this.controller.get('mv_current_calendar').update($L('Palm'));
			else
				this.controller.get('mv_current_calendar').update(calMgr.getCalAccountName("id"+calMgr.currentCal));
			
			this.controller.get('mv_calendar_source').setAttribute('class', 'header-cal-source ' + calMgr.getCalColorStyle("id"+calMgr.currentCal));
		}
	},
	
	calendarSettingsUpdated: function() {
		//Mojo.Log.info('month-assistant: calendarSettingUpdated  %s' , getCurrentCal());
		
		// If 'all' calendars are displayed, no colors are displayed so no need to update
		if (getCurrentCal() != 'all')
			this.pendingCalendarSettingsUpdate = true;
	},
	
	calendarListUpdated: function() {
		// Do nothing
	},
	
	calendarPrefsUpdated: function() {
		var newPrefs = getPrefs();
		
		// If the start of week changes, then we need to update the
		// day headers and the whole month layout
		if (newPrefs.startOfWeek != this.savedPrefs.startOfWeek) {
			//Mojo.Log.info('month-assistant: calendarPrefsUpdated startOfWeek');
			this.savedPrefs.startOfWeek = newPrefs.startOfWeek;
			this.buildDayHeader();
			this.buildMonth();
		}
		
		this.savedPrefs = Object.clone(newPrefs);
	},
	
	dayChanged: function() {
		//Mojo.Log.info("month-assistant: dayChanged");
		this.buildMonth();
	},
	
	// ***
  setup: function() 
	{
		//var time = new Date().getTime();
		//Mojo.Log.info("month-assistant:setup " + time);
		
		this.monthDate = getCurrentDateTime();
		
		this.scrollContainer = this.controller.get('mv_scroll_container');
		this.scrollData = this.controller.get('mv_scroll_data');
		this.dvCurrentDay = this.controller.get('dv_current_day');
		this.month_days = this.controller.get('mv_days');
			
		this.reminders = getReminderManager();
		this.reminders.observeReminders('month', this);
		
		observeCurrentDateTime('month', this);
		observeTimeChange('month,',this);
		observeDayChange('month', this);
		
		var prefs = getPrefs();
		this.savedPrefs = undefined;
		
		if (prefs) 
		{
			this.savedPrefs = Object.clone(prefs);
		}
		
		var t = this._createEmptyMonthTable();
		this._months = this.month_days.appendChild(t);
		
		// IMPORTANT: setupWidget for the scroller MUST be called after the this._months have
		// been setup.  Since we haven't loaded the month days yet, we just default the snap
		// elements to the first day in the first week (1st month), the first day in the 7th week 
		// (2nd month), and the first day in the 13th week (3rd month).  _setSnaps will eventually
		// set the snaps to the first days of each of the 3 months that we have loaded in the 
		// 18 weeks.
		this.snapElements = [];
		var rows = this._months.rows;
		this.snapElements.push(rows[((0 /*index of first day*/ + 21) / 7) | 0]);
		this.snapElements.push(rows[((43 /*index of the first day in the 7th week*/ + 21) / 7) | 0]);
		this.snapElements.push(rows[((85 /*index of the first day in the 13th week*/ + 21) / 7) | 0]);
		this.scrollerModel.snapElements = {y: this.snapElements, x: []};
		this.scrollerModel.snapIndex = 1;
		this.controller.setupWidget('mv_scroll_container', {}, this.scrollerModel);	
		this.scrollStartingHandler = this.scrollStarting.bindAsEventListener(this);
		this.controller.listen(this.scrollContainer, Mojo.Event.scrollStarting, this.scrollStartingHandler);
		this.propertyChangedHandler = this.propertyChanged.bindAsEventListener(this);
		this.controller.listen(this.scrollContainer, Mojo.Event.propertyChanged, this.propertyChangedHandler);
		
		this.monthHeight = this.scrollContainer.getHeight();
		this.monthTop = this.scrollContainer.positionedOffset().top;
		
		this.buildViewHeader();
		this.buildDayHeader();
		
		getCalendarsManager().observeCalendars('month', this);
		getPrefsManager().observeCalendarPrefs('month', this);
		
		this._createDummyWeek();
    
        this.menuModel = 
        {
            visible: true,
            items: [{}, 
            {
                label: $L('Views'),
                toggleCmd: 'month',
                items: [
                {
                    label: $L('Day'),
                    icon: 'menu-day',
                    command: 'day'
                }, 
                {
                    label: $L('Week'),
                    icon: 'menu-week',
                    command: 'week'
                }, 
                {
                    label: $L('Month'),
                    icon: 'menu-month',
                    command: 'month'
                }]
            }, {}, ]
        };
		
		this.controller.setupWidget(Mojo.Menu.commandMenu, undefined, this.menuModel);
		this.controller.setupWidget(Mojo.Menu.appMenu, {omitDefaultItems:true}, this.appMenuModel);
		this.remindersUpdated(); 	// Update missed reminders menu item
		
		this.CalSelectorHandler = this.calSelector.bindAsEventListener(this);
  	},
  
	showJumpTo: function() {
		this.controller.showDialog({template: 'shared/jumpto', assistant: new JumptoDialogAssistant(this)});
	},
	
	syncAllCalendars: function() {
		this.getCalendarAccountsRequest = getAccountsService().getAllAccountsForCalendarService(
		this.getCalendarAccountsCallback.bind(this), this.controller, false);
	},
	
	syncAllCallback: function(response) {
		Mojo.Controller.appController.showBanner({
			messageText: $L("Syncing accounts")},
			null /*launchArguments*/,
			"calendar-sync-all");
	},
	
	getCalendarAccountsCallback: function(response) {
		// Find all the EAS accounts if there are any
		this.easAccountIds = null;
		response.list.each(function(acct){
			//Mojo.Log.info("acct domain %s", acct.domain);
			if (acct.domain == 'eas') {
				// Keep track of the list of eas accounts for Sync Accounts button
				if (this.easAccountIds == null) {
					this.easAccountIds = new Array();
				}
				this.easAccountIds.push(acct.accountId);
			}
		}, this);

		getPimSyncService().syncAll(this.controller, this.easAccountIds, this.syncAllCallback.bind(this));
	},	

  	handleCommand: function(event) 
	{
		if(event.type == Mojo.Event.command) {
			if (event.command == 'month') {
				Event.stop(event);
				goToToday();
			} else if (event.command == 'week') {
				Event.stop(event);
				this.handleWeekView();
			} else if (event.command == 'day') {
				Event.stop(event);
				this.handleDayView();
			} else if (event.command == Mojo.Menu.prefsCmd) {
				Event.stop(event);
				this.controller.stageController.pushScene('prefs');
			}else if(event.command == Mojo.Menu.helpCmd){
				getAppManagerService().launchHelp(this.controller);
			} else if (event.command == 'sync') {
				Event.stop(event);
				this.syncAllCalendars();
			}else if (event.command == 'today') {
				Event.stop(event);
				goToToday();
			}else if (event.command == 'jumpto') {
				Event.stop(event);
				this.showJumpTo();
			} else if (event.command == 'reminders') {
				Event.stop(event);
				this.controller.stageController.pushScene('reminder-list');
			}				
		}
		else if(event.type == Mojo.Event.commandEnable && event.command == Mojo.Menu.prefsCmd) {
			// Enable prefs menuitem for this scene.
			event.stopPropagation();
		}
	},

  cleanup: function() {
		//Mojo.Log.info('month-assistant: cleanup');
		
		this.reminders.stopObservingReminders('month');
		getCalendarsManager().stopObservingCalendars('month');
		getPrefsManager().stopObservingCalendarPrefs('month');
		
		stopObservingCurrentDateTime('month');
		stopObservingTimeChange('month');
		stopObservingDayChange('month');
  	},

  activate: function() {
		//var time = new Date().getTime();
		//Mojo.Log.info("month-assistant:activate " + time);
		
		// Month View - Flick
	  this.flickHandler = this.handleFlickEvents.bindAsEventListener(this);
	  this.scrollData.observe(Mojo.Event.flick, this.flickHandler);

		this.scrollData.makePositioned();
			
		// Month Table
		this.daysTapHandler = this.handleDaysTap.bindAsEventListener(this);
		this.month_days.observe('mojo-tap', this.daysTapHandler);
		this.currentDayTapHandler = this.handleCurrentDayTap.bindAsEventListener(this);
		this.dvCurrentDay.observe('mojo-tap', this.currentDayTapHandler);
		
		// Calendar Header
		this.headerTapHandler = this.showJumpTo.bindAsEventListener(this);
		this.controller.get('mv_view_header').observe(Mojo.Event.tap, this.headerTapHandler);
		
		// Calendar Picker
		this.calendarTapHandler = this.CalSelectorHandler.bindAsEventListener(this);
	  	this.controller.get('mv_calendar_source').observe(Mojo.Event.tap, this.calendarTapHandler);
		
		this.scrollerModel.snapIndex = 0;
		this.buildMonth();
		
		// The colors have been updated, so we clear the cache and update everything
		if (this.pendingCalendarSettingsUpdate == true) {
			// Force another service call.
			//this.setupMonth(this.firstDay, this.lastDay, this.renderMonthBusyInfo.bind(this));
			// TODO: Clean this up when we've consolidated the cache stuff
			WeekAssistant.cacheSize = 0;
			WeekAssistant.weekCache = new Hash();
			WeekAssistant.cacheSubscribed = new Hash();
			DayAssistant.cacheSize = 0;
			DayAssistant.dayCache = new Hash();
			DayAssistant.cacheSubscribed = new Hash();
			this.pendingCalendarSettingsUpdate = false;
		}
	},

	_scrolling: 0,
	_scrollDir: 0,
	
	propertyChanged: function(event) {
		if (this.settingSnapIndex)
			return;
			
		this._scrollDir = event.value;

		// Change date displayed in header before we start scrolling.  
		if ((this._scrollDir == 0) || (this._scrollDir == 2)) {
			this._scrolling++;
			if (this._scrollDir == 0) {
				this.monthDate.addMonths(-1);
			} else {
				this.monthDate.addMonths(1);
			} 
			this.buildMonthTitle();
		}
	},
	
	scrollStarting: function(event) 
	{
		if (this.settingSnapIndex)
			return;
			
		//Mojo.Log.info("SCROLLSTARTING");
		event.scroller.addListener(this);
	},
	
	moved: function(scrollEnding, position) 
	{
		if (this.settingSnapIndex)
			return;
			
		if (scrollEnding)
		{
			this.scrollEnding();
		}
	},
	
	scrollEnding: function()
	{
		//Mojo.Log.info("SCROLLENDING");
		if (this._scrolling) 
		{
			this._scrolling = 0;
			if (this._scrollDir == 0) {
				setCurrentDateTime(getCurrentDateTime().addMonths(-1));
			} else if (this._scrollDir == 2) {
				setCurrentDateTime(getCurrentDateTime().addMonths(1));
			} 
		}
	},
	
	handleFlickEvents: function(event) 
	{
		if (this._scrolling) {
			event.stop();
		}
	},
  
  timeChangeUpdated:function()
	{
		//Mojo.Log.info('month-assistant:timeChangeUpdated');
		this.monthDate = getCurrentDateTime();
		this.buildMonth();
	},
	
  deactivate: function() 
	{
		this.month_days.stopObserving('mojo-tap', this.daysTapHandler);
		this.dvCurrentDay.stopObserving('mojo-tap', this.currentDayTapHandler);
	  	this.scrollData.stopObserving(Mojo.Event.flick, this.flickHandler);
		
		this.controller.get('mv_view_header').stopObserving(Mojo.Event.tap, this.headerTapHandler);
		this.controller.get('mv_calendar_source').stopObserving(Mojo.Event.tap, this.calendarTapHandler);
  	},
	
	// *** EVENT HANDLERS ***
	
	handleDaysTap: function(event) {
		if (this._scrolling) return;
		//Mojo.Log.info('month-assistant: handleDaysTap');
		var targetDay = this.controller.get(event.target);
		
    	if (!targetDay.hasClassName('day')) 
		{
			targetDay = event.target.up('td.day');
		}
			
		//Mojo.Log.info("handleDaysTap: %s" , targetDay.id);
		
		if (targetDay) 
		{
			var daycount = targetDay.parentNode.rowIndex * 7 + targetDay.cellIndex;
			var day = new Date(this.firstDay);
			day.addDays(daycount);
			this.gotoDayView(day);
		}
	},
	
	gotoDayView: function(day) {
		setCurrentDateTime(day);
		var sController = this.controller.stageController;
		sController.popScene();
		sController.pushScene({name: "day", transition: Mojo.Transition.crossFade, disableSceneScroller: true});
	},
	
	calSelector: function(e) 
	{
        e.stop();
        var listItems = buildCalendarsMenu(true /*includeAll*/, true /*includeReadOnly*/, true /*includeExcludedFromAll*/);
        
        this.controller.popupSubmenu(
        {
            onChoose: this.calendarChoose.bind(this),
            toggleCmd: getCurrentCal(),
            // placeNear: e.target,
            manualPlacement: true,
            popupClass: "cal-selector-popup",
            items: listItems
        });
	},
	 
	calendarChoose: function(value) 
	{
		if (value) {
			if(value=="viewOptions")
			{
				this.controller.stageController.pushScene("prefs-options",this.acctsModel);
				return;
			}
			setCurrentCal(value);

			// Force another service call.
			this.buildMonth();
			// TODO: Clean this up when we've consolidated the cache stuff
			WeekAssistant.cacheSize = 0;
			WeekAssistant.weekCache = new Hash();
			WeekAssistant.cacheSubscribed = new Hash();
			DayAssistant.cacheSize = 0;
			DayAssistant.dayCache = new Hash();
			DayAssistant.cacheSubscribed = new Hash();
		}
	},
	
	// ** MENU **

  	handleDayView: function(e) 
	{
		//var today = new Date();
 		//var currentDateTime = getCurrentDateTime();
		//if (currentDateTime.getMonth() == today.getMonth()) {
		//	goToToday();
		//} else {
		//	setCurrentDateTime(currentDateTime.moveToFirstDayOfMonth());
		//}
		//we should always show the day that was previously viewed
		//commenting out the above logic for bug /NOV-28956
		var sController = this.controller.stageController;
		sController.popScene();
		sController.pushScene({name: "day", transition: Mojo.Transition.crossFade, disableSceneScroller: true});
  	},
  
  	handleWeekView: function(e) {
		//var today = new Date();
 		//var currentDateTime = getCurrentDateTime();
		//if (currentDateTime.getMonth() == today.getMonth()) {
		//	goToToday();
		///} else {
		//	setCurrentDateTime(currentDateTime.moveToFirstDayOfMonth());
		//}
		//commenting out the above logic for bug /NOV-28956
		var sController = this.controller.stageController;
		sController.popScene();
		sController.pushScene({name: "week", transition: Mojo.Transition.crossFade, disableSceneScroller: true});
  	},
 
 	showCurrentDay: function() 
	{
		var style = this.dvCurrentDay.style;
		if (this.thisDayIndex >= 0 && this.thisDayIndex < 126)
		{
			var todayDiv = this._months.rows[(this.thisDayIndex / 7) | 0].cells[this.thisDayIndex % 7];
			var todayOffset = todayDiv.positionedOffset();
			style.top = todayOffset.top + 'px';
			style.left = todayOffset.left + 'px';
			style.display = 'block';
		}
		else
		{
			if (style.display != 'none') 
			{
				style.display = 'none';
			}
		}
	},
	
	handleCurrentDayTap: function(event) {
		this.gotoDayView(new Date());
	},
	
	remindersUpdated: function() {
		if (this.reminders.getNumReminders() == 0) {
			this.appMenuModel.items = this.appMenuModel.items.findAll(function(menuItem){
				return (menuItem.id != this.reminderMenuItemId);
			}.bind(this));
			this.controller.modelChanged(this.appMenuModel);
		} else {
			var menuItemVisible = false;
			this.appMenuModel.items.each(function(menuItem){
				if (menuItem.id == this.reminderMenuItemId) {
					menuItemVisible = true;
				}
			}, this);
			if (!menuItemVisible) {
				this.appMenuModel.items.splice(4,0,{label:$L('Missed reminders...'), command:'reminders', id: this.reminderMenuItemId});
				this.controller.modelChanged(this.appMenuModel);
			}
		}
	},
	
});


