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

var WeekAssistant = 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.horizScrollerModel = {scrollbars: false, mode: "horizontal-snap"};
		this.vertScrollerModel = {scrollbars: false, mode: "vertical"};

		this.colors = {
				'cal-color-blue': 	{background: 'rgb(145, 211, 234)', border: 'rgb(99, 165, 188)',	text: 'rgb(5, 32, 41)'},
				'cal-color-green': 	{background: 'rgb(140, 240, 140)', border: 'rgb(77, 206, 77)',	text: 'rgb(1, 52, 1)'},
				'cal-color-yellow': {background: 'rgb(232, 227, 124)', border: 'rgb(184, 188, 88)',	text: 'rgb(54, 51, 0)'},
				'cal-color-gray': 	{background: 'rgb(186, 186, 186)', border: 'rgb(140, 140, 140)',text: 'rgb(25, 25, 25)'},
				'cal-color-orange': {background: 'rgb(251, 180, 116)', border: 'rgb(212, 132, 61)',	text: 'rgb(48, 18, 00)'},
				'cal-color-pink': 	{background: 'rgb(245, 156, 188)', border: 'rgb(209, 104, 147)',text: 'rgb(40, 0, 0)'},
				'cal-color-red': 	{background: 'rgb(255, 151, 151)', border: 'rgb(224, 98, 98)',	text: 'rgb(45, 8, 21)'},
				'cal-color-purple': {background: 'rgb(217, 183, 255)', border: 'rgb(177, 128, 232)',text: 'rgb(38, 22, 56)'},
				'cal-color-teal': 	{background: 'rgb(114, 223, 210)', border: 'rgb(68, 177, 164)',	text: 'rgb(0, 42, 36)'},
			};

		this.PREV_WEEK = 1;
		this.THIS_WEEK = 2;
		this.NEXT_WEEK = 3;
		this.active = false;
		this.requests = [];
		this.HOUR_HEIGHT = 30;
		this.TOTAL_HEIGHT = this.HOUR_HEIGHT * 24;
		this.COL_WIDTH = [45, 46, 46, 46, 46, 46, 45];
		this.COL_POS = [0, 45, 91, 137, 183, 229, 275];
		this.pendingRender = false;
		this.pendingCalendarSettingsUpdate = false;
	},
					
	getEventChangeCallback: function(response) {
		if (response.changed) {
			this.clearCache();
			this.updateAll();
		}
	},
	
	getEventsCallback: function(response) {
		//Mojo.Log.info("week-assistant: getEventsCallback");
		var numWeeks = response.days.size() / 7;
		for (var i = 0; i < numWeeks; i++) {
			var week = {};
			week.days = [];
			for (var j = 0; j < 7; j++) {
				week.days.push(response.days[i*7+j]);
			}
			var beginOfWeek = week.days[0].date;
			this.addToCache(beginOfWeek, week);
			
			var weekDisplayed = this.isWeekDisplayed(beginOfWeek);
			// Rerender the week regardless of whether the Week View is active
			// or not.  Otherwise when the view does activate, it won't be
			// up to date.
			if (weekDisplayed) {
				if (this.active) {
					this.renderWeek(weekDisplayed, week);
					if (weekDisplayed == this.THIS_WEEK)
						this.scrollToDefault(true);
				}
				else 
					this.pendingRender = true;
			}
				
		}
	},
	
	isWeekDisplayed: function(week) {
		var weekDateKey = this.keyFromDate(week);
		var thisWeek = this.getFirstDayOfWeek(getCurrentDateTime());
		if (weekDateKey == this.keyFromDate(thisWeek))
			return this.THIS_WEEK;
		if (weekDateKey == this.keyFromDate(thisWeek.addWeeks(-1)))
			return this.PREV_WEEK;
		if (weekDateKey == this.keyFromDate(thisWeek.addWeeks(2)))
			return this.NEXT_WEEK;
		return 0;
	},
	
	buildViewHeader: function() {
		var hdr = {};
		hdr.viewPrefix = 'wv';	// Do NOT Localize
		
		var hdrHtml = Mojo.View.render({object: hdr, template: 'shared/calendar_view_header'});
		this.controller.get('wv_header_container1').update(hdrHtml);
	},
	
	buildWeekTitle: function() {
		var today = Date.today().clearTime();
		var dayOfWeek = this.getFirstDayOfWeek(this.weekDate);
		
		var template = new Template($L("Week of #{dateStr}"));
		var templateModel = {};
		templateModel.dateStr = Mojo.Format.formatDate(dayOfWeek, $L("MMM d, yyyy"));
		this.title.innerHTML = template.evaluate(templateModel);
		
		if (this.changingWeeks)
			return;

		// Erase the entire cavas
		this.ctxHeaderLabels.fillStyle = "rgb(228, 228, 226)";
		this.ctxHeaderLabels.fillRect(0, 0, 960, 25);
		
		// Draw seperator lines below labels
		this.ctxHeaderLabels.fillStyle = "rgb(169, 169, 169)";
		this.ctxHeaderLabels.fillRect(0, 16, 960, 1);
		this.ctxHeaderLabels.fillStyle = "rgb(242, 242, 241)";
		this.ctxHeaderLabels.fillRect(0, 17, 960, 1);
		
		// Draw vertical black seperator between weeks
		this.ctxHeaderLabels.fillStyle = "rgb(0, 0, 0)";
		this.ctxHeaderLabels.fillRect(320, 0, 1, 18);
		this.ctxHeaderLabels.fillRect(641, 0, 1, 18);
		
		dayOfWeek.addWeeks(-1).clearTime();
		var currentDayIndex = -1;
		this.ctxHeaderLabels.fillStyle = "rgb(0, 0, 0)";
		for (var i = 0; i < 3; i++) {
			for (var j = 0; j < 7; j++) {
				var text1 = String(Mojo.Format.formatDate(dayOfWeek,'E'));
				var text2 = String(dayOfWeek.getDate());
				this.ctxHeaderLabels.font = "bold 14px Prelude";
				var width1 = this.ctxHeaderLabels.measureText(text1).width;
				this.ctxHeaderLabels.font = "14px Prelude";
				var width2 = this.ctxHeaderLabels.measureText(text2).width;
				var xPos = i * 320 + this.COL_POS[j] + (this.COL_WIDTH[j] - (width1 + width2 + 2)) / 2
				this.ctxHeaderLabels.font = "bold 14px Prelude";
				this.ctxHeaderLabels.strokeText(text1, 
												xPos, 
												11);
				this.ctxHeaderLabels.font = "14px Prelude";
				this.ctxHeaderLabels.strokeText(text2, 
												xPos + width1 + 2, 
												11);
				
				if ((currentDayIndex == -1) && (dayOfWeek.compareTo(today) == 0)) {
					currentDayIndex = j + (i * 7);
				}
				dayOfWeek.addDays(1);
			}
		}
		if (currentDayIndex != -1) { 
			this.currentDay.setStyle({
				top: '34px',
				left: (960 / 21 * currentDayIndex - 8) + 'px',
				display: 'block'
			});
		} else {
			if (this.currentDay.style.display != 'none')
				this.currentDay.style.display = 'none';
		}
	},
	
	positionDayMarkers: function() {
		var prefs = getPrefs();
		if (prefs) {			
			// new Date() automatically converts to LOCAL time so we need to convert
			// back to UTC time here
			var startTime = new Date(prefs.startTimeOfDay);
			startTime.addMinutes(startTime.getTimezoneOffset());
			this.startDayMarkerPos = startTime.getHours() + (startTime.getMinutes() / 60.0);
			
			this.midDayMarkerPos = 12;
			
			var endTime = new Date(prefs.endTimeOfDay);
			endTime.addMinutes(endTime.getTimezoneOffset());
			this.endDayMarkerPos = endTime.getHours() + (endTime.getMinutes() / 60.0);
			
			var hourFormat = 'h';
			if (getTimeFormat() == 'HH24') {
				hourFormat = 'H';
			}
			
			// Start day marker
			if (startTime.getMinutes() == 0)
				this.startDayMarkerTime = Mojo.Format.formatDate(startTime, hourFormat);
			else
				this.startDayMarkerTime = Mojo.Format.formatDate(startTime, hourFormat + ':mm');
			
			// Middle Day Marker
			this.midDayMarkerTime = '12';
			
			// End day marker
			if (endTime.getMinutes() == 0)
				this.endDayMarkerTime = Mojo.Format.formatDate(endTime, hourFormat);
			else
				this.endDayMarkerTime = Mojo.Format.formatDate(endTime, hourFormat + ':mm');
		}
	},
	
	updateEmptySpacer: function() {
		// Determine the height of the fixed header
		var fixedHeight = this.controller.get('wv_view_header').getHeight() + 'px';
		
		this.controller.get('wv_empty_spacer').setStyle({height: fixedHeight});
	},
	
	currentDateTimeUpdated: function() {
 		this.weekDate = getCurrentDateTime();
		this.buildWeekTitle();
		if (!this.changingWeeks) {
			this.updateFromCache();
			this.scrollToDefault(true);
		}
	},
	
	timeFormatUpdated: function() {
		// The time format has changed so we need to update the time header
		this.positionDayMarkers();
		this.updateFromCache();
	},
	
	timeChangeUpdated:function(){
		Mojo.Log.info('week view: time change updated')
		this.weekDate = getCurrentDateTime();
		this.buildWeekTitle();
    	this.updateFromCache();
	},
	
	dayChanged: function() {
		this.buildWeekTitle();
	},
	
	currentCalendarUpdated: function() {
		//Mojo.Log.info("week-assistant: currentCalendarUpdated");
		var calMgr = getCalendarsManager();
		if (calMgr.currentCal == 'all') {
			this.controller.get('wv_current_calendar').update($L("All"));
			this.controller.get('wv_calendar_source').setAttribute('class', 'header-cal-source');
		} else {
			var syncSource = calMgr.getCalSyncSource("id"+calMgr.currentCal);
			if (syncSource == "Local")
				// Special case the local calendar to always show Palm since Palm Profile always
				// gets truncated and looks ugly
				this.controller.get('wv_current_calendar').update($L('Palm'));
			else
				this.controller.get('wv_current_calendar').update(calMgr.getCalAccountName("id"+calMgr.currentCal));
			this.controller.get('wv_calendar_source').setAttribute('class', 'header-cal-source ' + calMgr.getCalColorStyle("id"+calMgr.currentCal));
		}
	},
	
	calendarSettingsUpdated: function() {
		// The colors have been updated, so we clear the cache and update everything
		this.pendingCalendarSettingsUpdate = true;
	},
		
	calendarListUpdated: function() {
		// Do nothing
	},
	
	calendarPrefsUpdated: function() {
		var newPrefs = getPrefs();
		//Mojo.Log.info('week-assistant: calendarPrefsUpdated');
		
		// If the start time or end time changed, update the day markers
		if (this.savedPrefs.startTimeOfDay !=  newPrefs.startTimeOfDay ||
				this.savedPrefs.endTimeOfDay != newPrefs.endTimeOfDay) {
				this.positionDayMarkers();
				this.updateFromCache();
		}
		
		// If the start of week changed, update the whole layout
		if (this.savedPrefs.startOfWeek != newPrefs.startOfWeek) {
			// We have rebuild the week title, and the view since the start of
			// the week determines the day that is drawn at the left
			this.buildWeekTitle();
			this.updateFromCache();
		}
		
		this.savedPrefs = Object.clone(newPrefs);
	},
	
	updateFromCache: function() {
		var thisWeek = this.getFirstDayOfWeek(getCurrentDateTime()).set({hour: 0, minute: 0, second: 0, millisecond: 0});
		var prevWeek = this.getFirstDayOfWeek(getCurrentDateTime()).addWeeks(-1).set({hour: 0, minute: 0, second: 0, millisecond: 0});
		var nextWeek = this.getFirstDayOfWeek(getCurrentDateTime()).addWeeks(1).set({hour: 0, minute: 0, second: 0, millisecond: 0});
		var startDay = null;
		var endDay = null;
		
		var weekData = this.getFromCache(prevWeek.getTime());
		if (weekData) {
			this.renderWeek(this.PREV_WEEK, weekData);
			if (!this.isCacheEntrySubscribed(prevWeek.getTime())) {
				startDay = prevWeek;
				endDay = this.getLastDayOfWeek(prevWeek);
			}
		} else {
			this.renderEmptyWeek(this.PREV_WEEK);
			startDay = prevWeek;
			endDay = this.getLastDayOfWeek(prevWeek);
		}
		
		weekData = this.getFromCache(thisWeek.getTime());
		if (weekData) {
			this.renderWeek(this.THIS_WEEK, weekData);
			if (!this.isCacheEntrySubscribed(thisWeek.getTime())) {
				if (!startDay)
					startDay = thisWeek;
				endDay = this.getLastDayOfWeek(thisWeek);
			}
		} else {
			this.renderEmptyWeek(this.THIS_WEEK);
			if (!startDay)
				startDay = thisWeek;
			endDay = this.getLastDayOfWeek(thisWeek);
		}
		
		weekData = this.getFromCache(nextWeek.getTime());
		if (weekData) {
			this.renderWeek(this.NEXT_WEEK, weekData);
			if (!this.isCacheEntrySubscribed(nextWeek.getTime())) {
				if (!startDay)
					startDay = nextWeek;
				endDay = this.getLastDayOfWeek(nextWeek);
			}
		} else {
			this.renderEmptyWeek(this.NEXT_WEEK);
			if (!startDay)
				startDay = nextWeek;
			endDay = this.getLastDayOfWeek(nextWeek);
		}
		
		if (startDay)
    		this.requests.push(getCalendarService().getEvents(getCurrentCal(), 
							   startDay.getTime(), 
							   endDay.getTime(), 
							   this.getEventsCallback.bind(this), 
							   this.controller,
							   this.getEventsFailCallback.bind(this),
							   {useSlices: true}));
	},
	
	updateAll: function() {
		var beginOfWeek = this.getFirstDayOfWeek(getCurrentDateTime()).addWeeks(-1);
		var endOfWeek = this.getLastDayOfWeek(getCurrentDateTime()).addWeeks(1);
    	this.requests.push(getCalendarService().getEvents(getCurrentCal(), 
						   beginOfWeek.getTime(), 
						   endOfWeek.getTime(), 
						   this.getEventsCallback.bind(this), 
						   this.controller,
						   this.getEventsFailCallback.bind(this),
						   {useSlices: true}));
	},
	
	getEventsFailCallback: function(response) {
	  	Mojo.Log.info("FAILURE IN CALENDAR SERVICE GET EVENT REQUEST *******************************");
	},
	
	resetPos: function() {
		this.settingSnapIndex = true;
		this.controller.get('wv_scroll_container').mojo.setSnapIndex(1, false);
		this.settingSnapIndex = false;
	},

	renderDayMarkers: function(whichWeek) {
		var ctx = this.weeks[whichWeek].ctxEvents;
		
		var startPos = this.startDayMarkerPos * this.HOUR_HEIGHT - 1;
		var midPos = this.midDayMarkerPos * this.HOUR_HEIGHT - 1;
		var endPos = this.endDayMarkerPos * this.HOUR_HEIGHT - 1;

		// Draw marker lines
		ctx.fillStyle = "rgb(135, 135, 134)";
		ctx.fillRect(0, startPos, 320, 2);
		ctx.fillRect(0, endPos, 320, 2);
		ctx.fillStyle = "rgb(160, 160, 160)";
		ctx.fillRect(0, midPos, 320, 1);
		ctx.fillStyle = "rgb(255, 255, 255)";
		ctx.fillRect(0, startPos + 2, 320, 1);
		ctx.fillRect(0, midPos + 1, 320, 1);
		ctx.fillRect(0, endPos + 2, 320, 1);
	},
	
	renderStartEndDayLabels: function(whichWeek) {
		var ctx = this.weeks[whichWeek].ctxEvents;
		
		var startPos = this.startDayMarkerPos * this.HOUR_HEIGHT - 1;
		var midPos = this.midDayMarkerPos * this.HOUR_HEIGHT - 1;
		var endPos = this.endDayMarkerPos * this.HOUR_HEIGHT - 1;
		
		// Draw marker labels
		ctx.fillStyle = "rgb(0, 0, 0)";
		ctx.font = "bold 14px Prelude";
		ctx.strokeText(this.startDayMarkerTime, 17, startPos + 24);
		// Only draw the label for the noon marker IF it doesn't equal the start or end day times
		if (this.midDayMarkerPos != this.startDayMarkerPos && this.midDayMarkerPos != this.endDayMarkerPos)
			ctx.strokeText(this.midDayMarkerTime, 12, midPos + 24);
		ctx.strokeText(this.endDayMarkerTime, 17, endPos + 24);
	},
	
	renderEmptyWeek: function(whichWeek) {
		var ctx = this.weeks[whichWeek].ctxEvents;
		
		// Erase the entire cavas
		ctx.fillStyle = "rgb(228, 228, 226)";
		ctx.fillRect(0, 0, 320, this.TOTAL_HEIGHT);

		// Draw vertical day seperator lines
		var x = 0;
		for (var i = 0; i < 6; i++) {
			x += this.COL_WIDTH[i];
			ctx.fillStyle = "rgb(160, 160, 160)";
		    ctx.fillRect(x, 0, 1, this.TOTAL_HEIGHT);
			ctx.fillStyle = "rgb(255, 255, 255)";
		    ctx.fillRect(x+1, 0, 1, this.TOTAL_HEIGHT);
		}
		
		this.renderDayMarkers(whichWeek);
	},	
	
	updateWeekHeight: function(whichWeek) {
		var newHeight = this.weeks[whichWeek].alldayHeight;
		var allDayEvents = this.weeks[whichWeek].all_day_events;
		var allDayEventsContainer = this.weeks[whichWeek].alldaycontainer;
		if (newHeight) {
			if (allDayEvents.offsetHeight != newHeight) {
				//change the height and clear the new area
				allDayEvents.setAttribute("height", newHeight + 'px');
				var ctx = this.weeks[whichWeek].ctxAllDay;
				ctx.fillStyle = "rgb(198, 197, 196)";
				ctx.fillRect(0, 0, 320, newHeight);
			}
				
			if (allDayEvents.style.display != 'block')
				allDayEvents.style.display = 'block';
			if (allDayEventsContainer.style.display != 'block')
				allDayEventsContainer.style.display = 'block';
		} else {
			if (allDayEvents.style.display != 'none')
				allDayEvents.style.display = 'none';
			if (allDayEventsContainer.style.display != 'none')
				allDayEventsContainer.style.display = 'none';
		}
	},
	
	renderEvent: function(whichWeek, whichDay, event) {
		var ctx = this.weeks[whichWeek].ctxEvents;
		var marginLeft = 4;
		var marginRight = 4;
		var eventMaxWidth = this.COL_WIDTH[whichDay] - marginLeft - marginRight;
		
		var top = event.start_decimal * this.HOUR_HEIGHT / 100;
		var height = ((event.end_decimal - event.start_decimal) * this.HOUR_HEIGHT / 100) - 1;
		var left = marginLeft;
		var width = eventMaxWidth;
		if (event.overlap_count > 1) {
			left = (event.overlap_index * (eventMaxWidth / event.overlap_count)) + marginLeft;
			width = eventMaxWidth / event.overlap_count - 1;
			if (event.overlap_index >= event.overlap_count-1)
				width++;
		}
		left += this.COL_POS[whichDay]; 
		
		ctx.fillStyle = this.colors[event.calendarColor].border;
		ctx.fillRect(left, top, width, height);
		ctx.fillStyle = this.colors[event.calendarColor].background;
		ctx.fillRect(left+1, top+1, width-2, height-2);
		
		// highlights
		ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
		ctx.fillRect(left+1, top+1, width-2, 1);
		ctx.fillStyle = "rgba(255, 255, 255, 0.3)";
		ctx.fillRect(left+1, top+2, width-2, 1);
		ctx.fillStyle = "rgba(255, 255, 255, 0.6)";
		ctx.fillRect(left+1, top+1, 1, height-2);
		ctx.fillRect(left+width-2, top+1, 1, height-2);

		ctx.fillStyle = "rgba(255, 255, 255, 0.3)";
		ctx.fillRect(left+1, top+height-2, width-2, 1);
		ctx.fillStyle = "rgba(255, 255, 255, 0.20)";
		ctx.fillRect(left+2, top+1, 1, height-2);
		ctx.fillRect(left+width-3, top+1, 1, height-2);
		
		ctx.fillStyle = "rgba(255, 255, 255, 0.10)";
		ctx.fillRect(left+1, top+height-3, width-2, 1);
	},
	
	renderBusyTime: function(whichWeek, whichDay, busy) {
		var ctx = this.weeks[whichWeek].ctxEvents;
		var marginLeft = 1;
		var marginRight = 1;
		
		var top = busy.start_decimal * this.HOUR_HEIGHT / 100;
		var height = (busy.end_decimal - busy.start_decimal) * this.HOUR_HEIGHT / 100;
		var left = marginLeft + 1;
		var width = this.COL_WIDTH[whichDay] - marginLeft - marginRight;
		left += this.COL_POS[whichDay]; 
		
		ctx.fillStyle = "rgba(204, 204, 204, 10)";
		ctx.fillRect(left, top, width, height);
	},
	
	renderAllDayEvent: function(whichWeek, whichDay, row, allday) {
		var ctx = this.weeks[whichWeek].ctxAllDay;
		var marginLeft = 4;
		var top = 2 + row * 9;
		var left = this.COL_POS[whichDay] + marginLeft;
		ctx.drawImage(this.colors[allday.calendarColor].allday, left, top);
	},
	
	
	renderWeek: function(whichWeek, data) {
		this.renderEmptyWeek(whichWeek);
		
		if ((data == null) || (data.days == null))
			return;
		
		// Erase any existing all day events and any selection highlighting
		if (this.weeks[whichWeek].alldayHeight) {
			var ctx = this.weeks[whichWeek].ctxAllDay;
			ctx.fillStyle = "rgb(198, 197, 196)";
			ctx.fillRect(0, 0, 320, this.weeks[whichWeek].alldayHeight);
		}
		
		// Show selected column if any	
		if (this.selectedCol >= 0) {
			var ctx = this.weeks[whichWeek].ctxEvents;
			var selectedColor = "rgba(4, 4, 4, 0.5)"
			ctx.fillStyle = selectedColor;
		    ctx.fillRect(this.COL_POS[this.selectedCol], 0, this.COL_WIDTH[this.selectedCol], this.TOTAL_HEIGHT);
			
			var ctxAllDay = this.weeks[whichWeek].ctxAllDay;
			ctxAllDay.fillStyle = selectedColor;
		    ctxAllDay.fillRect(this.COL_POS[this.selectedCol], 0, this.COL_WIDTH[this.selectedCol], this.weeks[whichWeek].alldayHeight);
		}	
				
		//Figure out how high the allday area should be, and adjust the size 		
		var i;	
		var maxRows = 0;
		for(i = 0; i < 7; i++){
			maxRows = Math.max(data.days[i].allDayEvents.length, maxRows);
		}
		if(maxRows > 5)
			maxRows = 5;
			
		this.weeks[whichWeek].alldayHeight = maxRows ? maxRows * 9 + 3 : 0;
		this.updateWeekHeight(whichWeek);
		
		for (var whichDay = 0; whichDay < 7; whichDay++) {
			var day = data.days[whichDay];
			
			// All day events
			var row = 0;
			for (i = 0; i < day.allDayEvents.length; i++) {
				this.renderAllDayEvent(whichWeek, whichDay, row, day.allDayEvents[i]);
				if (row >= 5)
					break;
				row++;
			}
			
			// Busy times
			for (i = 0; i < day.busyTimes.length; i++) {
				this.renderBusyTime(whichWeek, whichDay, day.busyTimes[i]);
			}
			
			// Events
			for (i = 0; i < day.events.length; i++) {
				this.renderEvent(whichWeek, whichDay, day.events[i]);
			}
		}
		
		// Draw vertical day separator lines in allday area
		var x = 0;
		var ctxAllDay = this.weeks[whichWeek].ctxAllDay;
		for (var i = 0; i < 6; i++) {
			x += this.COL_WIDTH[i];
			ctxAllDay.fillStyle = "rgb(160, 160, 160)";
    		ctxAllDay.fillRect(x, 0, 1, this.weeks[whichWeek].alldayHeight);
			ctxAllDay.fillStyle = "rgb(242, 242, 241)";
    		ctxAllDay.fillRect(x+1, 0, 1, this.weeks[whichWeek].alldayHeight);
		}
		
		this.renderStartEndDayLabels(whichWeek);
	},
	
	setup: function() {
		this.clearCache();
		this.reminders = getReminderManager();
		this.reminders.observeReminders('week', this);
		this.prevScrollPos=100000; //some high value for the intial scrolling
		
		observeCurrentDateTime('week', this);
		observeTimeFormat('week', this);
		observeTimeChange('week',this);
		observeDayChange('week', this);
		
		this.colors['cal-color-blue'].allday = this.controller.get('allday_blue');
		this.colors['cal-color-green'].allday = this.controller.get('allday_green');
		this.colors['cal-color-yellow'].allday = this.controller.get('allday_yellow');
		this.colors['cal-color-gray'].allday = this.controller.get('allday_gray');
		this.colors['cal-color-orange'].allday = this.controller.get('allday_orange');
		this.colors['cal-color-pink'].allday = this.controller.get('allday_pink');
		this.colors['cal-color-red'].allday = this.controller.get('allday_red');
		this.colors['cal-color-purple'].allday = this.controller.get('allday_purple');
		this.colors['cal-color-teal'].allday = this.controller.get('allday_teal');
		
		this.currentDay = this.controller.get('wv_current_day');
		this.scrollMakerPos = this.controller.get('wv_scroll_marker');
		this.scrollDataDiv = this.controller.get('wv_scroll_data');
		
		this.weeks = [];
		this.weeks = [];
		this.weeks[this.PREV_WEEK] = {};
		this.weeks[this.THIS_WEEK] = {};
		this.weeks[this.NEXT_WEEK] = {};
		this.weeks[this.PREV_WEEK].eventContainer = this.controller.get('wv_eventsDay0');
		this.weeks[this.THIS_WEEK].eventContainer = this.controller.get('wv_eventsDay1');
		this.weeks[this.NEXT_WEEK].eventContainer = this.controller.get('wv_eventsDay2');

		this.weeks[this.PREV_WEEK].ctxEvents = this.controller.get('events0').getContext('2d');
		this.weeks[this.THIS_WEEK].ctxEvents = this.controller.get('events1').getContext('2d');
		this.weeks[this.NEXT_WEEK].ctxEvents = this.controller.get('events2').getContext('2d');
		this.weeks[this.PREV_WEEK].ctxEvents.font = "14px Prelude Bold";
		this.weeks[this.THIS_WEEK].ctxEvents.font = "14px Prelude Bold";
		this.weeks[this.NEXT_WEEK].ctxEvents.font = "14px Prelude Bold";
		
		this.selectedCol = -1;
		this.eventHandler = new TapHandler(this.controller.get('events1'));
		for (var i = 0; i < 7; i++) {
			this.eventHandler.addHandler(this.COL_POS[i], 
										 0, 
										 this.COL_POS[i] + this.COL_WIDTH[i],
										 this.TOTAL_HEIGHT,
										 this.tapHandler.bind(this, i),
										 this.showSelectedHandler.bind(this, i),
										 this.clearSelectedHandler.bind(this, i));
		}

		this.weeks[this.PREV_WEEK].alldayHeight = 0;
		this.weeks[this.THIS_WEEK].alldayHeight = 0;
		this.weeks[this.NEXT_WEEK].alldayHeight = 0;
		this.weeks[this.PREV_WEEK].all_day_events = this.controller.get('wv_all_day_events1');
		this.weeks[this.THIS_WEEK].all_day_events = this.controller.get('wv_all_day_events2');
		this.weeks[this.NEXT_WEEK].all_day_events = this.controller.get('wv_all_day_events3');
		this.weeks[this.PREV_WEEK].alldaycontainer = this.controller.get('wv_alldaycontainer1');
		this.weeks[this.THIS_WEEK].alldaycontainer = this.controller.get('wv_alldaycontainer2');
		this.weeks[this.NEXT_WEEK].alldaycontainer = this.controller.get('wv_alldaycontainer3');
		this.weeks[this.PREV_WEEK].ctxAllDay = this.weeks[this.PREV_WEEK].all_day_events.getContext('2d');
		this.weeks[this.THIS_WEEK].ctxAllDay = this.weeks[this.THIS_WEEK].all_day_events.getContext('2d');
		this.weeks[this.NEXT_WEEK].ctxAllDay = this.weeks[this.NEXT_WEEK].all_day_events.getContext('2d');
		this.allDayEventHandler = new TapHandler(this.weeks[this.THIS_WEEK].all_day_events);
		for (var i = 0; i < 7; i++) {
			this.allDayEventHandler.addHandler(this.COL_POS[i], 
												 0, 
												 this.COL_POS[i] + this.COL_WIDTH[i],
												 2 + 5 /* max rows */ * 9 /* row height */,
												 this.tapHandler.bind(this, i),
										 		 this.showSelectedHandler.bind(this, i),
										 		 this.clearSelectedHandler.bind(this, i));
		}
		
		this.snapElements = [];
		this.snapElements.push(this.weeks[this.PREV_WEEK].eventContainer);
		this.snapElements.push(this.weeks[this.THIS_WEEK].eventContainer);
		this.snapElements.push(this.weeks[this.NEXT_WEEK].eventContainer);
		this.horizScrollerModel.snapElements = {x: this.snapElements, y: []};
		this.horizScrollerModel.snapIndex = 1;
		this.scrollerPos = 1;
		this.controller.setupWidget('wv_scroll_container', {}, this.horizScrollerModel);	
		this.controller.setupWidget('wv_scroll_events', {}, this.vertScrollerModel);	

		this.propertyChangedHandler = this.propertyChanged.bindAsEventListener(this);
		this.controller.listen('wv_scroll_container', Mojo.Event.propertyChanged, this.propertyChangedHandler);
		this.scrollStartingHandler = this.scrollStarting.bindAsEventListener(this);
		this.controller.listen('wv_scroll_container', Mojo.Event.scrollStarting, this.scrollStartingHandler);
		
//		this.sceneWidth = this.controller.get('wv_scroll_container').getDimensions().width;
		this.sceneWidth = 320;
		
		this.eventChangedRequest = getCalendarService().getEventChange(this.getEventChangeCallback.bind(this), this.controller);
		
		this.buildViewHeader();
		this.title = this.controller.get('wv_title');
		this.headerLabels = this.controller.get('wv_header_labels');
		this.ctxHeaderLabels = this.headerLabels.getContext('2d');
		this.ctxHeaderLabels.font = "14px Prelude Bold";
		
		getCalendarsManager().observeCalendars('week', this);
		getPrefsManager().observeCalendarPrefs('week', this);
		
		var prefs = getPrefs();
		this.savedPrefs = undefined;
		
		if (prefs)
			this.savedPrefs = Object.clone(getPrefs());
		
		// Update the height of the empty_space based on whether the all day list is visible
		this.updateEmptySpacer();
		this.positionDayMarkers();

		this.currentDateTimeUpdated();

		this.menuModel = {
			visible:true,
			items: [ 
						{},
						{	label:$L('Views'), 
							toggleCmd:'week', 
							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);
		
		this.scrolling = 0;
	},
  
	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);
				this.handleMonthView();
			} else if (event.command == 'week') {
				Event.stop(event);
				goToToday();
			} 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);
				this.pendingScroll = true;
				setCurrentDateTime(new Date());
			}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();
		}
	},
	
	propertyChanged: function(event) {
		if (this.settingSnapIndex)
			return;
		
		// Change date displayed in header before we start scrolling.  
		if (this.scrollerPos != event.value) {
			var scrollerChange =  event.value - this.scrollerPos;
			this.scrolling += scrollerChange;
			this.scrollerPos = event.value;
			this.weekDate.addWeeks(scrollerChange);
			this.changingWeeks = true;
			this.buildWeekTitle();
			this.changingWeeks = false;
		}
	},
	
	scrollStarting: function(event) {
		if (this.settingSnapIndex)
			return;
			
		this.prevScrollPos = 100000;	// some abritrarly large number
		this.scrolling = 0;
		this.scrollerPos = 1;
		this.listeningToScroller = true;
		event.scroller.addListener(this);
	},
	
	moved: function(scrollEnding, position) {
		if (this.settingSnapIndex)
			return;
			
		var currentScrollPos = Math.abs(position.x);	
		if (this.listeningToScroller && ((Math.abs(this.prevScrollPos -currentScrollPos ) == 0)|| scrollEnding)) {
			this.listeningToScroller = false;
			switch (this.scrolling) {
				case 0:
					// Back where we started, dont need to do anything
					break;
				case -1:
					this.prevWeek();
					break;
				case +1:
					this.nextWeek();
					break;
				default:
					this.resetPos();
					this.changingWeeks = true;
					setCurrentDateTime(new Date(this.weekDate).clearTime());
					this.changingWeeks = false;
					this.updateFromCache();
					this.scrollToDefault(true);
					break;
			}
		}
		this.prevScrollPos = currentScrollPos;
	},
	
 	scrollToDefault: function(animate) {
		if (this.scroller) {
			this.scroller.mojo.scrollTo(0, -(this.startDayMarkerPos * this.HOUR_HEIGHT - this.weeks[this.THIS_WEEK].alldayHeight) + 20, animate);
		}
	},
	
	prevWeek: function() {
		// Get the current date and time
		var currentDateTime = getCurrentDateTime();
		
		if (currentDateTime != false) {

			var beginWeekNoTime = this.getFirstDayOfWeek(currentDateTime).set({hour: 0, minute: 0, second: 0, millisecond: 0});
			var week = this.getFromCache(beginWeekNoTime.getTime());
			this.renderWeek(this.NEXT_WEEK, week);

			// Subtract one week from the Date
			currentDateTime.addWeeks(-1);
			
			this.changingWeeks = true;
			setCurrentDateTime(currentDateTime);
			this.changingWeeks = false;
			
			beginWeekNoTime = this.getFirstDayOfWeek(currentDateTime).set({hour: 0, minute: 0, second: 0, millisecond: 0});
			week = this.getFromCache(beginWeekNoTime.getTime());
			this.renderWeek(this.THIS_WEEK, week);
			
			this.buildWeekTitle();
			currentDateTime.addWeeks(-1);
			beginWeekNoTime = this.getFirstDayOfWeek(currentDateTime).set({hour: 0, minute: 0, second: 0, millisecond: 0});
			week = this.getFromCache(beginWeekNoTime.getTime());
			if (week) {
				this.renderWeek(this.PREV_WEEK, week);
			} else {
	    		this.requests.push(getCalendarService().getEvents(getCurrentCal(), 
								   beginWeekNoTime.getTime(), 
								   this.getLastDayOfWeek(beginWeekNoTime).getTime(), 
								   this.getEventsCallback.bind(this), 
								   this.controller,
								   this.getEventsFailCallback.bind(this),
								   {useSlices: true}));
				this.renderEmptyWeek(this.PREV_WEEK);
			}
			this.resetPos();
			this.scrollToDefault(true);
		} 	
  	},

	nextWeek: function() {
		// Get the current date and time
		var currentDateTime = getCurrentDateTime();
		
		if (currentDateTime != false) {
			
			var beginWeekNoTime = this.getFirstDayOfWeek(currentDateTime).set({hour: 0, minute: 0, second: 0, millisecond: 0});
			var week = this.getFromCache(beginWeekNoTime.getTime());
			this.renderWeek(this.PREV_WEEK, week);
			
			// Add one week to the Date
			currentDateTime.addWeeks(1);

			this.changingWeeks = true;
			setCurrentDateTime(currentDateTime);
			this.changingWeeks = false;

			beginWeekNoTime = this.getFirstDayOfWeek(currentDateTime).set({hour: 0, minute: 0, second: 0, millisecond: 0});
			week = this.getFromCache(beginWeekNoTime.getTime());
			this.renderWeek(this.THIS_WEEK, week);

			this.buildWeekTitle();
			currentDateTime.addWeeks(1);
			beginWeekNoTime = this.getFirstDayOfWeek(currentDateTime).set({hour: 0, minute: 0, second: 0, millisecond: 0});
			week = this.getFromCache(beginWeekNoTime.getTime());
			if (week) {
				this.renderWeek(this.NEXT_WEEK, week);
			} else {
	    		this.requests.push(getCalendarService().getEvents(getCurrentCal(), 
								   beginWeekNoTime.getTime(), 
								   this.getLastDayOfWeek(beginWeekNoTime).getTime(), 
								   this.getEventsCallback.bind(this), 
								   this.controller,
								   this.getEventsFailCallback.bind(this),
								   {useSlices: true}));
				this.renderEmptyWeek(this.NEXT_WEEK);
			}
			this.resetPos();
			this.scrollToDefault(true);
		}	
  },	
	
 	cleanup: function() {
		this.reminders.stopObservingReminders('week');
		getCalendarsManager().stopObservingCalendars('week');
		getPrefsManager().stopObservingCalendarPrefs('week');
		
		this.requests.clear();
		
		stopObservingCurrentDateTime('week');
		stopObservingTimeFormat('week');
		stopObservingTimeChange('week');
		stopObservingDayChange('week');
 },
	
	ready: function() {
		this.scroller = this.controller.get('wv_scroll_events');
		this.scrollToDefault(false);
	},
	
  	activate: function() {
		this.active = true;
		
		this.eventHandler.activate();
		this.allDayEventHandler.activate();
		
		// Calendar Header
		this.headerTapHandler = this.showJumpTo.bindAsEventListener(this);
		this.controller.get('wv_view_header').observe(Mojo.Event.tap, this.headerTapHandler);
		
		this.controller.get('wv_calendar_source').observe(Mojo.Event.tap, this.CalSelectorHandler);

		// The colors have been updated, so we clear the cache and update everything
		if (this.pendingCalendarSettingsUpdate == true) {
			this.clearCache();
			this.updateAll();
			DayAssistant.cacheSize = 0;
			DayAssistant.dayCache = new Hash();
			DayAssistant.cacheSubscribed = new Hash();
			this.pendingCalendarSettingsUpdate = false;
		}
		
		if (this.pendingRender == true) {
			this.updateFromCache();
			this.pendingRender = false;
		}
	},
  
	deactivate: function(){
		this.active = false;
		
		this.eventHandler.deactivate();
		this.allDayEventHandler.deactivate();
		
		this.controller.get('wv_view_header').stopObserving(Mojo.Event.tap, this.headerTapHandler);
    	this.controller.get('wv_calendar_source').stopObserving(Mojo.Event.tap, this.CalSelectorHandler);
		this.clearCacheSubscribed();
		stopObservingCurrentDateTime('week');
	},
	
  	showSelectedHandler: function(col) {
		this.selectedCol = col;
		
		var beginWeekNoTime = this.getFirstDayOfWeek(getCurrentDateTime()).set({hour: 0, minute: 0, second: 0, millisecond: 0});
		var week = this.getFromCache(beginWeekNoTime.getTime());
		this.renderWeek(this.THIS_WEEK, week);
  	},
	
  	clearSelectedHandler: function(col) {
		this.selectedCol = -1;
		
		var beginWeekNoTime = this.getFirstDayOfWeek(getCurrentDateTime()).set({hour: 0, minute: 0, second: 0, millisecond: 0});
		var week = this.getFromCache(beginWeekNoTime.getTime());
		this.renderWeek(this.THIS_WEEK, week);
  	},
	
  	tapHandler: function(col) {
		var dayofweek= this.getFirstDayOfWeek(getCurrentDateTime());
		dayofweek.addDays(col);
			
		//show that day of the week in day view
		this.showDay(dayofweek);	// Do NOT Localize format string
  	},
	
	showDay: function(day) {
		setCurrentDateTime(day, true);
		var stageController = this.controller.stageController;
		stageController.popScene();
 		stageController.pushScene({name: "day", transition: Mojo.Transition.crossFade, disableSceneScroller: true});
  	},
	
	handleMonthView: function(e) {
		var stageController = this.controller.stageController;
		stageController.popScene();
		stageController.pushScene({name: 'month', transition: Mojo.Transition.crossFade, disableSceneScroller: true});
  	},
  
  	handleDayView: function(e) {
		var stageController = this.controller.stageController;
		stageController.popScene();
		stageController.pushScene({name: "day", transition: Mojo.Transition.crossFade, disableSceneScroller: true});
  	},
	
	calSelector: function(e) {
		Event.stop(e);
    	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);
			this.clearCache();
			this.updateAll();
			DayAssistant.cacheSize = 0;
			DayAssistant.dayCache = new Hash();
			DayAssistant.cacheSubscribed = new Hash();
		}
	},
	
	getFirstDayOfWeek: function (week) {
		var firstDayOfWeek = new Date(week);
		var prefs = getPrefs();
		if (prefs && firstDayOfWeek.getDay() != (prefs.startOfWeek - 1))
			firstDayOfWeek.moveToDayOfWeek((prefs.startOfWeek - 1), -1)
			
		return firstDayOfWeek;
	},
	
	getLastDayOfWeek: function (week) {
		var lastDayOfWeek = new Date(week);
		var prefs = getPrefs();
		var lastDay = 6;
		
		if (prefs && prefs.startOfWeek - 1 != 0)
			lastDay = (prefs.startOfWeek - 1) - 1;
		if (lastDayOfWeek.getDay() != lastDay)
			lastDayOfWeek.moveToDayOfWeek(lastDay)
			
		return lastDayOfWeek;
	},
	
	getFromCache: function(weekDate) {
		return WeekAssistant.weekCache.get(this.keyFromDate(weekDate));
	},	
	
	isCacheEntrySubscribed: function(weekDate) {
		return WeekAssistant.cacheSubscribed.get(this.keyFromDate(weekDate));
	},
	
	addToCache: function(weekDate, events) {
		var key = this.keyFromDate(weekDate);
		WeekAssistant.weekCache.set(key, events);
		WeekAssistant.cacheSubscribed.set(key, true);
		WeekAssistant.cacheSize = WeekAssistant.weekCache.keys().length;
		while (WeekAssistant.cacheSize > WeekAssistant.MAX_CACHE_SIZE) {
			// Remove the week that is farthest away from the current week
			var farthestIndex = -1;
			var farthestDiff = 0;
			var keys = WeekAssistant.weekCache.keys();
			var currentKey = this.keyFromDate(getCurrentDateTime());
			for (var i = 0; i < keys.length; i++) {
				var diff = Math.abs(currentKey - keys[i]);
				if (diff > farthestDiff) {
					farthestDiff = diff
					farthestIndex = i;
				}
			}
			if (farthestIndex >= 0) {
				WeekAssistant.weekCache.unset(keys[farthestIndex]);
				WeekAssistant.cacheSize--;
			}
		}
	},
	
	keyFromDate: function(weekDate) {
		var date = new Date(weekDate);
		return date.clearTime().getTime();
	},
	
	clearCache: function() {
		WeekAssistant.weekCache = new Hash();
		WeekAssistant.cacheSize = 0;
		this.clearCacheSubscribed();
	},
	
	clearCacheSubscribed: function() {
		WeekAssistant.cacheSubscribed = new Hash();
	},
	
	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);
			}
		}
	},
});

WeekAssistant.MAX_CACHE_SIZE = 10;		// in weeks
WeekAssistant.cacheSize = 0;
WeekAssistant.weekCache = new Hash();
WeekAssistant.cacheSubscribed = new Hash();
