if(VVF === undefined) {
	var VVF = {};
}

(function(namespace) {
	namespace = namespace || window;
	var $ = jQuery;
	
	// Counter to create eventually an ID
	var uc = 0;
	
	// Default names
	var config = {
		format: 'DD/MM/YYYY',
		months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
		days: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
		prevMonth: {txt: 'Mois précédent', cName: 'prev'},
		nextMonth: {txt: 'Mois suivant', cName: 'next'},
		closeLink: {txt: 'Fermer', cName: 'close'},
		hideClass: 'hide' ,
		indisponible : 'indisponible'
	};
	
	// Change configuration for all calendars
	var setConfig = function(datas) {
		for(var k in datas) {
			if(config[k]) {config[k] = datas[k];}
		}
	};
	
	// Date elements
	var today = new Date(),
		day = today.getDate(),
		month = today.getMonth() + 1,
		year = today.getFullYear();
	
	var leapYear = function(y) {
		if(y % 4 == 0) {return (y % 100 != 0 || y % 100 == 0 && y % 400 == 0) ? 29 : 28;}
		return 28;
	};
	
	var daysPerMonth = function(y, m) {
		var d = [31, leapYear(y), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
		return d[m - 1];
	};
	
	var firstDayOfMonth = function(y, m) {
		var d = new Date(y, m - 1).getDay();
		return d;
	};
	
	var getDayName = function(d) {
		return config.days[d - 1];
	};
	
	var getMonthName = function(m) {
		return config.months[m - 1];
	};
	
	var getNewDate = function(o) {
		o = o || {};
		if(o.method == 'add') {
			o.d = ((o.d !== undefined) ? o.d : 0) + day;
			o.m = ((o.m !== undefined) ? o.m : 0) + month;
			o.y = ((o.y !== undefined) ? o.y : 0) + year;
		} else if(o.method == 'substract') {
			o.d = day - ((o.d !== undefined) ? o.d : 0);
			o.m = month - ((o.m !== undefined) ? o.m : 0);
			o.y = year - ((o.y !== undefined) ? o.y : 0);
		} else {
			o.d = (o.d !== undefined) ? o.d : day;
			o.m = (o.m !== undefined) ? o.m : month;
			o.y = (o.y !== undefined) ? o.y : year;
		}
		var n = new Date(o.y, o.m - 1, o.d);
		return {d: n.getDate(), m: n.getMonth() + 1, y: n.getFullYear()};
	};
	
	var compare = function(d1, d2) {
		var d1 = Number(''+ d1.y + (d1.m < 10 ? '0'+ d1.m : d1.m) + (d1.d < 10 ? '0'+ d1.d : d1.d)),
			d2 = Number(''+ d2.y + (d2.m < 10 ? '0'+ d2.m : d2.m) + (d2.d < 10 ? '0'+ d2.d : d2.d));
		return d2 - d1;
	};
	
	var formatDate = function(y, m, d) {
		d = (d < 10) ? '0' + d : d;
		m = (m < 10) ? '0' + m : m;
		return config.format.replace('DD', d).replace('MM', m).replace('YYYY', y);
	};
	
	// Set headers for the calendars (abbr of day names)
	var setHeaders = function(map) {
		var headers = '', c;
		for(var i = 0; i < 7; i++) {
			c = i + map;
			if(!config.days[c]) {c = i + map - 7;}
			headers += '<th scope="col" title="' + config.days[c] + '">' + config.days[c].charAt(0).toUpperCase() + '</th>';
		}
		return headers;
	};
	
	
	// Set content for the calendars
	var setContents = function(o, focus, map, min, max) {
		var contents = '',
			fd = firstDayOfMonth(o.y, o.m) - map,
			md = daysPerMonth(o.y, o.m),
			cl = 0, cd = 0, cur, curMap, promo, focus,
			f = (o.m == focus.m && o.y == focus.y),
			dMin = (min && o.y == min.y && o.m == min.m) ? Math.max(o.d, min.d) : o.d;
			dMax = (max && o.y == max.y && o.m == max.m) ? Math.min(md, max.d) : md;
			fd = fd < 0 ? fd + 7 : fd;
		for(var i = 0; i < 6; i++) {
			contents += '<tr>';
			for(var j = 0; j < 7; j++) {
				if(cl < fd || cd >= md) {
					contents += '<td class="empty"><span>&nbsp;</span></td>';
				} else {
					cd++;
					cur = cd;//(cd < 10) ? '0' + cd : cd;
					curMap = 'day:' + cd;
					// TODO : recognize week-ends
					if((cd < dMin && o.m == min.m && o.y == min.y) || (cd > dMax && o.m == max.m && o.y == max.y) || (o.enable && !o.enable[curMap])) {
						contents += '<td class="unavailable"><span>' + cur + '</span></td>';
					} else {
						promo = o.enable[curMap].promo ? 'promo' : '';
						focus = (cd == focus.d && f) ? 'focus' : '';
						contents += '<td class="' + promo + ' ' + focus + '"><a href="#">' + cur + '</a></td>';
					}
				}
				cl++;
			}
			contents += '</tr>';
		}
		contents += '<tr><td colspan="7"><span class="message">&nbsp;</span></td></tr>';
		if ($('#bookingPopin').length){
			contents += '<tr id="specialOfferMsg"><td colspan="7">'+l10n.calendar.specialOffer+'</td></tr>';
		}
		return contents;
	};
	
	// Get position
	var getPosition = function(ref) {
		var p = $(ref), found = false, check, posRef = p.offset(), posP;
		while(p.length && p[0] != document) {
			check = p.css('position');
			if('relative;absolute;fixed'.indexOf(check) > -1) {
				posP = p.offset();
				found = true;
				break;
			} else {
				p = p.parent();
			}
		}
		if(found) {
			return {
				left: posRef.left - posP.left,
				top: posRef.top - posP.top
			};
		} else {
			return posRef;
		}
	};
	
	// Calendar constructor
	var Calendar = function(datas) {
		if(!(this instanceof Calendar)) {
			return new Calendar(datas);
		}
		
		Calendar.superclass.constructor.call(this);
		
		this.addEvents(['dateselect'], datas.onload);
		this.initialize(datas);
	};
	namespace.extend(Calendar, namespace.Observable, {
		initialize: function(datas) {
			uc++;
			var that = this;
			
			this.posDone = false;
			this.id = datas.id || 'calendar' + uc;
			if(!datas.target) {
				throw new Error('Calendar: no target submitted');
			}
			this.target = $('#' + datas.target);
			if(!this.target.length) {
				throw new Error('Calendar: submitted target does not exist');
			}
			this.targetType = (this.target[0].nodeName.toLowerCase() == 'input') ? 'val' : 'html';
			if(datas.insert) {
				this.insert = $('#' + datas.insert);
			}
			if(!this.insert.length) {
				this.insert = this.target;
			}
			
			this.fields = datas.fields ? $(datas.fields).attr('tabindex', '-1') : null;
			this.toDisable = datas.disable ? $(datas.disable) : null;
			this.map = datas.firstDay || 0;
			this.ajax = datas.ajax || false;
			this.min = (datas.min) ? getNewDate(datas.min) : null;
			this.max = (datas.max) ? getNewDate(datas.max) : null;
			this.start = (datas.start) ? getNewDate(datas.start) : getNewDate();
			var startDate, reField = /^\d+$/;
			if(reField.test(this.fields[0].value) && reField.test(this.fields[1].value) && reField.test(this.fields[2].value)) {
				startDate = {y: this.fields[2].value, m: this.fields[1].value, d: this.fields[0].value};
				this.target[this.targetType](formatDate(startDate.y, startDate.m, startDate.d));
				this.loadFromField = true;
			} else {
				startDate = this.start;
			}
			this.last = getNewDate(startDate);
			
			this.iframe = $('<iframe style="display:none; border:0;" frameborder="0"></iframe>');
			this.container = $(document.createElement('div')).click(function(e) {that.clickContainer(e, this);}).attr('id', this.id).addClass('calendar').addClass(config.hideClass);
			var insert = '<div class="calendarInner">\n'+
						 '<table>\n'+
						 '	<caption>' + getMonthName(this.start.m) + ' ' + this.start.y + '</caption>\n'+
						 '	<thead><tr>' + setHeaders(this.map) + '</tr></thead>\n'+
						 '	<tbody></tbody>\n'+
						 '</table>\n'+
						 '<a href="#" class="' + config.prevMonth.cName + '">' + config.prevMonth.txt + '</a>\n'+
						 '<a href="#" class="' + config.nextMonth.cName + '">' + config.nextMonth.txt + '</a>\n'+
						 '<a href="#" class="' + config.closeLink.cName + '">' + config.closeLink.txt + '</a>\n'+
						 '</div>';
			
			this.container[0].innerHTML = insert;
			
			if(datas.isResetable == true){
				var resetBtnInsert = $(document.createElement('a')).click(function(e) {that.clear(e)}).addClass("resetButton").attr('href', '#').html(l10n.calendar.resetLabel); 
				this.container.prepend(resetBtnInsert);
			}
			
			this.container.prepend(this.iframe);
			
			this.insert.after(this.container).click(function(e) {that.clickTarget(e);});
			this.caption = $('caption', this.container);
			this.contents = $('tbody', this.container);
			this.prevMonthLink = $('a.' + config.prevMonth.cName, this.container);
			this.nextMonthLink = $('a.' + config.nextMonth.cName, this.container);
			this.update(startDate);
		},
		
		setLinkVisibility: function() {
			if(this.min && this.last.y == this.min.y && this.last.m == this.min.m) {
				this.prevMonthLink.addClass(config.hideClass);
			} else {
				this.prevMonthLink.removeClass(config.hideClass);
			}
			if(this.max && this.last.y == this.max.y && this.last.m == this.max.m) {
				this.nextMonthLink.addClass(config.hideClass);
			} else {
				this.nextMonthLink.removeClass(config.hideClass);
			}
		},
		
		update: function(o) {
			var that = this;
			function doUpdate(data, ajaxCall) {
			    if(ajaxCall) {
				    if(data) {
						that.last.enable = eval('(' + data + ')');
				    } else {
				        that.last.enable = 'nodatas';
				    }
				}
				that.caption[0].innerHTML = getMonthName(that.last.m) + ' ' + that.last.y;
				var focus;
				if(!that.fields[0].value || !that.fields[1].value || !that.fields[2].value) {
					focus = {y: year, m: month, d: day}
				} else {
					focus = {y: that.fields[2].value, m: that.fields[1].value, d: that.fields[0].value};
				}
				that.contents.html(setContents(that.last, focus, that.map, that.min, that.max)); // passer par jQuery pour contourner un bug IE
				that.lastFocus = $('td.focus', that.contents);
				that.setLinkVisibility();
				bindInfosDisplay(that);
			}
			o = o || getNewDate(this.last);
			if(o.d === undefined) {o.d = 1;}
			this.last = getNewDate(o);
			if(this.ajax && this.ajax.url) {
				var that = this,
					params = this.ajax.getDatas(this) || {};
				$.get(this.ajax.url, params, function(data) {
					doUpdate(data, true);
				});
			} else {
				doUpdate();
			}
		},
		
		disable: function(fields, parent) {
			this.disabled = true;
			this.fields.attr('disabled', 'disabled');
			this.toDisable.addClass('disabled');
		},
		
		enable: function(fields, parent) {
			this.disabled = false;
			this.fields.removeAttr('disabled');
			this.toDisable.removeClass('disabled');
		},
		
		prev: function(o) {
			o = o || {};
			o.d = 1;
			o.m = this.last.m - ((o.y === undefined) ? 1 : 0);
			o.y = o.y || this.last.y;
			var n = getNewDate(o);
			if(this.min && compare(this.min, n) < 0) {
				n.m = this.min.m;
				n.y = this.min.y;
			}
			this.update(n);
		},
		
		next: function(o) {
			o = o || {};
			o.d = 1;
			o.m = this.last.m + ((o.y === undefined) ? 1 : 0);
			o.y = o.y || this.last.y;
			var n = getNewDate(o);
			if(this.max && compare(n, this.max) < 0) {
				n.m = this.max.m;
				n.y = this.max.y;
			}
			this.update(n);
		},
		
		clickTarget: function(e) {
			e.preventDefault();
			if(this.disabled) {return;}
			if(!this.posDone) {
				// TODO : better positionning
				this.container.css(getPosition(this.insert));
				this.posDone = true;
			}
			this.container.hasClass(config.hideClass) ? this.open() : this.close();
		},
		
		clickContainer: function(e, obj) {
			var t = e.target;
			while(t.parentNode && t != obj && t.nodeName.toLowerCase() != 'a') {t = t.parentNode;}
			if(t.nodeName.toLowerCase() == 'a') {
				var d = Number(t.innerHTML);
				t = $(t);
				if(t.hasClass(config.prevMonth.cName)) {
					this.prev();
				} else if(t.hasClass(config.nextMonth.cName)) {
					this.next();
				} else if(t.hasClass(config.closeLink.cName)) {
					this.close();
				} else if(d) {
					this.last.d = d;
					if(this.lastFocus && this.lastFocus.length) {
						this.lastFocus.removeClass('focus');
					}
					this.lastFocus = $(t[0].parentNode).addClass('focus');
					this.target[this.targetType](formatDate(this.last.y, this.last.m, d));
					this.close();
					this.fireEvent('dateselect');
				}
				e.preventDefault();
			}
		},
		
		clear:function(e) {
			e.preventDefault();
			this.last.d = this.start.d;
			this.last.m = this.start.m;
			this.last.y = this.start.y;
			if(this.lastFocus && this.lastFocus.length) {
				this.lastFocus.removeClass('focus');
				this.lastFocus = null;
			}
			
			this.target[this.targetType](l10n.calendar.dateHolder);
			this.close();
			this.fireEvent('dateunselect');
		},
		
		open: function() {
			var that = this;
			$(document).bind('mousedown.calendar', function(e) {
				var t = e.target;
				while(t.parentNode && t != that.container[0]) {
					if(t == that.target[0]) {return;}
					t = t.parentNode;
				}
				if(!t || (t && t != that.container[0])) {that.close();}
			});
			this.container.removeClass(config.hideClass);
			var width = this.container[0].offsetWidth;
			var height = this.container[0].offsetHeight;
			this.container.find('div.calendarInner').width(width - 2);
			this.iframe.css({
				width: width,
				height: height,
				marginBottom: -height,
				display: 'block'
			});
			
			// contournement du bug ou l'on selectionnait un jour jx puis 
			// l'on ne pouvait revenir a une date anterieure à jx en réouvrant le calendrier
			var o = 0;
			o = o || {};
			o.d = 1;
			o.m = this.last.m;
			o.y = o.y || this.last.y;
			var n = getNewDate(o);
			if(this.max && compare(n, this.max) < 0) {
				n.m = this.max.m;
				n.y = this.max.y;
			}
			this.update(n);
		},
		
		close: function() {
			$(document).unbind('mousedown.calendar');
			this.iframe.css('display', 'none');
			this.container.addClass(config.hideClass);
		}
	});
	
	var mouseOver = function(td, calendar) {
		if(td.className.indexOf('unavailable') > -1 || td.className.indexOf('promo') > -1) {
			var day = 'day:' + $(td).text();
			if(calendar.last.enable[day]) {
			var msg = calendar.last.enable[day] ? calendar.last.enable[day].message : l10n.calendar.indisponible;
				if(msg) {
					calendar.container.find('span.message').html(msg);
				}
			}
			/*if(msg) {
				calendar.container.find('span.message').parent().parent().after('<tr id="specialOfferMsg"><td colspan="7">'+l10n.calendar.specialOffer+'</td></tr>');
			}*/
		}
	};
	
	var mouseOut = function(calendar) {
		calendar.container.find('span.message').html('&nbsp;');
		//calendar.container.find('#specialOfferMsg').remove();
	};
	
	var bindInfosDisplay = function(calendar) {
		calendar.container.find('td').hover(
			function() {
				mouseOver(this, calendar);
			},
			function() {
				mouseOut(calendar);
			}
		);
	};
	
	if(!namespace.Calendar) {
		namespace.Calendar = {
			Create: Calendar,
			setConfig: setConfig
		};
	}
})(VVF);
