var CalendarDate = Class.create({
	// Constructor
	initialize: function(options) {
		if (options == undefined) {
			options = {};
		}
		var date = new Date();
		
		// Initialize Members
		this.calendar = null;
		this.elements = [];
		this.year = date.getFullYear();
		this.month = date.getMonth() + 1;
		this.day = date.getDate();
		this.state = 0;
		this.bookinginfo = {};
		this._selected = false;
		this._hovering = false
		
		if (options.calendar != undefined) {
			this.calendar = options.calendar;
		}
		if (options.year != undefined) {
			this.year = options.year;
		}
		if (options.month != undefined) {
			this.month = options.month;
		}
		if (options.day != undefined) {
			this.day = options.day;
		}
		if (options.bookinginfo != undefined) {
			this.bookinginfo = options.bookinginfo;
		} else {
			this.bookinginfo = {
				date: this.getId(),
				state: this.getState()
			};
		}
		if (options.state != undefined) {
			this.state = options.state;
			this.bookinginfo.state = options.state;
		}
		if (options.selected != undefined) {
			this._selected = options.selected;
		}
		if (options.modified == undefined) {
			options.modified = false;
		}
		
		// Set up bound events
		if (this.calendar.editable) {
			this.dragStartBind = this.dragStart.bindAsEventListener(this);
			this.dragStopBind = this.dragStop.bindAsEventListener(this);
		} else if (this.calendar.selectable) {
			this.selectBind = this.select.bindAsEventListener(this);
		}
		if (this.calendar.editable || this.calendar.selectable) {
			this.hoverOnEventBind = this.hoverOnEvent.bindAsEventListener(this);
			this.hoverOffEventBind = this.hoverOffEvent.bindAsEventListener(this);
		}
		if (this.calendar.extensions.protomenu) {
			this.contextMenuEventBind = this.contextMenuEvent.bindAsEventListener(this);
		}
		
		this._modified = options.modified;
	},
	
	getId: function() {
		return this.year + '-' + this.month + '-' + this.date;
	},
	
	modified: function(modified) {
		if (modified != undefined) {
			this._modified = modified;
		}
		return this._modified;
	},
	
	getYear: function() {
		return this.year;
	},
	getMonth: function() {
		return this.month;
	},
	getDay: function() {
		return this.day;
	},
	
	getDate: function() {
		var cDate = new Date();
		cDate.setDate(1);
		cDate.setFullYear(this.getYear());
		cDate.setMonth(this.getMonth() - 1);
		cDate.setDate(this.getDay());
		return cDate;
	},
	
	getState: function() {
		return this.state;
	},
	setState: function(state, options) {
		if (options == undefined) {
			options = {};
		}
		if (options.multiselect == undefined) {
			options.multiselect = false;
		}
		if (options.updateAll == undefined) {
			options.updateAll = true;
		}
		if (options.updatingAll == undefined) {
			options.updatingAll = false;
		}
		if (options.notify == undefined) {
			options.notify = true;
		}
		
		var selected = this.selected();
		if (selected) {
			this.setSelected(false);
		}
		
		if (options.multiselect) {
			if (state >= 2) {
				if (this.getState() != state) {
					return;
				}
			}
		}
		
		var oldstate = this.getState();
		
		if (options.notify && (options.updateAll || !options.updatingAll)) {
			if (this.calendar.extensions.notifier) {
				if (!Notifier.raise('calendar_statechange', {
					calendar: this.calendar,
					date: this,
					oldstate: oldstate,
					state: state
				})) {
					return;
				}
			}
		}
		
		this.modified(true);
		
		if (options.updateAll) {
			if (this.getBookingReference()) {
				var booking_reference = this.getBookingReference();
				for (var key in this.calendar.dates) {
					if (this.calendar.dates[key].getBookingReference() == booking_reference) {
						this.calendar.dates[key].setState(state, {
							updateAll: false,
							updatingAll: true
						});
					}
				}
				if (selected) {
					this.setSelected(true);
				}
				return;
			}
		}
		
		if (state < 2) {
			if (this.getBookingReference()) {
				this.setBookingInfo({
					date: this.getBookingInfo().date,
					state: 1
				});
			}
		}
		
		this.state = state;
		this.bookinginfo.state = state;
		
		var booking = false;
		var bookingstart = false;
		var bookingend = false;
		if (this.getBookingReference()) {
			booking = true;
			var cDateThis = this.getDate();
			var cDate = new Date();
			
			var date_from = this.getBookingInfo().date_from.split('-');
			cDate.setDate(1);
			cDate.setFullYear(date_from[0]);
			cDate.setMonth(date_from[1] - 1);
			cDate.setDate(date_from[2]);
			if (cDate.getFullYear() == cDateThis.getFullYear() && cDate.getMonth() == cDateThis.getMonth() && cDate.getDate() == cDateThis.getDate()) {
				bookingstart = true;
			}
			
			var date_to = this.getBookingInfo().date_to.split('-');
			cDate.setDate(1);
			cDate.setFullYear(date_to[0]);
			cDate.setMonth(date_to[1] - 1);
			cDate.setDate(date_to[2] - 1);
			if (cDate.getFullYear() == cDateThis.getFullYear() && cDate.getMonth() == cDateThis.getMonth() && cDate.getDate() == cDateThis.getDate()) {
				bookingend = true;
			}
		}
		
		for (var elementIndex = 0; elementIndex < this.elements.length; elementIndex++) {
			var element = this.elements[elementIndex];
			this.hoverOff(element);
			element.addClassName(this.calendar.states.states[state]);
			for (var stateIndex = 0; stateIndex < this.calendar.states.states.length; stateIndex++) {
				if (stateIndex != state) {
					element.removeClassName(this.calendar.states.states[stateIndex]);
				}
			}
			if (booking) {
				element.addClassName('booking');
				
				if (bookingstart) {
					element.addClassName('start');
				} else {
					element.removeClassName('start');
				}
				
				if (bookingend) {
					element.addClassName('end');
				} else {
					element.removeClassName('end');
				}
			} else {
				element.removeClassName('booking');
				element.removeClassName('start');
				element.removeClassName('end');
			}
			if (this.calendar.extensions.tooltips) {
				Tooltips.updateTooltip(element);
			}
		}
		
		if (selected) {
			this.setSelected(true);
		}
	},
	
	getBookingInfo: function() {
		return this.bookinginfo;
	},
	
	setBookingInfo: function(bookinginfo) {
		this.modified(true);
		this.bookinginfo = bookinginfo;
	},
	
	getBookingReference: function() {
		if (this.bookinginfo.booking_reference == undefined) {
			return '';
		} else {
			return this.bookinginfo.booking_reference;
		}
	},
	
	setBookingReference: function(booking_reference) {
		this.modified(true);
		this.bookinginfo.booking_reference = booking_reference;
	},
	
	addElement: function(element) {
		this.elements.push(element);
	},
	
	render: function(container, options) {
		if (!el(container)) {
			return;
		}
		if (options == undefined) {
			return;
		}
		if (options.date == undefined) {
			return;
		}
		if (options.showdate == undefined) {
			options.showdate = true;
		}
		if (options.off == undefined) {
			options.off = false;
		}
		if (options.toprow == undefined) {
			options.toprow = false;
		}
		if (options.bottomrow == undefined) {
			options.bottomrow = false;
		}
		if (options.first == undefined) {
			options.first = false;
		}
		if (options.last == undefined) {
			options.last = false;
		}
		
		var className = 'day ' + (CalendarWeekNames[this.calendar.getWeekday(options.date)].short).toLowerCase() + ' ' + this.calendar.states.states[this.getState()];
		if (options.toprow) {
			className += ' toprow';
		}
		if (options.bottomrow) {
			className += ' bottomrow';
		}
		if (options.first) {
			className += ' first';
		}
		if (options.last) {
			className += ' last';
		}
		if (this.selected()) {
			className += ' selected';
		}
		
		// Get Adjacent References
		if (this.getBookingReference()) {
			className += ' booking';
			var cDate = new Date();
			cDate.setDate(1);
			cDate.setFullYear(options.date.getFullYear());
			cDate.setMonth(options.date.getMonth());
			cDate.setDate(options.date.getDate() - 1);
			var key_yesterday = cDate.getFullYear() + '-' + (cDate.getMonth() + 1) + '-' + cDate.getDate();
			cDate.setDate(1);
			cDate.setFullYear(options.date.getFullYear());
			cDate.setMonth(options.date.getMonth());
			cDate.setDate(options.date.getDate() + 1);
			var key_tomorrow = cDate.getFullYear() + '-' + (cDate.getMonth() + 1) + '-' + cDate.getDate();
			if (this.calendar.dates[key_yesterday] == undefined) {
				className += ' start';
			} else if (this.calendar.dates[key_yesterday].getBookingReference() != this.getBookingReference()) {
				className += ' start';
			}
			if (this.calendar.dates[key_tomorrow] == undefined) {
				className += ' end';
			} else if (this.calendar.dates[key_tomorrow].getBookingReference() != this.getBookingReference()) {
				className += ' end';
			}
		}
		
		var date = createExtendedElement('li', {
			className: className,
			innerHTML: options.showdate ? options.date.getDate() : '',
			parent: container
		});
		if (options.off) {
			$(date).addClassName('off');
		}
		
		date.cCalendarDate = this;
		
		if (this.calendar.editable) {
			$(date).observe('mousedown', this.dragStartBind);
			$(date).observe('selectstart', function(event) {
				Event.stop(event);
			});
			$(date).observe('mouseup', this.dragStopBind);
		} else if (this.calendar.selectable) {
			$(date).observe('click', this.selectBind);
		}
		if (this.calendar.editable || this.calendar.selectable) {
			$(date).observe('mouseover', this.hoverOnEventBind);
			$(date).observe('mouseout', this.hoverOffEventBind);
		}
		
		if (this.calendar.extensions.tooltips) {
			$(date).addTooltip(this.calendar.tooltips.date.callback.bindAsEventListener(this));
		}
		if (this.calendar.extensions.protomenu) {
			$(date).observe('contextmenu', this.contextMenuEventBind);
		}
		
		this.addElement(date);
	},
	
	dragStart: function(event) {
		if (Event.isLeftClick(event)) {
			var element = Event.element(event);
			this.calendar.setDragging(true);
			if (this.getState() == this.calendar.getState()) {
				if (this.calendar.getState() == this.calendar.states.defaultstate) {
					this.calendar.setDragMode(this.calendar.states.defaultpolar);
					this.setState(this.calendar.states.defaultpolar);
				} else {
					this.calendar.setDragMode(this.calendar.states.defaultstate);
					this.setState(this.calendar.states.defaultstate);
				}
			} else {
				this.calendar.setDragMode(this.calendar.getState());
				this.setState(this.calendar.getState());
			}
			event.stop();
		} else {
			this.calendar.setDragging(false);
		}
	},
	dragStop: function(event) {
		if (Event.isLeftClick(event)) {
			this.calendar.setDragging(false);
		}
	},
	
	selected: function() {
		return this._selected;
	},
	setSelected: function(selected) {
		if (selected == this._selected) {
			return;
		}
		if (selected) {
			this._selected = true;
			for (var elementIndex = 0; elementIndex < this.elements.length; elementIndex++) {
				var element = this.elements[elementIndex];
				this.hoverOff(element);
				element.addClassName('selected');
			}
			if (this.calendar.extensions.notifier) {
				Notifier.raise('calendar_select', {
					calendar: this.calendar,
					date: this
				});
			}
		} else {
			this._selected = false;
			for (var elementIndex = 0; elementIndex < this.elements.length; elementIndex++) {
				var element = this.elements[elementIndex];
				this.hoverOff(element);
				element.removeClassName('selected');
			}
		}
	},
	
	select: function(event) {
		var selectable = false;
		for (var i = 0; i < this.calendar.states.selectable.length; i++) {
			if (this.getState() == this.calendar.states.selectable[i]) {
				selectable = true;
				break;
			}
		}
		
		if (selectable) {
			if (this.selected()) {
				for (var key in this.calendar.dates) {
					if (this.calendar.dates[key].selected()) {
						this.calendar.dates[key].setSelected(false);
					}
				}
			} else {
				if (this.getBookingReference()) {
					for (var key in this.calendar.dates) {
						if (this.calendar.dates[key].getBookingReference() == this.getBookingReference()) {
							this.calendar.dates[key].setSelected(true);
						} else {
							this.calendar.dates[key].setSelected(false);
						}
					}
					return;
				} else {
					for (var key in this.calendar.dates) {
						if (this.calendar.dates[key].selected()) {
							this.calendar.dates[key].setSelected(false);
						}
					}
					this.setSelected(true);
				}
			}
		}
	},
	
	hoverOn: function(element, options) {
		if (CalendarHoverLock.locked) {
			return;
		}
		
		if (this._hovering) {
			return;
		}
		
		if (options == undefined) {
			options = {};
		}
		if (options.onlyOn == undefined) {
			options.onlyOn = false;
		}
		if (options.onlyOff == undefined) {
			options.onlyOff = false;
		}
		if (options.noInversion == undefined) {
			options.noInversion = false;
		}
		if (options.hoveringByReference == undefined) {
			options.hoveringByReference = false;
		}
		if (element == undefined) {
			element = null;
		}
		if (element == null) {
			for (var elementIndex = 0; elementIndex < this.elements.length; elementIndex++) {
				var element = this.elements[elementIndex];
				this.hoverOn(element, options);
			}
			return;
		}
		
		if (this.calendar.selectable) {
			if (this.selected()) {
				return false;
			}
			var selectable = false;
			for (var i = 0; i < this.calendar.states.selectable.length; i++) {
				if (this.getState() == this.calendar.states.selectable[i]) {
					selectable = true;
					break;
				}
			}
			if (!selectable) {
				return;
			}
		}
		
		if (!options.hoveringByReference) {
			if (this.getBookingReference()) {
				options.hoveringByReference = true;
				for (var key in this.calendar.dates) {
					if (this.calendar.dates[key].getBookingReference() == this.getBookingReference()) {
						this.calendar.dates[key].hoverOn(null, options);
						this._hovering = false;
					}
				}
				this._hovering = true;
				return;
			}
		}
		
		element = $(element);
		if (options.onlyOn) {
			if (element.hasClassName('off')) {
				return;
			}
		}
		if (options.onlyOff) {
			if (!element.hasClassName('off')) {
				return;
			}
		}
		if (this.calendar.isDragging()) {
			this.setState(this.calendar.getDragMode());
		} else {
			element.addClassName('hover');
			if (!options.noInversion && element.hasClassName(this.calendar.states.states[this.calendar.getState()])) {
				if (this.getState() == this.calendar.states.defaultstate) {
					element.addClassName('hover' + this.calendar.states.states[this.calendar.states.defaultpolar]);
				} else {
					element.addClassName('hover' + this.calendar.states.states[this.calendar.states.defaultstate]);
				}
			} else {
				element.addClassName('hover' + this.calendar.states.states[this.calendar.getState()]);
			}
		}
		this._hovering = true;
	},
	hoverOnEvent: function(event) {
		return this.hoverOn(Event.element(event));
	},
	
	hoverOff: function(element, options) {
		if (CalendarHoverLock.locked) {
			return;
		}
		
		if (!this._hovering) {
			return;
		}
		
		if (options == undefined) {
			options = {};
		}
		if (options.onlyOn == undefined) {
			options.onlyOn = false;
		}
		if (options.onlyOff == undefined) {
			options.onlyOff = false;
		}
		if (options.hoveringByReference == undefined) {
			options.hoveringByReference = false;
		}
		if (element == undefined) {
			element = null;
		}
		if (element == null) {
			for (var elementIndex = 0; elementIndex < this.elements.length; elementIndex++) {
				var element = this.elements[elementIndex];
				this.hoverOff(element, options);
			}
			return;
		}
		
		if (!options.hoveringByReference) {
			if (this.getBookingReference()) {
				options.hoveringByReference = true;
				for (var key in this.calendar.dates) {
					if (this.calendar.dates[key].getBookingReference() == this.getBookingReference()) {
						this.calendar.dates[key].hoverOff(null, options);
						this._hovering = true;
					}
				}
				this._hovering = false;
				return;
			}
		}
		
		element = $(element);
		if (options.onlyOn) {
			if (element.hasClassName('off')) {
				return;
			}
		}
		if (options.onlyOff) {
			if (!element.hasClassName('off')) {
				return;
			}
		}
		element.removeClassName('hover');
		if (element.hasClassName('hover' + this.calendar.states.states[this.calendar.states.defaultstate])) {
			element.removeClassName('hover' + this.calendar.states.states[this.calendar.states.defaultstate]);
		} else if (element.hasClassName('hover' + this.calendar.states.states[this.calendar.states.defaultpolar])) {
			element.removeClassName('hover' + this.calendar.states.states[this.calendar.states.defaultpolar]);
		} else {
			element.removeClassName('hover' + this.calendar.states.states[this.calendar.getState()]);
		}
		this._hovering = false;
	},
	hoverOffEvent: function(event) {
		return this.hoverOff(Event.element(event));
	},
	
	contextMenuEvent: function(event) {
		this.calendar._protomenu.show(event);
	},
	
	lockHover: function() {
		CalendarHoverLock.element = this;
		CalendarHoverLock.locked = true;
	}
});