
var MCalendar = new Class({
	Implements: [Events, Options],

	options: {
		inputField:     null,
		displayArea:    null,
		dayStart:		1,
		direcction:		0,
		navigation:		2, // 0: Mes actual, 2: Navegación por meses, 4: Navegación por meses y años.
		weekendDays:	[0,6],
		dateF:			"{dd}/{mm}/{YYYY}",
		legendF:		"{A}, {e} de {B} de {YYYY}",
		tittleF:		"{Month}, {YYYY}",
		days:			[],
		daysT:			[],
		months:			[],
		monthsT:		[],
		monthsDays:		[],
		controls:		[],
		controlsT:		[],
		onSelect:		null,
		onUpdate:       null,
		date:           new Date(),
		position:       null,
		availableDays:	null,
		blockNonAvailables: false
	},
	
	initialize: function(options) {
		this.setOptions(options);
		//todo JGM: esto no debería asignarse de forma directa, es el método de setOptions de la clase Options el que
        // debe asignarlo, pero no lo hace y de momento no tenemos tiempo.
		this.options.onSelect = options.onSelect;
		this.options.onUpdate = options.onUptate;

		var opt = this.options;
		this.date = opt.date;
		this.year = this.date.getFullYear();
		this.month = this.date.getMonth();
		this.dayOfMonth = this.date.getDate();
		this.day = this.date.getDay();
		//this.tittle = opt.monthsT[this.month] + ', ' + this.year;
		this.tittle = this.getFormatedDate(opt.tittleF);
		
		this.updateMonthsDays();
			
		//Sustituyo los identificadores por sus correspondientes objetos.
		this.displayAreaName = opt.displayArea;
		if($chk(opt.displayArea)) opt.displayArea = $(opt.displayArea);
		if(!$chk($(opt.inputField))) {
			$$('input').each(function(input, index){
				if(input.get('name') == opt.inputField) {
					input.set('id', opt.inputField);
					opt.inputField = $(opt.inputField);
				}
			});
		} else {
			opt.inputField = $(opt.inputField);
		}
		this.writeCalendar();
	},//end initialize
	
	updateMonthsDays: function() {
		//Actualizamos el número de días de febrero teniendo en cuenta que el año pueda ser bisiesto
		if ((this.year % 4 == 0) && ((this.year % 100 != 0) || (this.year % 400 == 0))) {
			this.options.monthsDays[1] = 29;
		} else {
			this.options.monthsDays[1] = 28;
		}
	},
	writeCalendar: function() {
		this.options.displayArea.set('text', '');
		this.options.displayArea.addClass('Mcalendar');
		
		var table = new Element('table', {
			'cellspacing': 0,
			'cellpadding': 0
		});
		var thead = new Element('thead');
		var tbody = new Element('tbody');
		var tfoot = new Element('tfoot');
		
		table.inject(this.options.displayArea);
		thead.inject(table);
		tbody.inject(table);
		tfoot.inject(table);
		
		//Cabecera de mes y año
		var tr = new Element('tr');
		var td = new Element('td', {
			'class': 'title',
			'colspan': 7,
			'html': this.tittle,
			'id': 'tittle'
		});
		td.inject(tr);
		tr.inject(thead);
		
		this.tittle = $$('#' + this.displayAreaName + ' #tittle').set('html', this.getFormatedDate(this.options.tittleF));

		if(this.options.navigation > 0) {
			//Controles de mes y año
			var tr = new Element('tr', {
				'class': 'headrow'
			});
			this.options.controls.each(function(control, idx){
				var addMonths = 0;
				var jumpControl = false;
				switch(idx) {
					case 0:
						if(this.options.navigation < 4) jumpControl = true;
						addMonths = -12;
						break;
					case 1:
						if(this.options.navigation < 2) jumpControl = true;
						addMonths = -1;
						break;
					case 2:
						addMonths = 0;
						break;
					case 3:
						if(this.options.navigation < 2) jumpControl = true;
						addMonths = 1;
						break;
					case 4:
						if(this.options.navigation < 4) jumpControl = true;
						addMonths = 12;
						break;
				}
				if(!jumpControl) {
					var div = new Element('div', {
						'html': control,
						'class': 'button'
					});

					var td = new Element('td', {
						'colspan': (idx==2? (7 - this.options.navigation): 1)
					});
					td.inject(tr);
					div.addMonths = addMonths;
					div.inject(td);
				}
			}, this);
			tr.inject(thead);
		}
		
		//Días de la semana
		var tr = new Element('tr', {
			'class': 'name'
		});
		this.options.days.each(function(day, idx){
			if(idx < this.options.dayStart)	 return;
			var td = new Element('td', {
				'class': (this.options.weekendDays.contains(idx)? 'name weekend': 'name'),
				'html': day
			});
			td.inject(tr);
		}, this);
		tr.inject(thead);

		//Día 1 del mes en curso
		var dAux = new Date(this.year, this.month, this.dayOfMonth);
		dAux.setDate(1);
		
		var month = this.month;
		var dayIni = this.options.dayStart - (dAux.getDay() > 0? dAux.getDay():7);
		var dayEnd = this.options.monthsDays[this.month];
		if((this.options.monthsDays[this.month] - dayIni)%7 != 0) {
			dayEnd += (7 - ( (this.options.monthsDays[this.month] - dayIni)%7 ));
		}
		var tr;
		var cont = 0;
		var days = new Array();
		var now = new Date();
		for(var i = (dayIni +1); i <= dayEnd; i++) {
			var day = i;
			var month = this.month;
			var year = this.year;
			if(i <= 0) {
				month--;
				if(month < 0) {
					month = 11;
					year--;
				}
				day = this.options.monthsDays[month] + i;
			} else if(i > this.options.monthsDays[month]){
				day = i - this.options.monthsDays[month];
				month++;
				if(month > 11) {
					month = 0;
					year ++;
				}
			}
			if(cont%7 == 0) {
				tr = new Element('tr', {});
				tr.inject(tbody);
			}
			
			var isActive = 
				this.options.direcction == 0 ||
				year > now.getFullYear() ||
				(month > now.getMonth() && year == now.getFullYear() ) ||
				(day >= now.getDate() && year == now.getFullYear() && month == now.getMonth());
			//Validaciones para solo dibujar y seleccionar elementos del mes seleccionado.
			isActive = isActive && (month == this.month);
			var isPrintable = month == this.month;

			//Validación para comprobar que el día es uno de los días disponibles. Solo en caso de indicar una lista de días.
			if($chk(this.options.availableDays) && this.options.blockNonAvailables) {
				var days = this.options.availableDays.filter(function(objDay){
					return year == objDay.getFullYear() && month == objDay.getMonth() && day == objDay.getDate();
				}, this);
				isActive = days.length > 0;
			}
			var style = 'day';
			if(this.options.weekendDays.contains((cont%7 + 1))) style +=' weekend';
			if(day == this.dayOfMonth && month == this.month && year == this.year) style += ' today';
			if(!isActive) style = 'day disabled ';
			var td = new Element('td', {
				'class': style,
				'html': (isPrintable? day: '')
			});
			
			td.year = year;
			td.month = month;
			td.dayOfMonth = day;
			td.isActive = isActive;
			td.inject(tr);
			cont++;
		}
		this.initCalEvents();
	},
	
	initCalEvents: function() {
		//Días del calendario
		var days = $$('#' + this.displayAreaName + ' .day');
		var onDaySelect = this.options.onDaySelect;
		var now = new Date();
		days.each(function(day) {
			if(!day.isActive) return;
			//if($chk(this.options.availableDays)) day.addClass('available');
            if($chk(this.options.availableDays)) {
				var days = this.options.availableDays.filter(function(objDay){
					return day.year == objDay.getFullYear() && day.month == objDay.getMonth() && day.dayOfMonth == objDay.getDate();
				}, this);
				if(days.length > 0) day.addClass('available');
			}



			day.addEvents({
				'mouseover': function(){
					this.addClass('active');
				},
				'mouseout': function(){
					this.removeClass('active');
				}
			});
			
			if($chk(this.options.inputField)) {
				var onSelectBound = this.onSelect.bind(this);
				day.addEvent('click', onSelectBound);
			};
		}, this);
		
		//Controles del calendario
		var controls = $$('#' + this.displayAreaName + ' .button');
		controls.each(function(control, idx) {
			control.addEvents({
				'mouseover': function(){
					this.addClass('active');
				},
				'mouseout': function(){
					this.removeClass('active');
				}
			});
			
			var reWriteCalendarBound = this.reWriteCalendar.bind(this);
			control.addEvent('click', reWriteCalendarBound);
			//var onChangeBound = this.onChange.bind(this);
			//control.addEvent('click', onChangeBound );
			
		}, this);
	},
	
	reWriteCalendar: function(event) {
		var control = event.target;
		var addMonths = control.addMonths;
		if(addMonths != 0) {
			this.month += addMonths;
			if(this.month < 0) {
				this.year--;
				this.updateMonthsDays();
				this.month = 12 + this.month;
			} else if(this.month > 11) {
				this.year++;
				this.updateMonthsDays();
				this.month = this.month - 12;
			}
			if(this.dayOfMonth > this.options.monthsDays[this.month]) {
				this.dayOfMonth = this.options.monthsDays[this.month];
			}
			this.date = new Date(this.year, this.month, this.dayOfMonth);
		} else {
			this.date = new Date();
		}
		this.year = this.date.getFullYear();
		this.month = this.date.getMonth();
		this.dayOfMonth = this.date.getDate();
		this.day = this.date.getDay();
		this.writeCalendar();
		this.onChange();
	},

	setDate: function (date) {
		this.date = date;
		this.year = this.date.getFullYear();
		this.month = this.date.getMonth();
		this.dayOfMonth = this.date.getDate();
		this.day = this.date.getDay();
		this.updateMonthsDays();
		this.writeCalendar();
	},

	getFormatedDate: function(format) {
		var formatedDate = format.substitute({
			d: this.dayOfMonth,
			dd: (this.dayOfMonth > 9? this.dayOfMonth: ('0' + this.dayOfMonth)),
			m: (this.month + 1),
			mm: (this.month > 8? (this.month + 1): ('0' + (this.month + 1))),
			Month: this.options.monthsT[this.month],
			YY: new String(this.year).substring(2,2),
			YYYY: this.year
		});
		return formatedDate;
	},
	
	onChange: function(event, day) {
		if(!$chk(day)) day = $$('#' + this.displayAreaName + ' .today')[0];
		if(!$chk(day)) return;
		if(this.year != day.year) this.updateMonthsDays();
		this.year = day.year;
		this.month = day.month;
		this.dayOfMonth = day.dayOfMonth;
		this.date = new Date(this.year, this.month, this.dayOfMonth);
		this.day = this.date.getDay();
		this.tittle = this.getFormatedDate(this.options.tittleF);
		this.tittle = $$('#' + this.displayAreaName + ' #tittle').set('html', this.getFormatedDate(this.options.tittleF));
		this.options.inputField.value =  this.getFormatedDate(this.options.dateF);
		$$('#' + this.displayAreaName + ' .today').removeClass('today');
		day.addClass('today');
	},
	
	onSelect: function(event, day) {
		if(!$chk(day)) day = event.target;
		this.onChange(event, day);
		if($chk(this.options.onSelect)) {
			this.options.onSelect();
		}
	}
});