(function($) {
	
	/* JQuery plugin to build a calendar month table from a list of hCalendar-formatted events.
	Run makeCalendar() on the container element; this must have a class such as for_month_2010-02
	to indicate the month to draw (Feb 2010 in this case). */
	
	/* Event rendering code assumes that events are in ascending order of start date... */
	
	function createEventElement(event, container) {
		var eventElement = $('<a class="event event_link"></a>');
		container.append(eventElement);
		eventElement.attr('href', '/applications/150th/event_lightbox_info.rm?id=' + event.eventId);
		eventElement.text(event.summary);
		if (event.numberOfDays > 1) {
			eventElement.addClass('multi_day_event');
			if (event.type) {
				eventElement.addClass(event.type);
				eventElement.addClass('multi_day_' + event.type);
			}
		} else {
			eventElement.addClass('single_day_event');
			if (event.type) {
				eventElement.addClass(event.type);
				eventElement.addClass('single_day_event_' + event.type);
				eventElement.parent().addClass('single_day_event_' + event.type);
			}
		}
		return eventElement;
	}
	
	function dateFromIso(isoString) {
		return new Date(isoString.substr(0,4), isoString.substr(5,2) - 1, isoString.substr(8,2))
	}
	
	$.fn.makeCalendar = function() {
		STRIPE_HEIGHT = 16; /* pixel height of a stripe for a multi-day event, including spacing */
		DAY_WIDTH = 93; /* pixel width of a day cell */
		
		this.each(function() {
			var listingClassNames = $(this).attr('class').split(' ');
			for (var i = 0; i < listingClassNames.length; i++) {
				var match = listingClassNames[i].match(/for_month_(\d+)-(\d+)/);
				if (match) {
					var year = match[1];
					var month = match[2];
					break;
				}
			}
			
			/* Get event details as an array of simple structs */
			var events = [];
			$('.vevent', this).each(function() {
				var startDate = dateFromIso($('.dtstart', this).attr('title'));
				var endDateIso = $('.dtend', this).attr('title');
				var endDate;
				if (endDateIso && endDateIso != '') {
					endDate = dateFromIso(endDateIso);
				} else {
					endDate = startDate;
				}
				var eventType = null;
				
				/* look for a class name matching event_type_* */
				var eventClassNames = $(this).attr('class').split(' ');
				for (var i = 0; i < eventClassNames.length; i++) {
					var match = eventClassNames[i].match(/^event_type_.*/);
					if (match) {
						eventType = match[0];
						break;
					}
				}
				
				events[events.length] = {
					'startDate': startDate,
					'endDate': endDate,
					'numberOfDays': Math.round((endDate - startDate) / (1000*60*60*24)) + 1,
					'summary': $('.summary', this).text(),
					'type': eventType,
					'eventId': $('a[id]', this).attr('id').match(/^event_(\d+)$/)[1]
				};
			})
			
			var eventIndex = 0; /* will step through the events list as we count through calendar dates */
			
			/* add calendar table to the doc */
			var calendarTable = $('<table class="calendar"></table>');
			$(this).after(calendarTable);
			
			/* Create day headings */
			var daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
			var headerRow = $('<tr></tr>');
			calendarTable.append(headerRow);
			
			for (var i = 0; i < daysOfWeek.length; i++) {
				dayHeader = $('<th></th>').text(daysOfWeek[i]);
				headerRow.append(dayHeader);
			}
			
			/* Determine day to start the calendar on */
			var firstDayOfMonth = new Date(year, month-1, 1); /* JS months are zero-based */
			/* Monday => getDay=1, subtract no days; Tuesday => getDay=2, subtract 1 day etc.
			Sunday => getDay=0, subtract 6 days */
			var daysToSubtract = (firstDayOfMonth.getDay() + 6) % 7;
			
			var currentDate = new Date(year, month-1, 1 - daysToSubtract);
			var calendarEndDate = new Date(year, month, 1);
			/* i.e. the first day of the following month (month is zero-based) */
			
			var now = new Date();
			var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
			
			/* keep track of multi-day events and their vertical positions,
				so that we know where to slot in new ones */
			var openEvents = [];
			
			/* Repeatedly add week rows until we've passed the end of the month */
			while(currentDate < calendarEndDate) {
				/* create a new week row */
				var weekRow = $('<tr></tr>');
				calendarTable.append(weekRow);
				
				/* generate 7 cells */
				for (i = 0; i < 7; i++) {
					var dayCell = $('<td></td>');
					weekRow.append(dayCell);
					dayCell.text(currentDate.getDate());
					
					var eventsContainer = $('<div class="events"></div>');
					dayCell.append(eventsContainer);
					
					/* add a class to distinguish days which are before or after the month we're interested in */
					if (currentDate.getMonth() != firstDayOfMonth.getMonth()) {
						dayCell.addClass('other_month');
					}
					
					if (currentDate.getTime() == today.getTime()) {
						dayCell.addClass('today');
					}
					
					/* if this is the start of a row, repeat the element for any events which are still open */
					if (i == 0) {
						for (j = 0; j < openEvents.length; j++) {
							var event = openEvents[j];
							if (event) {
								var eventHtml = createEventElement(event, eventsContainer);
								
								var daysRemaining = Math.round((event.endDate - currentDate) / (1000*60*60*24)) + 1;
								eventHtml.css({
									top: (stripeIndex * STRIPE_HEIGHT) + 'px',
									width: (Math.min(7, daysRemaining) * DAY_WIDTH) + 'px'
								});
							}
						}
					}
					
					/* keep single day events in a list, to insert after the multi-day ones */
					var singleDayEvents = []
					
					/* keep a counter of the 'stripe' row index at which we can insert a new event */
					var stripeIndex = 0;
					
					/* look for events that have begun by this date */
					while (events[eventIndex] && events[eventIndex].startDate <= currentDate) {
						var event = events[eventIndex];
						
						if (event.numberOfDays > 1) {
							/* insert at the next free stripe position */
							while (openEvents[stripeIndex] != null) stripeIndex++;
							openEvents[stripeIndex] = event;
							
							var eventHtml = createEventElement(event, eventsContainer);
							
							var daysLeftThisRow = 7 - i;
							var daysRemaining = Math.round((event.endDate - currentDate) / (1000*60*60*24)) + 1;
							
							eventHtml.css({
								top: (stripeIndex * STRIPE_HEIGHT) + 'px',
								width: (Math.min(daysLeftThisRow, daysRemaining) * DAY_WIDTH) + 'px'
							});
						} else {
							singleDayEvents.push(event);
						}
						
						eventIndex++;
					}
					
					if (singleDayEvents.length) {
						var singleDayEventsContainer = $('<ul class="single_day_events"></ul>');
						eventsContainer.append(singleDayEventsContainer);
						
						/* single day events need to go after all the multi-day ones vertically,
						as the text may wrap */
						nextEventIndex = openEvents.length;
						while (nextEventIndex > 0 && openEvents[nextEventIndex-1] == null) nextEventIndex--;
						singleDayEventsContainer.css({top: (nextEventIndex * STRIPE_HEIGHT) + 'px'});
						
						for (var j = 0; j < singleDayEvents.length; j++) {
							var event = singleDayEvents[j];
							var eventLi = $('<li></li>');
							singleDayEventsContainer.append(eventLi);
							var eventHtml = createEventElement(event, eventLi);
						}
					}
					
					/* close any open events that end on this date */
					for (j = 0; j < openEvents.length; j++) {
						var event = openEvents[j];
						if (event && event.endDate.getTime() == currentDate.getTime()) {
							openEvents[j] = null;
						}
					}
					
					/* advance to next date */
					currentDate.setDate(currentDate.getDate() + 1);
				}
			}
		})
	};
	
	var lightboxElement;
	var lightboxBody;
	function initLightbox() {
	
		function onShow(opts) {
			var trigger = opts.t;
			/* look for an 'event_123' ID and use that with /applications/150th/event_lightbox_info.rm,
			falling back on href if present */
			var eventIdMatch;
			var href;
			if (trigger.id && (eventIdMatch = trigger.id.match(/^event_(\d+)$/))) {
				href = '/applications/150th/event_lightbox_info.rm?id=' + eventIdMatch[1];
			} else {
				href = trigger.href;
			}
			lightboxBody.load(href, function() {opts.w.show()});
		}
		
		lightboxElement = $('<div class="jqmWindow"></div>');
		var closeButton = $('<a href="javascript:void(0)" class="close_button">close</a>');
		lightboxElement.append(closeButton);
		closeButton.click(function() {
			lightboxElement.jqmHide();
		})
		lightboxBody = $('<div class="lightbox_body"></div>');
		lightboxElement.append(lightboxBody);
		$('body').append(lightboxElement);
		lightboxElement.hide().jqm({onShow: onShow});
	}
	
	$.fn.applyEventLightbox = function() {
		lightboxElement.jqmAddTrigger(this);
	}
	
	$(function() {
		$('.calendar_container').show();
		initLightbox();
		$('.events_listing').makeCalendar();
		$('.event_link').applyEventLightbox();
		$('.events_listing').hide();
		/*$('.calendar').hide();
		var viewToggleLink = $('<a href="javascript:void(0)">View as a calendar</a>')
		$('.view_toggle_link_container').html(viewToggleLink);
		viewToggleLink.toggle(function() {
			viewToggleLink.text('View as a calendar');
			$('.events_listing').show();
			$('.calendar').hide();
		}, function() {
			viewToggleLink.text('View as a listing');
			$('.events_listing').hide();
			$('.calendar').show();
		});*/
	});
	
})(jQuery);

