JSDate_arr = new Array();
var JSDate = new Class({
  	initialize: function(opt) {
		// init target (id of text input to put date in)
		if (opt.target == null || $(opt.target) == null) return;
		this.target = opt.target;
		
		// container that will hold the control		
		if (opt.cont != null && $(opt.cont) != null)
			this.cont = $(opt.cont);
		else
			this.cont = $(document.body);
		
		// init handlers
		this.handlers = new Object;
		this.handlers.cont = 'jsdate_' + this.target + '_cont';
		this.handlers.hide = 'jsdate_' + this.target + '_hide';
		this.handlers.empty = 'jsdate_' + this.target + '_empty';
		this.handlers.year = 'jsdate_' + this.target + '_year';
		this.handlers.year_prev = 'jsdate_' + this.target + '_year_prev';
		this.handlers.year_next = 'jsdate_' + this.target + '_year_next';
		this.handlers.month = 'jsdate_' + this.target + '_month';
		this.handlers.month_prev = 'jsdate_' + this.target + '_month_prev';
		this.handlers.month_next = 'jsdate_' + this.target + '_month_next';
		this.handlers.days = 'jsdate_' + this.target + '_days';
		this.handlers.day = 'jsdate_' + this.target + '_day';
		
		// bind target to jsdate
		$(this.target).addEvents({
			'click': this.show.bind(this),
			'focus': this.show.bind(this)
		});
		
		// opened control flag
		this.opened = false;
		
		// constraints
		if (opt.ref_min != null && $(opt.ref_min) != null)
			this.ref_min = $(opt.ref_min);
		this.present = false;
		if (opt.present != null && opt.present)
			this.present = true;
			
		this.maintainInstances();
	},
	
	// debug instances
	debugInstances: function() {
		var elem = new Array();
		for (var i = 0; i < JSDate_arr.length; i++)
			if (typeof(JSDate_arr[i]) != 'undefined')
				elem.push(JSDate_arr[i].target);
		alert(elem.join(', '));
	},
	
	// instances garbage cleaner
	maintainInstances: function() {
		for (var i = 0; i < JSDate_arr.length; i++)
			if (typeof(JSDate_arr[i]) != 'undefined' && (JSDate_arr[i].target == this.target || $(JSDate_arr[i].target) == null))
				delete JSDate_arr[i];
		JSDate_arr.push(this);
	},
	
	// hide all other instances
	clearInstances: function() {
		for (var i = 0; i < JSDate_arr.length; i++)
			if (typeof(JSDate_arr[i]) != 'undefined' && JSDate_arr[i] != this)
				JSDate_arr[i].hide();
	},
	
	show: function() {
		if (this.opened) return false;
		this.opened = true;
		
		// close all other opened controls
		this.clearInstances();
		
		// hide selects (IE6 bug)
		this.toggleSelects('hidden');
		
		// control top: year and month navs
		var html =
			'<div style="float: right"><strong><span id="' + this.handlers.month + '"></span></strong> <a href="#nogo" id="' + this.handlers.month_prev + '">&laquo;</a> <a href="#nogo" id="' + this.handlers.month_next + '">&raquo;</a></div>' +
			'<a href="#nogo" id="' + this.handlers.year_prev + '">&laquo;</a> <a href="#nogo" id="' + this.handlers.year_next + '">&raquo;</a> <strong><span id="' + this.handlers.year + '"></span></strong>';
		// days
		// control middle: days table
		html += '<table cellpadding="0" cellspacing="0" class="jsdate_days" id="' + this.handlers.days + '">';
		html += '<thead><tr align="right"><td>su</td><td>mo</td><td>tu</td><td>we</td><td>th</td><td>fr</td><td>sa</td></tr></thead>';
		for (var i = 1; i <= 42; i++) {
			if (i % 7 == 1) html += '<tr align="right">';
			html += '<td><a href="#nogo" id="' + this.handlers.day + i + '"></a></td>';
			if (i % 7 == 0) html += '</tr>';
		}
		html += '</table>';
		// control footer: close link
		html +=
			'<a href="#nogo" id="' + this.handlers.hide + '" class="jsdate_hide">close</a>' +
			'<a href="#nogo" id="' + this.handlers.empty + '" class="jsdate_empty">empty</a>' +
			'<div class="clr">';
		var id = this.handlers.cont;
		var element  = new Element('div', { id: id });
		element.addClass('jsdate_cont');
		element.setStyle('display', 'none');
		element.set('html', html);
		this.cont.grab(element);
		
		// bind prev/next events for year and month
		$(this.handlers.year_prev).addEvent('click', this.navYear.bindWithEvent(this, -1));
		$(this.handlers.year_next).addEvent('click', this.navYear.bindWithEvent(this, 1));
		$(this.handlers.month_prev).addEvent('click', this.navMonth.bindWithEvent(this, -1));
		$(this.handlers.month_next).addEvent('click', this.navMonth.bindWithEvent(this, 1));
		$(this.handlers.hide).addEvent('click', this.hide.bind(this));
		$(this.handlers.empty).addEvent('click', this.empty.bind(this));
		$(this.handlers.days).addEvent('click', this.dateClick.bind(this));

		// calculate position to show control at
		var pos = $(this.target).getPosition(this.cont);
		pos.y += $(this.target).getSize().y + 1;
		$(this.handlers.cont).setStyles({ left: pos.x + 'px', top: pos.y + 'px', display: 'block' });
		
		// initial date
		this.setConstraints();
		this.setCurrentDate();		
		this.fillDays();
	},
	
	hide: function() {
		this.opened = false;
		if ($(this.handlers.cont) != null)
			$(this.handlers.cont).dispose();
		
		// show selects (IE6 bug)
		this.toggleSelects('visible');
	},
	
	empty: function() {
		this.opened = false;
		if ($(this.handlers.cont) != null)
			$(this.handlers.cont).dispose();
		$(this.target).value = '';
	},
	
	navYear: function() {
		var inc = arguments[1];
		this.setCurrentDate(this.date.year + inc, this.date.month);
		this.fillDays();
	},
	
	navMonth: function() {
		var inc = arguments[1];
		this.setCurrentDate(this.date.year, this.date.month + inc);
		this.fillDays();
	},
	
	fillDays: function() {
		var days = new Array(12);
		days[0] = 31;
		if ((this.date.year % 4 == 0 && this.date.year % 100 != 0) || this.date.year % 400 == 0)
			days[1] = 29;
		else
			days[1] = 28;
		days[2] = 31;
		days[3] = 30;
		days[4] = 31;
		days[5] = 30;
		days[6] = 31;
		days[7] = 31;
		days[8] = 30;
		days[9] = 31;
		days[10] = 30;
		days[11] = 31;
		var no_days = days[this.date.month];
		
		// empty current setup
		for (var i = 1; i <= 42; i++) {
			$(this.handlers.day + i).set('html', '');
			$(this.handlers.day + i).setStyle('display', 'none');
			$(this.handlers.day + i).className = '';
		}
			
		// fill right values
		var date = new Date();
		var date_Y = date.getFullYear();
		var date_m = date.getMonth();
		var date_d = date.getDate();
		var date_full = new Date(date_Y, date_m, date_d, 12);
		for (var i = 1; i <= no_days; i++) {
			$(this.handlers.day + (this.date.day + i)).set('html', i);
			$(this.handlers.day + (this.date.day + i)).setStyle('display', 'block');
			
			if (this.present && (new Date(this.date.year, this.date.month, i, 12) < date_full))
				$(this.handlers.day + (this.date.day + i)).addClass('dimmed');
				
			if (this.date_min != null && (new Date(this.date.year, this.date.month, i, 12) < new Date(this.date_min.year, this.date_min.month, this.date_min.date, 12)))
				$(this.handlers.day + (this.date.day + i)).addClass('dimmed');
		}
		
		if (this.date.year == date_Y && this.date.month == date_m)
			$(this.handlers.day + (this.date.day + date_d)).addClass('today');
				
		if (this.date_sel != null && this.date.year == this.date_sel.year && this.date.month == this.date_sel.month)
			$(this.handlers.day + (this.date.day + this.date_sel.date)).addClass('picked');
	},

	setCurrentDate: function() {
		// set current month
		if (arguments.length == 2)
			var date = new Date(arguments[0], arguments[1], 1, 12);
		else
			if (this.date_sel != null)
				var date = new Date(this.date_sel.year, this.date_sel.month, 1, 12);
			else
				if (this.date_min != null)
					var date = new Date(this.date_min.year, this.date_min.month, 1, 12);
				else
					var date = new Date();
			
		// define current month
		this.date = new Object;
		this.date.year = date.getFullYear();
		this.date.month = date.getMonth();
		date = new Date(this.date.year, this.date.month, 1, 12);
		this.date.day = date.getDay();

		// put correct month and year in navigation
		$(this.handlers.year).set('html', this.date.year);
		$(this.handlers.month).set('html', this.getMonthLabel(this.date.month));
	},
	
	setConstraints: function() {
		// set selected date if any
		this.date_sel = null;
		if ($(this.target).value) {
			var tmp = $(this.target).value.split('-');
			var date = new Date(tmp[0], tmp[1] - 1, tmp[2], 12);
			this.date_sel = new Object;
			this.date_sel.year = date.getFullYear();
			this.date_sel.month = date.getMonth();
			this.date_sel.date = date.getDate();
		}			
		
		// set minimum reference date if any
		this.date_min = null;
		if (this.ref_min != null && this.ref_min.value) {
			var tmp = this.ref_min.value.split('/');
			var date = new Date(tmp[2], tmp[0] - 1, tmp[1], 12);
			this.date_min = new Object;
			this.date_min.year = date.getFullYear();
			this.date_min.month = date.getMonth();
			this.date_min.date = date.getDate();
		}
	},
	
	dateClick: function(event) {
		var element = event.target;
		if ('A' == element.tagName && element.className.indexOf('dimmed') == -1) {
			$(this.target).value =
				this.date.year + '-' +
				(this.date.month + 1 < 10 ? '0' + (this.date.month + 1) : this.date.month + 1) + '-' + 
				(element.innerHTML < 10 ? '0' + element.innerHTML : element.innerHTML);
			this.hide();
		}
	},

	getMonthLabel: function(m) {
		switch (m) {
			case 0:
				return 'January';
				break;
			case 1:
				return 'February';
				break;
			case 2:
				return 'March';
				break;
			case 3:
				return 'April';
				break;
			case 4:
				return 'May';
				break;
			case 5:
				return 'June';
				break;
			case 6:
				return 'July';
				break;
			case 7:
				return 'August';
				break;
			case 8:
				return 'September';
				break;
			case 9:
				return 'October';
				break;
			case 10:
				return 'November';
				break;
			default:
				return 'December';
		}
	},
	
	toggleSelects: function(state) {
		return;
		
		if (Prototype.Browser.IE && document.getElementsByTagName) {
			var selects = document.getElementsByTagName('select');
			
			for (i = 0; i < selects.length; i++)
				Element.setStyle(selects[i], { visibility: state });
		}
	}
})