var ARR_MONTHS = ["Gener","Febrer","Març","Abril","Maig","Juny","Juliol","Agost","Setembre","Octubre","Novembre","Desembre"];
var ARR_WEEKDAYS = ["Dg", "Dl", "Dm", "Dc", "Dj", "Dv", "Ds"];
var NUM_WEEKSTART = 1;
var LOG_SHOWERROR = true; // show error message
var LOG_SETLIMIT  = true; // set closest allowed date
var NUM_YEARSBEFORE = 4;
var NUM_YEARSAFTER  = 4;

var calendars = [];

function cal_parse_date (str_date) {
		var re_date = /^(\d+)\/(\d+)\/(\d+)$/;
		if (!re_date.exec(str_date))
			return alert("Parsing error: unsupported date format '" + str_date + "'");
		return (new Date (RegExp.$3, RegExp.$2-1, RegExp.$1));
	}
	function cal_generate_date (dt_date) {
		return (
			new String (
				(dt_date.getDate() < 10 ? '0' : '') + dt_date.getDate() + '/'
				+ (dt_date.getMonth() < 9 ? '0' : '') + (dt_date.getMonth() + 1) + '/'
				+ dt_date.getFullYear()
			)
		);
	}

function calendar (str_date, str_form_name, str_ctrl_name, str_min_date, str_max_date) {

	this.get_html  = cal_get_html;
	this.get_body  = cal_get_body;
	this.set_year  = cal_set_year;
	this.set_month = cal_set_month;
	this.set_day   = cal_set_day;
	this.validate  = cal_validate;

	this.update = (document.body && document.body.innerHTML
		? cal_css_update : cal_rel_update);

	this.id = calendars.length;
	calendars[this.id] = this;

	var re_url = new RegExp('cal' + this.id + '_val=(\\d+)');
	var dt_params = (str_date ? cal_parse_date(str_date) : cal_date_only());
	this.dt_current = (re_url.exec(String(window.location))
		? new Date(new Number(RegExp.$1)) : dt_params);

	if (!str_form_name)
		return alert('Form name is required parameter of draw method.');
	if (!document.forms[str_form_name])
		return alert ("Form with name '" + this.form_name + "' can't be found in the document.");
	this.form_name = str_form_name;
	this.control_name = (str_ctrl_name ? str_ctrl_name : 'datetime_' + this.id);

	var re_num = /^-?\d+$/;
	if (str_min_date != null)
		this.min_date = (re_num.exec(str_min_date)
			? new Date (dt_params.valueOf() - new Number(str_min_date * 864e5))
			: cal_parse_date(str_min_date)
		);
	if (str_max_date != null)
		this.max_date = (re_num.exec(str_max_date)
			? new Date (dt_params.valueOf() + new Number(str_max_date * 864e5))
			: cal_parse_date(str_max_date)
		);

	document.write (this.get_html(this.dt_current));

	this.control_obj = document.forms[str_form_name].elements[this.control_name];
	this.mon_ctrl    = document.forms[this.form_name].elements['dt_mon_'  + this.id];
	this.year_ctrl   = document.forms[this.form_name].elements['dt_year_' + this.id];
}

function cal_css_update (dt_datetime) {
	this.dt_current = dt_datetime;
	this.control_obj.value = cal_generate_date(dt_datetime);

	var obj_container = (document.all ?
		document.all['cal_body_' + this.id] :
		document.getElementById('cal_body_' + this.id)
	);
	obj_container.innerHTML =  this.get_body(dt_datetime);
	this.mon_ctrl.selectedIndex = dt_datetime.getMonth();
	this.year_ctrl.selectedIndex =
		dt_datetime.getFullYear() - Number(this.year_ctrl.options[1].text) + 1;
	this.control_obj.value = cal_generate_date(dt_datetime);
}

function cal_rel_update (dt_datetime) {
	var arr_location = String(window.location).split('?');
	var arr_params = String(arr_location[1]).split('&');
	var num_found = -1;
	for (var i = 0; i < arr_params.length; i++)
		if((arr_params[i].split('='))[0] == 'cal' + this.id + '_val') {
			num_found = i;
			break;
		}
	arr_params[(num_found == -1 ? (arr_location[1] ? arr_params.length : 0) : num_found)]
		= 'cal' + this.id + '_val=' + dt_datetime.valueOf();

	this.control_obj.value = cal_generate_date(dt_datetime);
	window.location = arr_location[0] + '?' + arr_params.join('&');
}

function cal_set_day (num_datetime) {
	var dt_newdate = new Date(num_datetime);
	this.update(this.validate(dt_newdate));
}

function cal_set_month () {
	var dt_newdate = new Date(this.dt_current);
	var num_month = this.mon_ctrl.options[this.mon_ctrl.selectedIndex].value;

	dt_newdate.setMonth(num_month);
	if (num_month != dt_newdate.getMonth())
		dt_newdate.setDate(0);

	dt_newdate = this.validate(dt_newdate);
	if (dt_newdate == this.dt_current)
		this.mon_ctrl.selectedIndex = this.dt_current.getMonth();
	else
		this.update(dt_newdate);
}

function cal_set_year () {
	var dt_newdate = new Date(this.dt_current);
	var str_year = this.year_ctrl.options[this.year_ctrl.selectedIndex].text;
	var str_scroll = this.year_ctrl.options[this.year_ctrl.selectedIndex].value;

	var num_year;
	if (str_scroll) {
		num_year = (str_scroll == '+'
			? this.dt_current.getFullYear() + NUM_YEARSAFTER
			: this.dt_current.getFullYear() - NUM_YEARSBEFORE
		);
	}
	else
		num_year = new Number(str_year);
	dt_newdate.setFullYear(num_year);
	var num_month = this.mon_ctrl.options[this.mon_ctrl.selectedIndex].value;
	if (num_month != dt_newdate.getMonth())
		dt_newdate.setDate(0);

	dt_newdate = this.validate(dt_newdate);
	if (dt_newdate == this.dt_current) {
		this.year_ctrl.selectedIndex =
			this.dt_current.getFullYear() - Number(this.year_ctrl.options[1].text) + 1;
		return;
	}

	if (str_scroll) {
		this.year_ctrl.length = 0;
		this.year_ctrl.options[0] = new Option('<< ' + (num_year - NUM_YEARSBEFORE), '-');
		for (var i = 1; i < NUM_YEARSBEFORE + NUM_YEARSAFTER; i++) {
			this.year_ctrl.options[i] = new Option(num_year + i - NUM_YEARSBEFORE);
			this.year_ctrl.options[i].selected = !(i-NUM_YEARSBEFORE);
		}
		this.year_ctrl.options[NUM_YEARSBEFORE + NUM_YEARSAFTER] = new Option((num_year + NUM_YEARSAFTER) + ' >>', '+');
	}

	this.update(dt_newdate);
}

function cal_get_html (dt_datetime) {

	// print calendar header
	var i, str_buffer = new String (
		'<table cellspacing="0" class="calOuterTable" border="0"><tr>' +
		'<td><input type="Hidden" name="' + this.control_name + '" value="' + cal_generate_date(this.dt_current) + '">' +
		'<table cellspacing="0" class="calSelectTable"><tr><td>' +
		'<select name="dt_mon_' + this.id + '" class="calMonCtrl" onchange="return calendars[' + this.id + '].set_month();">');
	for (i = 0; i < 12; i++)
			str_buffer += '<option value="' + i + '"' + (i == dt_datetime.getMonth() ? ' selected' : '') + '>' + ARR_MONTHS[i] + "</option>\n";
	str_buffer += '</select></td>' +
		'<td align="right"><select name="dt_year_' + this.id + '" class="calYearCtrl" onchange="return calendars[' + this.id + '].set_year();">' +
		'<option value="-">&lt;&lt; ' + (dt_datetime.getFullYear() - NUM_YEARSBEFORE) + "</option>\n";

	for (i = 1 - NUM_YEARSBEFORE; i < NUM_YEARSAFTER; i++)
			str_buffer += '<option' + (i == 0 ? ' selected' : '') + '>' + (dt_datetime.getFullYear() + i) + "</option>\n";
	str_buffer += '<option value="+">' + (dt_datetime.getFullYear() + NUM_YEARSAFTER) +  " &gt;&gt;</option>\n" +
		'</select></td></tr></table></td></tr>' +
		'<tr><td><div id="cal_body_' + this.id + '">' + this.get_body(dt_datetime) +'</div>'
		+ '</td>'
		+ '</tr></table>';

	return (str_buffer);
}

function cal_get_body (dt_datetime) {

	// get same date in the previous year
	var dt_prev_year = new Date(dt_datetime);
	dt_prev_year.setFullYear(dt_prev_year.getFullYear() - 1);
	if (dt_prev_year.getDate() != dt_datetime.getDate())
		dt_prev_year.setDate(0);

	// get same date in the next year
	var dt_next_year = new Date(dt_datetime);
	dt_next_year.setFullYear(dt_next_year.getFullYear() + 1);
	if (dt_next_year.getDate() != dt_datetime.getDate())
		dt_next_year.setDate(0);

	// get first day to display in the grid for current month
	var dt_firstday = new Date(dt_datetime);
	dt_firstday.setDate(1);
	dt_firstday.setDate(1 - (7 + dt_firstday.getDay() - NUM_WEEKSTART) % 7);

	// print calendar body
	var str_buffer = new String (
	'<table cellspacing="0" cellpadding="1" class="calDaysTable" width="100%">');

	// print weekdays titles
	str_buffer += '<tr>';
	for (var n = 0; n < 7; n++)
		str_buffer += '<td class="calWTitle">' + ARR_WEEKDAYS[(NUM_WEEKSTART + n) % 7] + '</td>';
	// print calendar table
	str_buffer += "</tr>\n";

	var dt_current_day = new Date(dt_firstday);
	while (dt_current_day.getMonth() == dt_datetime.getMonth() ||
		dt_current_day.getMonth() == dt_firstday.getMonth()) {
		// print row heder
		str_buffer += '<tr>';
		for (var n_current_wday = 0; n_current_wday < 7; n_current_wday++) {

				// --- set background style ---
				if (dt_current_day.getDate() == dt_datetime.getDate() &&
					dt_current_day.getMonth() == dt_datetime.getMonth())
					// print current date
					str_buffer += '<td class="calDayCurrent" align="center" valign="middle">';
				else if (dt_current_day.getDay() == 0 || dt_current_day.getDay() == 6)
					// weekend days
					str_buffer += '<td class="calDayWeekend" align="center" valign="middle">';
				else
					// print working days of current month
					str_buffer += '<td class="calDayWorking" align="center" valign="middle">';

				// --- set foreground style ---
				// forbidden dates
				if (!this.validate(dt_current_day, true))
					str_buffer += '<span class="calForbDate">'
						+ dt_current_day.getDate() + '</span></td>';
				// dates of current month
				else if (dt_current_day.getMonth() == dt_datetime.getMonth())
					str_buffer += '<a href="javascript:calendars[' + this.id +
						'].set_day(' + dt_current_day.valueOf() + ');" class="calThisMonth">'
						+ dt_current_day.getDate() + '</a></td>';
				// dates of other months
				else
					str_buffer += '<a href="javascript:calendars[' + this.id +
						'].set_day(' + dt_current_day.valueOf() + ');" class="calOtherMonth">'
						+ dt_current_day.getDate() + '</a></td>';

				dt_current_day.setDate(dt_current_day.getDate() + 1);
		}
		// print row footer
		str_buffer += "</tr>\n";
	}
	// print calendar footer

	str_buffer +=
		'<tr><td colspan="7" align="center"><input type="submit" value="Veure" class="botodates"></td></tr></table>';

	return (str_buffer);
}

function cal_date_only (dt_datetime) {
	if (!dt_datetime)
		dt_datetime = new Date();
	dt_datetime.setHours(0);
	dt_datetime.setMinutes(0);
	dt_datetime.setSeconds(0);
	dt_datetime.setMilliseconds(0);
	return dt_datetime;
}

function cal_validate (dt_datetime, b_silent) {
	if (this.max_date && dt_datetime > this.max_date) {
		if (b_silent) return false;
		if (LOG_SHOWERROR) alert ('Sorry, dates after ' + cal_generate_date(this.max_date) + ' are not allowed.');
		return (LOG_SETLIMIT ? this.max_date : this.dt_current);
	}
	if (this.min_date && dt_datetime < this.min_date) {
		if (b_silent) return false;
		if (LOG_SHOWERROR) alert ('Sorry, dates before ' + cal_generate_date(this.min_date) + ' are not allowed.');
		return (LOG_SETLIMIT ? this.min_date : this.dt_current);
	}
	// more validation can be added here
	return dt_datetime;
}
