/* Date extensions --------------------------------------------------------------*/

Date.prototype.getCorrectDay = function () {
	// In my opinion, monday = 1 and sunday = 7
	var day = this.getDay();
	if (day === 0) {
		return 7;
	}
	else {
		return day;
	}
};
Date.prototype.getNextDay = function () {
	return new Date(this.getFullYear(), this.getMonth(), this.getDate() + 1);
};
Date.prototype.getPreviousDay = function () {
	return new Date(this.getFullYear(), this.getMonth(), this.getDate() - 1);
};
Date.prototype.getFirstDayOfWeek = function () {
	return new Date(this.getFullYear(), this.getMonth(), this.getDate() - (this.getCorrectDay() - 1));
};
Date.prototype.getLastDayOfWeek = function () {
	return new Date(this.getFullYear(), this.getMonth(), this.getFirstDayOfWeek().getDate() + 6);
};
Date.prototype.getFirstDayOfMonth = function () {
	return new Date(this.getFullYear(), this.getMonth(), 1);
};
Date.prototype.getLastDayOfMonth = function () {
	return new Date(this.getFullYear(), this.getMonth() + 1, 0);
};
Date.prototype.isFirstDayOfWeek = function () {
	return this.getCorrectDay() == 1;
};
Date.prototype.isLastDayOfWeek = function () {
	return this.getCorrectDay() == 7;
};
Date.prototype.isToday = function () {
	return this.isTheSameAs(new Date());
};
Date.prototype.compareTo = function (otherDate) {
	return this.getTime() - otherDate.getTime();
};
Date.prototype.isTheSameAs = function (otherDate) {
	return this.compareTo(otherDate) === 0;
};
Date.prototype.getMonthName = function () {
	return Date.i18n.monthNames[this.getMonth()];
};
Date.prototype.toISODateString = function () {
	var y = this.getFullYear(),
	    m = (this.getMonth() + 1).toString().replace(/^(\d)$/, '0$1'),
	    d = (this.getDate()).toString().replace(/^(\d)$/, '0$1');
	return y + '-' + m + '-' + d;
};

Date.parseISODate = function (string) {
	var split, date;
	split = string.split('-');
	date = new Date(split[0], split[1] - 1, split[2]);
	return date;
};

Date.i18n = {
	monthNames: ['Januari','Februari','Mars','April','Maj','Juni', 'Juli','Augusti','September','Oktober','November','December'],
	monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', 'Jul','Aug','Sep','Okt','Nov','Dec'],
	dayNamesShort: ['Mån','Tis','Ons','Tor','Fre','Lör','Sön'],
	dayNames: ['Måndag','Tisdag','Onsdag','Torsdag','Fredag','Lördag','Söndag'],
	dayNamesMin: ['Må','Ti','On','To','Fr','Lö','Sö']
};


/* Month --------------------------------------------------------------*/

Month = function (date) {
	date = date || new Date();
	this.firstDay = date.getFirstDayOfMonth();
	this.lastDay  = date.getLastDayOfMonth();
};

Month.prototype = {
	getName: function () {
		return this.firstDay.getMonthName();
	},
	getYear: function () {
		return this.firstDay.getFullYear();
	}
};


/* Pick-a-date --------------------------------------------------------------*/

PickADate = function (options) {
	var self = this;
	this.options = $.extend({
		target: null,
		hideTarget: false,
		date: new Date(),
		// Format for calendar heading
		// {0} is replaced with date in human readable format
		headingFormat: '{0}',
		previousAndNextMode: PickADate.PrevoiusAndNextModes.MONTHNAMES
	}, options || {});

	// Set month
	this.currentMonth = new Month(this.options.date);

	// Create and hide containers
	this.containerElement = $('<div class="pick-a-date"></div>');
	this.hide();
	this.heading = $('<h3></h3>');
	this.navContainer = $('<div class="navigation-container cf"></div>');
	this.tableContainer = $('<div class="table-container"></div>');
	this.containerElement.append(this.heading, this.tableContainer, this.navContainer);

	// Setup navigation
	this.goBack = $('<a href="#" class="prev"></a>');
	this.goForward = $('<a href="#" class="next"></a>');
	this.goBack.click(function (e) {
		e.preventDefault();
		self.showLastMonth();
	});
	this.goForward.click(function (e) {
		e.preventDefault();
		self.showNextMonth();
	});
	this.navContainer.append(this.goBack, this.goForward);
	this.setPrevNextTexts();
	
	// Observe date clicks
	this.tableContainer.click(function (e) {
		var newDate;
		if (e.target.tagName.toLowerCase() == "a") {
			e.preventDefault();
			newDate = Date.parseISODate(e.target.title);
			if (self.activeDate && newDate.isTheSameAs(self.activeDate)) {
				self.unsetDate();
			} else {
				self.setDate(newDate);
			}
			if (self.options.target) {
				self.options.target.focus();
			}
			self.updateView();
		}

	});
	
	// Bind to target, if supplied
	if (this.options.target) {
		this.options.target.bind('focus', function () {
			self.show();
		});
		this.options.target.change(function (e) {
			self.setDate(Date.parseISODate(self.options.target.val()));
		});
		this.options.target.after(this.containerElement);
		
		// Hide target
		if (this.options.hideTarget) {
			this.options.target.hide();
		}
		if (this.options.hideTargetLabel) {
			this.options.target.getLabel().hide();
		}
	}

	// Set date
	this.setDate(this.options.date);
	this.updateView();
	
};

PickADate.PrevoiusAndNextModes = {};
PickADate.PrevoiusAndNextModes.MONTHNAMES = 0;
PickADate.PrevoiusAndNextModes.PREVOIUSANDNEXT = 1;

PickADate.prototype = {
	activeDate: null,
	setDate: function (newDate) {
		newDate = Date.parseISODate(newDate.toISODateString());
		if (!this.activeDate || this.activeDate && newDate.getMonth() != this.activeDate.getMonth() || newDate.getYear() != this.activeDate.getYear()) {
			this.activeDate = newDate;
			this.changeMonth(newDate); // changeMonth updates the view
		} else {
			this.activeDate = newDate;
		}
		// Update target value, if any
		if (this.options.target) {
			this.options.target.val(this.activeDate.toISODateString());
		}
	},
	unsetDate: function () {
		this.activeDate = null;
		// Update target value, if any
		if (this.options.target) {
			this.options.target.val('');
		}
	},
	changeMonth: function (date) {
		this.currentMonth = new Month(date);
		this.updateView();
	},
	showNextMonth: function () {
		this.changeMonth(this.currentMonth.lastDay.getNextDay());
	},
	showLastMonth: function () {
		this.changeMonth(this.currentMonth.firstDay.getPreviousDay());
	},
	show: function () {
		this.containerElement.show();
	},
	hide: function () {
		this.containerElement.hide();
	},
	setPrevNextTexts: function () {
		var prevMonth, nextMonth;
		switch (this.options.previousAndNextMode) {
			case PickADate.PrevoiusAndNextModes.PREVOIUSANDNEXT:
				this.goBack.text('Föregående');
				this.goBack.attr('title', 'Föregående månad');
				this.goForward.text('Nästa');
				this.goForward.attr('title', 'Nästa månad');
			break;
			case PickADate.PrevoiusAndNextModes.MONTHNAMES:
				prevMonth = this.currentMonth.firstDay.getPreviousDay();
				nextMonth = this.currentMonth.lastDay.getNextDay();
				this.goBack.text(prevMonth.getMonthName());
				this.goBack.attr('title', '{0} {1}'.format(prevMonth.getMonthName(), prevMonth.getFullYear()));
				this.goForward.text(nextMonth.getMonthName());
				this.goForward.attr('title', '{0} {1}'.format(nextMonth.getMonthName(), nextMonth.getFullYear()));
			break;
			default:
		}
	},
	renderMonthTable: function () {
		var d, 
		    str   = "",
		    tdClass = [],
		    start = this.currentMonth.firstDay.getFirstDayOfWeek(),
		    stop  = this.currentMonth.lastDay.getLastDayOfWeek();
		    
		str += "<table>";
		
		str += '<caption>' + this.currentMonth.firstDay.getMonthName() + ' ' + this.currentMonth.firstDay.getFullYear() + '</caption>';
		
		// Render table head
		str += '<thead class="structural"><tr>';
		Date.i18n.dayNamesMin.each(function () {
			str += '<th>' + this + '</th>';
		});
		str += '</tr></thead>';
		
		// Render table body
		str += '<tbody>';
		d = start;
		// Render six full weeks so that table height always stays the same
		for (i = 0, e = 42; i < e; i++) {
			str += d.isFirstDayOfWeek() ? '<tr>' : '';
			// Is this day part of another month than the current one?
			if (d.getMonth() != this.currentMonth.firstDay.getMonth()) {
				tdClass.push('other-month');
			}
			// Is this day the chosen date?
			if (this.activeDate && d.isTheSameAs(this.activeDate)) {
				tdClass.push('active');
			}
			// Is this day today?
			if (d.isToday()) {
				tdClass.push('today');
			}
			str += '<td' + (tdClass.length ? (' class="' + tdClass.join(' ') + '"') : '') + '>';
			str += '<a href="#" title="' + d.toISODateString() + '">';
			// Wrap date in strong if this day is the chosen date
			str += (this.activeDate && d.isTheSameAs(this.activeDate)) ? '<strong>' + d.getDate() + '</strong>' : d.getDate();
			str += '</a>';
			str += d.isToday() ? '</strong>' : '';
			str += '</td>';
			str += d.isLastDayOfWeek() ? '</tr>' : '';
			// Reset td class
			tdClass = [];
			d = d.getNextDay();
		}
		str += '</tbody>';
		str += "</table>";
		return str;
	},
	updateView: function () {
		if (this.activeDate) {
			this.heading.text(this.options.headingFormat.format([this.activeDate.getDate(), this.activeDate.getMonthName(), this.activeDate.getFullYear()].join(' ')));
		} else {
			this.heading.text(this.options.headingFormat.format(' (inget datum valt)'));
		}
		this.tableContainer.html(this.renderMonthTable());
		this.setPrevNextTexts();
		this.tableContainer.find('td.active a').focus();
	}
};