/*
 * tripedia.org
 * 
 * (c) Jan Poeschko
 * 
 */

MONTH_NAMES = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
MONTH_NAMES_SHORT = ['Jan.', 'Feb.', 'March', 'April', 'May', 'June', 'July', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'];
MONTH_DAYS = [31,28,31,30,31,30,31,31,30,31,30,31];
DAY_NAMES = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];

function min(a, b) {
	if (a < b)
		return a;
	return b;
}

function max(a, b) {
	if (a > b)
		return a;
	return b;
}

Calendar = function(parent, referenceDate, existingDates, start, stop, options) {
	//existingDates = existingDates || [];
	
	parent = $(parent);
	
	options = options || {};
	this.input = $(options.input) || null;
	this.singleDate = options.singleDate || false;
	options.msg = options.msg || {};
	this.msg = options.msg;
	if (this.singleDate)
		this.msg.init = this.msg.init || "Click the date!";
	else
		this.msg.init = this.msg.init || "Click the starting date!";
	this.msg.stop = this.msg.stop || "Click the end date!";
	this.msg.modify = this.msg.modify || "To modify the time range, click the (new) starting date!";
	
	this.div = new Element('div', {'class': 'calendar'});
	
	var div_monthyear = new Element('div');
	
	var prev_month = new Element('span', {'class': 'navbutton'}).setText('<');
	var next_month = new Element('span', {'class': 'navbutton'}).setText('>');
	div_monthyear.appendChild(prev_month);
	this.select_month = new Element('select');
	for (var month = 0; month < MONTH_NAMES.length; ++month)
		this.select_month.appendChild(new Element('option', {'value': month}).setText(MONTH_NAMES[month]));
	div_monthyear.appendChild(this.select_month);
	div_monthyear.appendChild(next_month);
	
	var prev_year = new Element('span', {'class': 'navbutton'}).setText('<');
	var next_year = new Element('span', {'class': 'navbutton'}).setText('>');
	div_monthyear.appendChild(prev_year);
	this.select_year = new Element('select');
	div_monthyear.appendChild(this.select_year);
	div_monthyear.appendChild(next_year);

	var calendar = this;
	//new Form.Element.Observer(select_month, 0.3, function(element, value) {
	Event.observe(this.select_month, 'change', function(event) {
		var value = Event.element(event).value;
		calendar.setMonth(value, calendar.year);
	});
	//new Form.Element.Observer(this.select_year, 0.3, function(element, value) {
	Event.observe(this.select_year, 'change', function(event) {
		var value = Event.element(event).value;
		calendar.setMonth(calendar.month, value);
	});
	
	Event.observe(prev_month, 'click', function(event) {
		var prev = new Month(calendar.month, calendar.year).prev();
		calendar.setMonth(prev.month, prev.year);
	});
	Event.observe(next_month, 'click', function(event) {
		var next = new Month(calendar.month, calendar.year).next();
		calendar.setMonth(next.month, next.year);
	});
	Event.observe(prev_year, 'click', function(event) {
		calendar.setMonth(calendar.month, calendar.year - 1);
	});
	Event.observe(next_year, 'click', function(event) {
		calendar.setMonth(calendar.month, calendar.year + 1);
	});
	//prev_month.setAttribute('unselectable', true);
	
	[prev_month, next_month, prev_year, next_year].each(function(element) {
		Event.observe(element, 'mouseover', function(event) {
			element.addClassName('over');
		});
		Event.observe(element, 'mouseout', function(event) {
			element.removeClassName('over');
		});
	});
	
	this.div.appendChild(div_monthyear);
	
	this.table = new Element('table');
	
	/*var thead = new Element('thead');
	var tr_month = new Element('tr');
	
	// month
	var th_prevmonth = new Element('th').setText('<');
	var td_month = new Element('td');
	var select_month = new Element('select');
	for (var month = 0; month < MONTH_NAMES.length; ++month)
		select_month.appendChild(new Element('option', {'value': month}).setText(MONTH_NAMES[month]));
	td_month.appendChild(select_month);
	var th_nextmonth = new Element('th').setText('>');
	tr_month.appendChild(th_prevmonth);
	tr_month.appendChild(td_month);
	tr_month.appendChild(th_nextmonth);
	
	// year
	var th_prevyear = new Element('th').setText('<');
	var td_year = new Element('td');
	var select_year = new Element('select');
	td_year.appendChild(select_year);
	var th_nextyear = new Element('th').setText('>');
	tr_month.appendChild(th_prevyear);
	tr_month.appendChild(td_year);
	tr_month.appendChild(th_nextyear);
	
	thead.appendChild(tr_month);*/

	Event.observe(this.div, 'mousedown', function(event) {
		Event.stop(event);
	});
	Event.observe(this.div, 'mouseover', function(event) {
		Event.stop(event);
	});
	Event.observe(this.div, 'mouseup', function(event) {
		Event.stop(event);
	});
	
	var thead = new Element('thead');
	
	var tr_days = new Element('tr');
	tr_days.appendChild(new Element('th'));
	DAY_NAMES.each(function(day) {
		tr_days.appendChild(new Element('th').setText(day));
	});
	thead.appendChild(tr_days);
	
	this.table.appendChild(thead);
	
	this.tbody = new Element('tbody');	
	this.table.appendChild(this.tbody);
	
	this.tfoot = new Element('tfoot');
	var tr = new Element('tr');
	this.helpCell = new Element('th');
	this.helpCell.colSpan = 8;
	tr.appendChild(this.helpCell);
	this.tfoot.appendChild(tr);
	this.table.appendChild(this.tfoot);
	
	this.div.appendChild(this.table);
	parent.appendChild(this.div);
	
	/*this.existingDates = existingDates;
	
	this.start = null;
	this.stop = null;
	this.overStart = null;
	this.overStop = null;
	
	var now = new Date();
	this.setMonth(now.getMonth(), now.getFullYear());
	this.setHelpText("Click the starting date!");*/
	
	this.reset(referenceDate, existingDates, start, stop);
};

Calendar.prototype.setMonth = function(monthIdx, year) {
	monthIdx = parseInt(monthIdx);
	year = parseInt(year);
	this.month = monthIdx;
	this.year = year;
	
	this.tbody.deleteChildNodes();
	var month = new Month(monthIdx, year);
	var days = month.getDays();
	var prevMonth = month.prev();
	var nextMonth = month.next();
	var firstDate = new Date(year, monthIdx, 1);
	var weekDay = firstDate.getDay();
	if (weekDay == 0)
		weekDay = 7;
	var tr = new Element('tr');
	var week = firstDate.getWeekNumber();
	tr.appendChild(new Element('th', {'class': 'week'}).setText(week));

	var calendar = this;
	
	function initDay(td, date) {
		td.date = date;
		Event.observe(td, 'mouseover', function(event) {
			td.addClassName('over');
			if (calendar.stop == null) {
				calendar.overStop = max(date, calendar.start);
				calendar.overStart = min(date, calendar.start);
			}
			calendar.refreshSelection();
		});
		Event.observe(td, 'mouseout', function(event) {
			td.removeClassName('over');
			calendar.overStop = null;
			calendar.overStart = null;
			calendar.refreshSelection();
		});
		Event.observe(td, 'click', function(event) {
			//alert(date);
			if (calendar.singleDate) {
				calendar.setValue(date, date);
				calendar.overStart = null;
				calendar.overStop = null;
			} else {
				if (calendar.start == null || calendar.stop != null) {
					//calendar.start = date;
					//calendar.stop = null;
					calendar.setValue(date, null);
					calendar.overStart = date;
					calendar.overStop = date;
					calendar.setHelpText(calendar.msg.stop);
				} else {
					var stop = max(date, calendar.start);
					var start = min(date, calendar.start);
					calendar.setValue(start, stop);
					calendar.overStart = null;
					calendar.overStop = null;
					calendar.setHelpText(calendar.msg.modify);
				}
			}
			calendar.refreshSelection();
		});
	}
	
	for (var prev = 1; prev < weekDay; ++prev) {
		var monthDay = prevMonth.getDays() - weekDay + prev + 1;
		var date = new Date(prevMonth.year, prevMonth.month, monthDay);
		var td = new Element('td', {'class': 'prevMonth'}).setText(monthDay);
		initDay(td, date);
		tr.appendChild(td);
	}
	for (var day = 1; day <= days; ++day) {
		var date = new Date(year, monthIdx, day);
		var td = new Element('td').setText(day);
		initDay(td, date);
		tr.appendChild(td);
		//alert(date);
		if (date.getDay() == 0 && day != days) {
			//alert(date);
			this.tbody.appendChild(tr);
			tr = new Element('tr');
			tr.appendChild(new Element('th', {'class': 'week'}).setText(++week));
		}
	}
	var currentWeekDay = (weekDay + days) % 7;
	if (currentWeekDay == 0)
		currentWeekDay = 7;
	else if (currentWeekDay == 1)
		currentWeekDay = 8;
	for (var next = currentWeekDay; next <= 7; ++next) {
		var monthDay = next - currentWeekDay + 1;
		var date = new Date(nextMonth.year, nextMonth.month, monthDay);
		var td = new Element('td', {'class': 'nextMonth'}).setText(monthDay);
		initDay(td, date);
		tr.appendChild(td);
	}
	this.tbody.appendChild(tr);
	
	this.select_month.value = monthIdx;
	
	this.select_year.deleteChildNodes();
	for (var otherYear = year - 10; otherYear <= year + 10; ++otherYear)
		this.select_year.appendChild(new Element('option', {'value': otherYear}).setText(otherYear));
	this.select_year.value = year;
	
	this.refreshSelection();
};

Calendar.prototype.refreshSelection = function() {
	//alert(this.start + ', ' + this.stop);
	var start = this.start;
	var stop = this.stop;
	var overStart = this.overStart;
	var overStop = this.overStop;
	var rangeStart = this.rangeStart;
	var rangeStop = this.rangeStop;
	var existingDates = this.existingDates;
	this.tbody.select('td').each(function(day) {
		//alert(day.date);
		var selected = (start != null && stop != null && day.date >= start && day.date <= stop) ||
			(stop == null && day.date == start);
		var overSelected = (overStart != null && overStop != null && day.date >= overStart && day.date <= overStop);
		var existing = existingDates.any(function(date) {
			return day.date >= date.start && day.date <= date.end;
		});
		var inRange = (rangeStart != null && rangeStop != null && day.date >= rangeStart && day.date <= rangeStop) ||
			(rangeStart == null && rangeStop == null);
		day.setClassName('outofrange', !inRange);
		day.setClassName('existing', existing);
		day.setClassName('selected', selected);
		day.setClassName('overSelected', overSelected);
		//if (selected)
		//	day.setText('S');
	});
	//alert(this.tbody.select('td.existing').length);
};

Calendar.prototype.setHelpText = function(text) {
	this.helpCell.setText(text);
};

Calendar.prototype.reset = function(reference, existingDates, start, stop, rangeStart, rangeStop) {
	existingDates = existingDates || [];
	//this.start = start || null;
	//this.stop = stop || null;
	this.setValue(start || null, stop || null);
	this.overStart = null;
	this.overStop = null;
	this.existingDates = existingDates;
	this.rangeStart = rangeStart;
	this.rangeStop = rangeStop;
	
	reference = reference || new Date();
	this.setMonth(reference.getMonth(), reference.getFullYear());
	this.setHelpText(this.msg.init);

	this.refreshSelection();
};

Calendar.prototype.setValue = function(start, stop) {
	this.start = start;
	this.stop = stop;
	if (this.input)
		this.input.value = this.getValue();
}

Calendar.prototype.getValue = function() {
	if (this.start == null)
		return '';
	else if (this.stop == null)
		//return this.start;
		//return this.start.toJSON();
		return dateToStr(this.start)
	else
		return dateToStr(this.start) + ' - ' + dateToStr(this.stop);
};

Month = function(month, year) {
	this.month = month;
	this.year = year;
};

Month.prototype.prev = function() {
	if (this.month == 0)
		return new Month(11, this.year - 1);
	else
		return new Month(this.month - 1, this.year);
}

Month.prototype.next = function() {
	if (this.month == 11)
		return new Month(0, this.year + 1);
	else
		return new Month(this.month + 1, this.year);
}

Month.prototype.getDays = function() {
	if (((0 == (this.year%4)) && ( (0 != (this.year%100)) || (0 == (this.year%400)))) && this.month == 1)
		return 29;
	else
		return MONTH_DAYS[this.month];
}

/** Returns the number of the week in year, as defined in ISO 8601. */
Date.prototype.getWeekNumber = function() {
	var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var DoW = d.getDay();
	d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
	var ms = d.valueOf(); // GMT
	d.setMonth(0);
	d.setDate(4); // Thu in Week 1
	return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
};

stopEvent = function(ev) {
	ev || (ev = window.event);
	if (Calendar.is_ie) {
		ev.cancelBubble = true;
		ev.returnValue = false;
	} else {
		ev.preventDefault();
		ev.stopPropagation();
	}
	return false;
};



/** Returns the number of days in the current month */
Date.prototype.getMonthDays = function(month) {
	var year = this.getFullYear();
	if (typeof month == "undefined") {
		month = this.getMonth();
	}
	if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
		return 29;
	} else {
		return Date._MD[month];
	}
};

/** Returns the number of day in the year. */
Date.prototype.getDayOfYear = function() {
	var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
	var time = now - then;
	return Math.floor(time / Date.DAY);
};

/** Checks date and time equality */
Date.prototype.equalsTo = function(date) {
	return ((this.getFullYear() == date.getFullYear()) &&
		(this.getMonth() == date.getMonth()) &&
		(this.getDate() == date.getDate()) &&
		(this.getHours() == date.getHours()) &&
		(this.getMinutes() == date.getMinutes()));
};

/** Set only the year, month, date parts (keep existing time) */
Date.prototype.setDateOnly = function(date) {
	var tmp = new Date(date);
	this.setDate(1);
	this.setFullYear(tmp.getFullYear());
	this.setMonth(tmp.getMonth());
	this.setDate(tmp.getDate());
};