// a class to look after the dates selects 
function DateSelector( daySelectId, monthYearSelectId, minOrMax ) {
	/* private vars */
	var ds = $(daySelectId);
	var ms = $(monthYearSelectId);
	var minDate;
	var maxDate;
	var observers = [];
	var selector = this;
	
	init();
	
	function init() {
		addEvent( ds, 'change', onChangeHandler );
		addEvent( ms, 'change', onChangeHandler );
	};
	
	function getMonthYear( optionElement ) {
		optionElement = optionElement || ms[ms.selectedIndex];
		if( !optionElement ) {
			return null;
		}
		var monthYear = /^(\w{3}) (\d{4})$/.exec( optionElement.text );
		if( monthYear ) {
			var ret = {};
			ret.month     		= new Date( '1 '+monthYear[1]+' 1970' ).getMonth(); //hehehe
			ret.year      		= parseInt( monthYear[2] );
			ret.lastDayInMonth 	= new Date( ret.year, ret.month+1, 0 ).getDate();
			return ret;
		}
		return null;
	};
	
	function getDay( optionElement ) {
		optionElement = optionElement || ds[ds.selectedIndex];
		if( !optionElement ) {
			return null;
		}
		var day = parseInt( optionElement.value ) || null;
		return day;
	};

	function checkMinDate( date ) {
		if( minDate && date.getTime() < minDate.getTime() ) {
			return minDate;
		}
		return date;
	};
	
	function checkMaxDate( date ) {
		if( maxDate && date.getTime() > maxDate.getTime() ) {
			return maxDate;
		}
		return date;
	};
	
	function checkDateRange( date ) {
		date = checkMinDate( date );
		date = checkMaxDate( date );
		return date;
	};
	
	function dateNoTime( date ) {
		date = date || new Date();
		date.setHours(0);
		date.setMinutes(0);
		date.setSeconds(0);
		date.setMilliseconds(0);
		return date;
	};
	
	function onChangeHandler( e ) {
		
		if( e.target.selectedIndex == 0 ) {
			resetSelects();
			return;
		}
		
		var date = selector.getDate();
		date	 = checkDateRange( date );
		setSelects( date );
		notify( date );
	};
	
	function setSelects( date ) {
		if( !date ) {
			return;
		}
		/* 
		 * assumes that the day select's index == day
		 * and that the month select's value == month+1
		 */
		ds.selectedIndex = date.getDate();
		for( var i = 0; i < ms.options.length; i++ ) {
			if( ms.options[i].value == date.getMonth()+1 ) {
				ms.selectedIndex = i;
				break;
			}
		}
		disableInvalidOptions();
	};
	
	function resetSelects() {
		ds.selectedIndex = 0;
		ms.selectedIndex = 0;
		enableAllOptions();
		notify( null );
	};
	
	function enableOpt( optElement ) {
		optElement.removeAttribute('disabled');
		$removeClass( 'disabled', optElement );
	};
	
	function disableOpt( optElement ) {
		optElement.setAttribute('disabled', 'disabled');
		$addClass( 'disabled', optElement );
	};
	
	function enableAllOptions() {
		for( var i = 0; i < ms.options.length; i++ ) {
			enableOpt( ms.options[i] );
		}
		for( var i = 0; i < ds.options.length; i++ ) {
			enableOpt( ds.options[i] );
		}
	};
	
	function disableInvalidOptions() {
		enableAllOptions();
		var selectedMonthYear = getMonthYear();
		var selectedDay = getDay();
		var opt;
		var enabled = [];
		
		if( minDate ) {
			var minMonthYearTot = minDate.getFullYear()*12+minDate.getMonth();
			var minDay = minDate.getDate();
		}
	
		if( maxDate ) {
			var maxMonthYearTot = maxDate.getFullYear()*12+maxDate.getMonth();
			var maxDay = maxDate.getDate();
		}
		
		if ( selectedMonthYear ) {
			var selectedMonthYearTot = selectedMonthYear.year*12+selectedMonthYear.month;
			var lastDayOfMonth = selectedMonthYear.lastDayInMonth;
		}

		
		// do the months
		for( var i = 0; i < ms.options.length; i++ ) {
			var iOpt = ms.options[i];
			var iOptVals = getMonthYear(iOpt);
			if( !iOptVals ) continue;
			var mMaxDay = selectedDay || iOptVals.lastDayInMonth;
			var mMinDay = selectedDay || 1;
			// disable if less than minDate
			if( minDate && new Date( iOptVals.year, iOptVals.month, mMaxDay ).getTime() < minDate.getTime() ) {
				disableOpt(iOpt);
			} else if( maxDate && new Date( iOptVals.year, iOptVals.month, mMinDay ).getTime() > maxDate.getTime() ) { // disable if greater than maxDate
				disableOpt(iOpt);
			} else {
				enabled.push(iOpt);
			}
		}
		
		// a hack which kicks in if there is only one possible month that could be selected
		if( enabled.length == 1 ) {
			selectedMonthYear = getMonthYear( enabled[0] );
			var selectedMonthYearTot = selectedMonthYear.year*12+selectedMonthYear.month;
			var lastDayOfMonth = selectedMonthYear.lastDayInMonth;
		}
		
		// do the days
		if( selectedMonthYearTot && minMonthYearTot && maxMonthYearTot
				&& selectedMonthYearTot == minMonthYearTot && selectedMonthYearTot == maxMonthYearTot ) {
			for( var i = 0; i < ds.options.length; i++ ) {
				opt = ds.options[i];
				if( opt.value > maxDay || opt.value < minDay || opt.value > lastDayOfMonth ) {
					disableOpt( opt );
				}
			}
		} else if( selectedMonthYearTot && minMonthYearTot
					&& selectedMonthYearTot == minMonthYearTot ) {
			for( var i = 0; i < ds.options.length; i++ ) {
				opt = ds.options[i];
				if( opt.value < minDay || opt.value > lastDayOfMonth ) {
					disableOpt( opt );
				}
			}
		} else if( selectedMonthYearTot && maxMonthYearTot
					&& selectedMonthYearTot == maxMonthYearTot ) {
			for( var i = 0; i < ds.options.length; i++ ) {
				opt = ds.options[i];
				if( opt.value > maxDay || opt.value > lastDayOfMonth ) {
					disableOpt( opt );
				}
			}
		} else {
			for( var i = 0; i < ds.options.length; i++ ) {
				opt = ds.options[i];
				if( lastDayOfMonth && opt.value > lastDayOfMonth ) {
					disableOpt(opt);
				}
			}
		}
		

	};
	
	function notify( date ) {
		for( var i = 0; i < observers.length; i++ ) {
			observers[i]( date );
		}
	};
	
	/* 
	 * priviledged public methods
	 * http://www.crockford.com/javascript/private.html
	 */
	
	this.attach = function( callback ) {
		observers.push( callback );
	};
	
	this.setMinDate = function( date ) {
		minDate = dateNoTime( date );
		disableInvalidOptions();
	};

	this.setMaxDate = function( date ) {
		if( date ) {
			maxDate = dateNoTime( date );
		} else {
			maxDate  = null;
		}
		disableInvalidOptions();
	};
	
	
	this.getDate = function() {
		var date = null;
		var today = dateNoTime();
		
		var day 	  = getDay();
		var monthYear = getMonthYear();
		
		if( day && monthYear ) {
			day = Math.min( day, monthYear.lastDayInMonth );
			date = new Date( monthYear.year, monthYear.month, day );
		} else if( monthYear ) {
			if( minOrMax == 'min' ) {
				day = 1;
			} else {
				day = monthYear.lastDayInMonth;
			}
			date = new Date( monthYear.year, monthYear.month, day );
		} else if ( day ) {
			/* find first month that has this day and is not outside the bounds*/
			var iMonth = today.getMonth();
			var minDate = checkMinDate( today );
			while( day > new Date( today.getFullYear(), iMonth+1, 0).getDate()
				   || new Date( today.getFullYear(), iMonth, day ).getTime() < minDate.getTime() ) {
				iMonth++;
			}
			date = new Date( today.getFullYear(), iMonth, day );
		}
		
		return date;
	};


};


