/**
 * org.bearcatmusic -- namespace
 **/
 var org;
 if (!org) org = {}; else if (typeof org != "object") throw new Error("org already exists and is not an object");
 if (!org.bearcatmusic) org.bearcatmusic = {}; else if (typeof org.bearcatmusic != "object") throw new Error("org.bearcatmusic already exists and is not an object");
 
/**
 * RehearsalCalendarFeed class
 **/
org.bearcatmusic.RehearsalCalendarFeed = org.bearcatmusic.Class.define({
	name: 		"RehearsalCalendarFeed",

	construct:	function (feedURL, completedHandler, context)
				{
					this.query = new google.gdata.calendar.CalendarEventQuery(feedURL);
					this.query.setSingleEvents(true);
					
					this.completedHandler = completedHandler;
					this.context = context;
					
					if (!this.constructor.service)
						this.constructor.service = new google.gdata.calendar.CalendarService('SMHSMusicBoosters-bearcatmusic.org-1.0');
				},
	
	statics: {
		load:		function() { google.load("gdata", "1"); }
	},
	
	methods: {
		requestDateRange:	function (startDate, endDate)
							{
								var cf = this; // Need a variable so we can refer to "this" in the closure below

								// Run the query
								cf.query.setMinimumStartTime(new google.gdata.DateTime(startDate));
								cf.query.setMaximumStartTime(new google.gdata.DateTime(endDate));
								cf.constructor.service.getEventsFeed(cf.query,
											function(feedRoot) { cf.parseFeedForDateRange(feedRoot, startDate, endDate); },
											function(error) { cf.completedHandler({ context: cf.context, startDate: startDate, endDate: endData, error: error}); });

							},
							
		parseFeedForDateRange:	function (feedRoot, startDate, endDate)
								{
									var feedData = new Object();
								
									feedData.context = this.context;
									feedData.startDate = new Date(startDate);
									feedData.endDate = new Date(endDate);
									feedData.entries = new Object();
									
									// Get the entries from the feed
									var feedEntries = feedRoot.feed.getEntries();
									for (var entryNdx = 0; entryNdx < feedEntries.length; ++entryNdx)
									{
										var times = feedEntries[entryNdx].getTimes();
										for (var timeNdx in times)
										{
											var entryStartDate = times[timeNdx].getStartTime().date.toDateString();
											if (!(entryStartDate in feedData.entries))
												feedData.entries[entryStartDate] = times[timeNdx];
											
											// We only care about the first time	
											break;
										}
									}
									
									// Signal completion
									if (this.completedHandler)
										this.completedHandler(feedData);
								},
								
	}
});

/**
 * RehearsalCalendar class
 **/
org.bearcatmusic.RehearsalCalendar = org.bearcatmusic.Class.define({
	name:		"RehearsalCalendar",
	
	construct:	function()
				{
					// Set up the transition object
					this.transition = new org.bearcatmusic.Transition("rehearsal_week_1", "rehearsal_week_2", true);
	
					// Allocate the feeds
					var feedCompletedDelegate = org.bearcatmusic.utilities.createDelegate(this, this.feedCompleted);
					this.feeds = new Object();
					this.feeds.mb = new org.bearcatmusic.RehearsalCalendarFeed("http://www.google.com/calendar/feeds/bearcatmusic.org_am6sfoatd9pcu7dc64e1e672p4@group.calendar.google.com/public/full", feedCompletedDelegate, "mb");
					this.feeds.cg = new org.bearcatmusic.RehearsalCalendarFeed("http://www.google.com/calendar/feeds/bearcatmusic.org_10stuiu3vf65ii21t87m4ogmpc@group.calendar.google.com/public/full", feedCompletedDelegate, "cg");
					this.feeds.wp = new org.bearcatmusic.RehearsalCalendarFeed("http://www.google.com/calendar/feeds/bearcatmusic.org_i0799c74q2rnljf1iprs5t8knk@group.calendar.google.com/public/full", feedCompletedDelegate, "wp");
	
					// Set the start date and prepare the first week
					this.currentStartDate = Date.today().is().sunday() ? new Date(Date.today()) : new Date(Date.today().previous().sunday());
	
					this.requestCurrentWeek();
					this.prepareForDisplay('rehearsal_week_1', this.feedData[this.currentStartDate.toDateString()]);
				},
				
	statics: {
		load:		function() { org.bearcatmusic.RehearsalCalendarFeed.load(); org.bearcatmusic.Transition.load(); }
	},

	methods: {
		// Public methods
		previousWeek:
				function()
				{
					var oldRehearsalData = this.feedData[this.currentStartDate.toDateString()];
					var newRehearsalData = null;
					
					this.currentStartDate = new Date(this.currentStartDate.previous().sunday());
					newRehearsalData = this.feedData[this.currentStartDate.toDateString()];
		
					if (!newRehearsalData)
					{
						this.requestCurrentWeek();
						newRehearsalData = this.feedData[this.currentStartDate.toDateString()];
					}
		
					this.prepareForDisplay(this.transition.getOtherID(), newRehearsalData);
			
					var monthStringsDifferent = oldRehearsalData.monthString != newRehearsalData.monthString;
		
					this.transition.previous([
							new Effect.Opacity(this.transition.getCurrentID() + "_month", { from: monthStringsDifferent ? 1 : 0, to: 0, duration: 1.0, sync: true }),
							new Effect.Opacity(this.transition.getOtherID() + "_month", { from: monthStringsDifferent ? 0 : 1, to: 1, duration: 1.0, sync: true })
						]);
				},
		
		nextWeek:
				function()
				{
					var oldRehearsalData = this.feedData[this.currentStartDate.toDateString()];
					var newRehearsalData = null;
					
					this.currentStartDate = new Date(this.currentStartDate.next().sunday());
					newRehearsalData = this.feedData[this.currentStartDate.toDateString()];
		
					if (!newRehearsalData)
					{
						this.requestCurrentWeek();
						newRehearsalData = this.feedData[this.currentStartDate.toDateString()];
					}
					
					this.prepareForDisplay(this.transition.getOtherID(), newRehearsalData);
			
					var monthStringsDifferent = oldRehearsalData.monthString != newRehearsalData.monthString;
		
					this.transition.next([
							new Effect.Opacity(this.transition.getCurrentID() + "_month", { from: monthStringsDifferent ? 1 : 0, to: 0, duration: 1.0, sync: true }),
							new Effect.Opacity(this.transition.getOtherID() + "_month", { from: monthStringsDifferent ? 0 : 1, to: 1, duration: 1.0, sync: true })
						]);
				},
			
		// Private methods
		requestCurrentWeek:
				function()
				{
					var weekStartDateString = this.currentStartDate.toDateString();
					
					if (!this.feedData)
						this.feedData = new Object();
						
					if (!this.feedData[weekStartDateString])
					{
						var feedData = this.feedData[weekStartDateString] = new Object();
						feedData.startDate = new Date(this.currentStartDate);
						feedData.endDate = new Date(this.currentStartDate).add(1).week();
						feedData.completedCount = 0;
					
						// Request the updated feed
						this.feeds.mb.requestDateRange(feedData.startDate, feedData.endDate);
						this.feeds.cg.requestDateRange(feedData.startDate, feedData.endDate);
						this.feeds.wp.requestDateRange(feedData.startDate, feedData.endDate);
					}
				},

		feedCompleted:
				function(data)
				{
					var calendarFeedData = this.feedData[data.startDate.toDateString()];
					
					calendarFeedData[data.context] = data;
					
					if (this.currentStartDate.equals(data.startDate))
					{
						if ("mb" in calendarFeedData && "cg" in calendarFeedData && "wp" in calendarFeedData)
						{
							var elementID = this.transition.getCurrentID();
							
							// We now have all of the data for the currently displayed week, so update the display!
							this.renderFeedData(elementID + "_mb", calendarFeedData.mb);
							this.renderFeedData(elementID + "_cg", calendarFeedData.cg);
							this.renderFeedData(elementID + "_wp", calendarFeedData.wp);
							
							// Hide the "loading" overlay
							new Effect.Fade(elementID + "_overlay", { duration: 1.0 });
						}
					}
				},
		
		prepareForDisplay:
				function(weekElementID, calendarFeedData)
				{
					// Render dates
					for (var ndx = 0, currentDate = new Date(calendarFeedData.startDate); currentDate.isBefore(calendarFeedData.endDate); currentDate = currentDate.add(1).day(), ++ndx)
					{
						var parent = document.getElementById(weekElementID + "_dates_" + ndx);
						var textNode = document.createTextNode(currentDate.getDate());
						
						if (!parent.firstChild)
							parent.appendChild(textNode)
						else
							parent.replaceChild(textNode, parent.firstChild);
					}
					
					// If all feeds are complete, render the feed data for all of the mat once, otherwise put up a "loading" graphic
					if ("mb" in calendarFeedData && "cg" in calendarFeedData && "wp" in calendarFeedData)
					{
						// We now have all of the data for the currently displayed week, so update the display!
						this.renderFeedData(weekElementID + "_mb", calendarFeedData.mb);
						this.renderFeedData(weekElementID + "_cg", calendarFeedData.cg);
						this.renderFeedData(weekElementID + "_wp", calendarFeedData.wp);
					}
					else
					{
						// Bring in the "loading" overaly
						new Effect.Appear(weekElementID + "_overlay", { duration: 0.0 });
					}
		
					// Generate a string representing the month(s)
					if (!calendarFeedData.monthString)
					{
						if (calendarFeedData.startDate.getMonth() == calendarFeedData.endDate.getMonth())
							calendarFeedData.monthString = calendarFeedData.startDate.toString("MMMM");
						else
							calendarFeedData.monthString = calendarFeedData.startDate.toString("MMMM") + "/" + calendarFeedData.endDate.toString("MMMM");
					}
		
					// Replace the month
					parentElement = document.getElementById(weekElementID + "_month");
					newParentElement = parentElement.cloneNode(false);
					
					newParentElement.appendChild(document.createTextNode(calendarFeedData.monthString));
					parentElement.parentNode.replaceChild(newParentElement, parentElement);
				},

		renderFeedData:	
				function(parentElementID, data)
				{
					var fragment = document.createDocumentFragment();
		
					if (data.error)
					{
						fragment.appendChild(document.createElement("li"));
						fragment.lastChild.setAttribute("class", "error");
						fragment.lastChild.appendChild(document.createTextNode("Error loading calendar data"));
					}
					else
					{
						// Handle a null set of entries
						if (!data.entries)
							data.entries = new Array();
					
						for (var currentDate = new Date(data.startDate); currentDate.isBefore(data.endDate); currentDate = currentDate.add(1).day())
						{
							fragment.appendChild(document.createElement("li"));
							
							var entry = data.entries[currentDate.toDateString()];
							if (entry !== undefined)
							{
								var startDateTime = entry.getStartTime();
								if (startDateTime.isDateOnly())
								{
									fragment.lastChild.appendChild(document.createTextNode("All Day"));
								}
								else
								{
									var endDateTime = entry.getEndTime();
				
									var startFormatString = "h"; // always show hours
									if (startDateTime.date.getMinutes() != 0)
										startFormatString += ":mm"; // add minutes if not 00
									if (startDateTime.date.getHours() < 12 && endDateTime.date.getHours() >= 12)
										startFormatString += "t"; // add AM/PM if different from end time
									
									var endFormatString = "h"; // always show hours
									if (endDateTime.date.getMinutes() != 0)
										endFormatString += ":mm"; // add minutes if not 00
									endFormatString += "t"; // always add AM/PM
				
									fragment.lastChild.appendChild(document.createTextNode((startDateTime.date.toString(startFormatString) + " - " + endDateTime.date.toString(endFormatString)).toLowerCase()));
								}
							}
							else
							{
								// No entry for the current date
								fragment.lastChild.setAttribute("class", "null");
							}
						}
					}
					
					var parentElement = document.getElementById(parentElementID);
					var newParentElement = parentElement.cloneNode(false);
		
					newParentElement.appendChild(fragment);
					parentElement.parentNode.replaceChild(newParentElement, parentElement);
				},
	}
});

