/**
 * 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");
 
/**
 * EventCalendarFeed class
 **/
org.bearcatmusic.EventCalendarFeed = org.bearcatmusic.Class.define({
	name: 		"EventCalendarFeed",

	construct:	function (feedURL, completedHandler)
				{
					this.query = new google.gdata.calendar.CalendarEventQuery(feedURL);
					
					this.completedHandler = completedHandler;
					
					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({ startDate: startDate, endDate: endData, error: error}); });

							},
							
		parseFeedForDateRange:	function (feedRoot, startDate, endDate)
								{
									var feedData = new Object();
								
									feedData.startDate = new Date(startDate);
									feedData.endDate = new Date(endDate);
									feedData.entries = feedRoot.feed.getEntries();
									
									// Signal completion
									if (this.completedHandler)
										this.completedHandler(feedData);
								},
	}
});

/**
 * EventCalendar class
 **/
org.bearcatmusic.EventCalendar = org.bearcatmusic.Class.define({
	name:		"EventCalendar",
	
	construct:	function(containerListID)
				{
					// Allocate the feeds
					var feedCompletedDelegate = org.bearcatmusic.utilities.createDelegate(this, this.feedCompleted);
					this.feed = new org.bearcatmusic.EventCalendarFeed("http://www.google.com/calendar/feeds/bearcatmusic.org_soqaj91e65n4h1gbecsvp4fk6s@group.calendar.google.com/public/full", feedCompletedDelegate);
					this.events = new Array();
					this.currentEventItem = null;
					this.lastEventItem = null;
					this.roundingCompensation = 0;
					this.timer = null;
	
					// Set the start date and prepare the first week
					this.startDate = new Date(Date.today());
					this.endDate = new Date(Date.today());
					this.containerListID = containerListID;
					
					this.outstandingRequests = 0;
					while (this.events.length < 3 && this.endDate.isBefore(new Date(this.startDate).addMonths(3)))
					{
						this.outstandingRequests++;
						this.requestOneMonth();
					}
				},
				
	statics: {
		load:		function() { org.bearcatmusic.EventCalendarFeed.load(); org.bearcatmusic.Transition.load(); }
	},

	methods: {
		// Public methods
		previousEvent:
				function()
				{
					if (this.currentEventItem != null)
					{
						this.pause();
						this.displayItem(-1);
					}
				},
		
		nextEvent:
				function()
				{
					if (this.currentEventItem != null)
					{
						this.pause();
						this.displayItem(1);
						
					}
				},
				
		pause:
				function()
				{
					if (this.timer)
						clearInterval(this.timer);
						
					this.timer = null;
				},
				
		resume:
				function()
				{
					if (!this.timer && this.currentEventItem != null)
					{
						var myThis = this;
						this.timer = setInterval(function() { myThis.displayItem(1); }, 3000);
					}
				},
				
		isPaused:
				function()
				{
					return !(this.timer);
				},
			
		// Private methods
		requestOneMonth:
				function()
				{
					// Request the updated feed
					this.feed.requestDateRange(new Date(this.endDate), new Date(this.endDate).next().month());
					this.endDate = new Date(this.endDate.next().month());
				},

		feedCompleted:
				function(data)
				{
					for (var entryNdx = 0; entryNdx < data.entries.length; ++entryNdx)
						this.events.push(data.entries[entryNdx]);
					
					if (--this.outstandingRequests == 0)
					{
						this.events.sort(function(a,b) { return Date.compare(a.getTimes()[0].getStartTime().getDate(), b.getTimes()[0].getStartTime().getDate()); });
						this.renderFeedData();
					}
				},
		
		renderFeedData:
				function()
				{
					var list = document.getElementById(this.containerListID);

					for (var eventNdx = 0; eventNdx < this.events.length; ++eventNdx)
					{
						var listItem = document.createElement("li");

						if (this.events[eventNdx] instanceof google.gdata.calendar.CalendarEventEntry)
						{
							var when = this.events[eventNdx].getTimes()[0];
							var startTime = when.getStartTime().getDate(), startDate = new Date(startTime).clearTime();
							var endTime = when.getEndTime().getDate(), endDate = new Date(endTime).clearTime();
							var dateOnly = when.getStartTime().isDateOnly();
							var element = null;
							var dateString = null, timeString = null;
							
							// When it's only a date, the endDate/endTime is actually midnight of the next day, so subtract
							//  a day so that it doesn't appear that the event is one day longer than it actually is.
							if (dateOnly)
								endDate.addDays(-1);
							
							if (startDate.equals(endDate))
								dateString = startDate.toString("MMMM d");
							else if (startDate.getMonth() == endDate.getMonth())
								dateString = startDate.toString("MMMM d") + endDate.toString(" - d");
							else
								dateString = startDate.toString("MMM d") + " - " + endDate.toString("MMM d");
							
							if (!dateOnly)
								timeString = startTime.toString("h:mm t") + " - " + endTime.toString("h:mm t");

							listItem.appendChild(document.createElement("p"));
							listItem.lastChild.setAttribute("class", "title");
							listItem.lastChild.appendChild(document.createTextNode(this.events[eventNdx].getTitle().getText()));

							if (dateString)
							{
								listItem.appendChild(document.createElement("p"));
								listItem.lastChild.setAttribute("class", "date");
								listItem.lastChild.appendChild(document.createTextNode(dateString));
							}

							if (timeString)
							{
								listItem.appendChild(document.createElement("p"));
								listItem.lastChild.setAttribute("class", "time");
								listItem.lastChild.appendChild(document.createTextNode(timeString));
							}
						}
						else
						{
						}
						
						// Add the event to the list
						list.appendChild(listItem);
					}
					
					if (this.events.length < 3)
					{
						// Less than three items, just show them
						var totalHeight = 0;
						for (var item = list.firstChild; item != null; item = item.nextSibling)
							totalHeight += item.offsetHeight;
							
						var offsetTop = Math.floor((list.clientHeight - totalHeight) / 2);
						for (var item = list.firstChild; item != null; item = item.nextSibling)
						{
							item.style.top = offsetTop + "px";
							item.style.visibility = "visible";
							offsetTop += item.offsetHeight;
						}
					}
					else
					{
						this.currentEventItem = list.firstChild;
						this.displayItem(0);
					}
					
					this.resume();
				},
				
		displayItem:
				function(direction)
				{
					with (org.bearcatmusic.utilities)
					{
						var list = document.getElementById(this.containerListID);
						if (direction == 0)
						{
							var prevItem = previousSiblingWithWrap(this.currentEventItem);
							var nextItem = nextSiblingWithWrap(this.currentEventItem);
							
							var currentOffsetTop = Math.floor((list.clientHeight - this.currentEventItem.offsetHeight) / 2);
							
							prevItem.style.top = (currentOffsetTop - prevItem.offsetHeight) + "px";
							prevItem.style.opacity = 0.2;
							prevItem.style.visibility = "visible"
							nextItem.style.top = (currentOffsetTop + this.currentEventItem.offsetHeight) + "px";
							nextItem.style.opacity = 0.2;
							nextItem.style.visibility = "visible"
							this.currentEventItem.style.top = currentOffsetTop + "px";
							this.currentEventItem.style.opacity = 1;
							this.currentEventItem.style.visibility = "visible";
						}
						else
						{
							if (direction > 0)
							{
								var fadeOutItem = previousSiblingWithWrap(this.currentEventItem);
								var fadeDownItem = this.currentEventItem
								var fadeUpItem = nextSiblingWithWrap(this.currentEventItem);
								var fadeInItem = nextSiblingWithWrap(fadeUpItem);
								
							}
							else
							{
								var fadeOutItem = nextSiblingWithWrap(this.currentEventItem);
								var fadeDownItem = this.currentEventItem
								var fadeUpItem = previousSiblingWithWrap(this.currentEventItem);
								var fadeInItem = previousSiblingWithWrap(fadeUpItem);
							}
							
							var scrollAmount = (((fadeUpItem.offsetHeight + fadeDownItem.offsetHeight) / 2) + this.roundingCompensation) * (direction > 0 ? -1 : 1);
							this.currentEventItem = fadeUpItem;
							
							var effects = [];
							
							effects.push(new Effect.Move(fadeOutItem, { x: 0, y: scrollAmount, mode: 'relative', duration: 1, sync: true }));
							effects.push(new Effect.Opacity(fadeOutItem, { from: 0.2, to: 0.0, duration: 0.2, sync: true}));
							
							effects.push(new Effect.Move(fadeDownItem, { x: 0, y: scrollAmount, mode: 'relative', duration: 1, sync: true }));
							effects.push(new Effect.Opacity(fadeDownItem, { from: 1, to: 0.2, duration: 0.8, sync: true}));
							
							effects.push(new Effect.Move(fadeUpItem, { x: 0, y: scrollAmount, mode: 'relative', duration: 1, sync: true }));
							effects.push(new Effect.Opacity(fadeUpItem, { from: 0.2, to: 1, duration: 0.8, sync: true}));
	
							effects.push(new Effect.Move(fadeInItem, { x: 0, y: scrollAmount, mode: 'relative', duration: 1, sync: true }));
							effects.push(new Effect.Opacity(fadeInItem, { from: 0, to: 0.2, duration: 0.2, sync: true}));
							
							var myThis = this;
							new Effect.Parallel(effects, { duration: 1.0, queue: { position: 'end', scope: 'eventCalendar' },
												afterSetup: function(effect) {
													// Make sure fadeInItem is in the right place before we start and is visible
													fadeInItem.style.top = ((direction > 0 ? fadeUpItem.offsetTop + fadeUpItem.offsetHeight : fadeUpItem.offsetTop - fadeInItem.offsetHeight)) + "px";
													fadeInItem.style.visibility = "visible";
												},
												afterFinish: function(effect) {
													// Compensate for rounding errors
													myThis.roundingCompensation = fadeUpItem.offsetTop - Math.floor((list.clientHeight - fadeUpItem.offsetHeight) / 2);
												}
										});
						}
					}
				},
	}
});

