var Calendar = Class.create({

	initialize: function(options) {
		options = options || {};

		this.textField = options.textField;

		this.converter = options.converter || Calendar.DEFAULT_CONVERTER;

		var selectedDate = this.textField ? this.converter.parse(this.textField.value) : null;

		if (options.positionImage) {
			this.position = Calendar._createPositionFromImage($(options.positionImage));
		} else {
			this.position = options.position || new Point(0, 0);
		}

		var highlightStyleClass = options.highlightStyleClass || 'highlighted_date';

		var highlightSelectedDate = !(options.highlightSelectedDate == false);

		this.highlightedDateMap = {};
		if (selectedDate && highlightSelectedDate) {
			this.highlightedDateMap[selectedDate.formatMsDsYYYY()] = highlightStyleClass;
		}
		if (options.highlightedDates) {
			options.highlightedDates.each(
				(function(date) {
					date = Date.resolveInputDate(date);
					if (date) {
						this.highlightedDateMap[date.formatMsDsYYYY()] = highlightStyleClass;
					}
				}).bind(this)
			);
		}
		if (options.styleInfos) {
			options.styleInfos.each(
				(function(styleInfo) {
					if (styleInfo && styleInfo.dates) {
						var styleClass = styleInfo.styleClass || highlightStyleClass;
						styleInfo.dates.each(
							(function(date) {
								date = Date.resolveInputDate(date);
								if (date) {
									this.highlightedDateMap[date.formatMsDsYYYY()] = styleClass;
								}
							}).bind(this)
						);
					}
				}).bind(this)
			);
		}

		if (options.selectableRange) {
			options.selectableRange.start = Date.resolveInputDate(options.selectableRange.start);
			options.selectableRange.end   = Date.resolveInputDate(options.selectableRange.end);

			var rangeStartDate = options.selectableRange.start ? options.selectableRange.start.clearTime() : null;
			var rangeEndDate   = options.selectableRange.end ? options.selectableRange.end.clearTime() : null;

			this.selectableRange = { start: rangeStartDate, end: rangeEndDate };
		} else {
			this.selectableRange = { start: null, end: null };
		}

		this.onOpen      = options.onOpen || null;
		this.onPreSelect = options.onPreSelect || null;
		this.onSelect    = options.onSelect || null;
		this.onClose     = options.onClose || null;

		var startDate = Date.resolveInputDate(options.startDate) || selectedDate || Date.today();

		this.startMonth = startDate.getFirstOfMonth();

		this.isOpen = false;
		this.div    = null;
		this.iframe = null;
	},

	open: function() {
		if (!this.isOpen) {
			this.div = this._createDiv();
			document.body.appendChild(this.div);

			if (isBrowserIE) {
				this.iframe = this._createIFrame();
				document.body.appendChild(this.iframe);
			}

			this._draw();

			this.isOpen = true;

			if (this.onOpen) {
				this.onOpen.call(null, this);
			}
		}
	},

	close: function() {
		if (this.isOpen) {
			if (this.iframe) {
				document.body.removeChild(this.iframe);
				this.iframe = null;
			}

			document.body.removeChild(this.div);
			this.div = null;

			this.isOpen = false;

			if (this.onClose) {
				this.onClose.call(null, this);
			}
		}
	},

	_changeMonth: function(delta) {
		this.startMonth.addMonths(delta);
		this._draw();
	},

	_createDiv: function() {
		var div = $(document.createElement('div'));

		div.className      = 'cal';
		div.style.position = 'absolute';
		div.style.left     = this.position.x + "px";
		div.style.top      = this.position.y + "px";
		div.style.zIndex   = Calendar.IFRAME_ZORDER + 1;

		return div;
	},

	_createIFrame: function() {
		var iframe = $(document.createElement('iframe'));

		iframe.src            = Calendar.IFRAME_SRC;
		iframe.scrolling      = 'no';
		iframe.frameborder    = '0px';
		iframe.style.position = 'absolute';
		iframe.style.left     = this.position.x + 'px';
		iframe.style.top      = this.position.y + 'px';
		iframe.style.width    = Calendar.TOTAL_WIDTH + 'px';
		iframe.style.height   = Calendar.IFRAME_HEIGHT + 'px';
		iframe.style.zIndex   = Calendar.IFRAME_ZORDER;

		return iframe;
	},

	_draw: function() {
		this.div.update(this._getHtml());
	},

	_getCellStyle: function(date, isDateSelectable) {
		var cellStyle = this.highlightedDateMap[date.formatMsDsYYYY()];
		if (cellStyle) {
			return cellStyle;
		}

		return isDateSelectable ? 'normal_date' : 'nonselectable_date';
	},

	_getHtml: function() {
		var firstOfMonths = new Array(Calendar.MONTH_COUNT);
		for (var monthIndex = 0; monthIndex < Calendar.MONTH_COUNT; monthIndex++) {
			firstOfMonths[monthIndex] = this.startMonth.getCopyPlusMonths(monthIndex);
		}

		var html = '';

		html += '<table border="0" cellpadding="0" cellspacing="0" width="' + Calendar.TOTAL_WIDTH + '">';

		html += '<tr class="cal_header">';
		html += '<td class="arrow" width="' + Calendar.ARROW_WIDTH + '"><a href="javascript:void(0)" onclick="Calendar._changeMonth(-1); return false;">&laquo;</a></td>';
		for (var monthIndex = 0; monthIndex < Calendar.MONTH_COUNT; monthIndex++) {
			var firstOfMonth = firstOfMonths[monthIndex];
			if (monthIndex > 0) {
				html += '<td width="' + Calendar.GAP_WIDTH + '">&nbsp;</td>';
			}
			html += '<td class="month" width="' + Calendar.MONTH_WIDTH + '">' + Calendar.MONTH_NAMES[firstOfMonth.getMonth()] + ' ' + firstOfMonth.getFullYear() + '</td>';
		}
		html += '<td class="arrow" width="' + Calendar.ARROW_WIDTH + '"><a href="javascript:void(0)" onclick="Calendar._changeMonth(1); return false;">&raquo;</a></td>';
		html += '</tr>';

		html += '<tr>';
		html += '<td colspan="' + Calendar.OUTER_CELL_COUNT + '">';
		html += '<table border="0" cellpadding="0" cellspacing="0" class="calendar_border">';

		html += '<tr valign="top">';
		for (var monthIndex = 0; monthIndex < Calendar.MONTH_COUNT; monthIndex++) {

			var firstOfMonth = firstOfMonths[monthIndex];

			html += '<td class="calendar">';
			html += '<table bgcolor="#ffffff" border="0" cellpadding="0" cellspacing="0">';

			html += '<tr class="day_bgcolor">';
			html += '<td class="calendar_padding">';
			html += '<table border="0" cellpadding="0" cellspacing="0">';
			html += '<tr>';
			for (var dowIndex = 0; dowIndex < 7; dowIndex++) {
				html += '<td class="day" width="' + Calendar.CELL_WIDTH + '">' + Calendar.DOW_TITLES[dowIndex] + '</td>';
			}
			html += '</tr>';
			html += '</table>';
			html += '</td>';
			html += '</tr>';

			var date = firstOfMonth.getCopyPlusDays(-firstOfMonth.getDay());

			html += '<tr>';
			html += '<td class="calendar_padding">';
			html += '<table border="0" cellpadding="0" cellspacing="0">';
			for (var weekIndex = 0; weekIndex < 6; weekIndex++) {
				html += '<tr>';

				for (var dowIndex = 0; dowIndex < 7; dowIndex++) {

					var cellStyle    = null;
					var cellContents = null;

					if (date.isInSameMonth(firstOfMonth)) {
						var isDateSelectable = this._isDateSelectable(date);

						cellStyle    = this._getCellStyle(date, isDateSelectable);
						cellContents = date.getDate();

						if (this.textField && isDateSelectable) {
							cellContents = '<a href="javascript:void(0)" onclick="Calendar._selectDate(\'' + date.formatMsDsYYYY() + '\');">' + cellContents + '</a>';
						}
					} else {
						cellStyle    = 'non_date';
						cellContents = '&nbsp;';
					}

					html += '<td class="date ' + cellStyle + '" width="' + Calendar.CELL_WIDTH + '" height="' + Calendar.CELL_HEIGHT + '">' + cellContents + '</td>';

					date.addDays(1);
				}

				html += '</tr>';
			}
			html += '</table>';
			html += '</td>';
			html += '</tr>';

			html += '</table>';
			html += '</td>';
		}
		html += '</tr>';

		html += '</table>';
		html += '</td>';
		html += '</tr>';

		html += '<tr>';
		html += '<td colspan="' + Calendar.OUTER_CELL_COUNT + '">';
		html += '<div class="cal_footer">';
		html += '<a href="javascript:void(0)" onclick="Calendar.close(); return false;">Close</a>';
		html += '</div>';
		html += '</td>';
		html += '</tr>';

		html += '</table>';

		return html;
	},

	_isDateSelectable: function(date) {
		if (this.selectableRange.start && (date < this.selectableRange.start)) {
			return false;
		} else if (this.selectableRange.end && (date >= this.selectableRange.end)) {
			return false;
		}
		return true;
	},

	_selectDate: function(inputDate) {
		if (this.onPreSelect) {
			if (!this.onPreSelect.call(null, this, inputDate)) {
				return false;
			}
		}

		this.textField.value = this.converter.format(inputDate);

		if (this.onSelect) {
			this.onSelect.call(null, this, inputDate);
		}

		return true;
	}
});

Calendar.DOW_TITLES  = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
Calendar.MONTH_COUNT = 2;
Calendar.MONTH_NAMES = ['January', 'February', 'March', 'April', 'May', 'June',
                        'July', 'August', 'September', 'October', 'November', 'December'],

Calendar.ARROW_WIDTH = 18;
Calendar.GAP_WIDTH   = 18;
Calendar.MONTH_WIDTH = 120;

Calendar.CELL_HEIGHT = 20;
Calendar.CELL_WIDTH  = 20;

Calendar.TOTAL_WIDTH = (2 * Calendar.ARROW_WIDTH) + (Calendar.MONTH_WIDTH * Calendar.MONTH_COUNT) + (Calendar.GAP_WIDTH * (Calendar.MONTH_COUNT - 1));
Calendar.OUTER_CELL_COUNT = 2 + Calendar.MONTH_COUNT + (Calendar.MONTH_COUNT - 1);

Calendar.IFRAME_HEIGHT = 200;
Calendar.IFRAME_SRC    = '/blank.jsp';
Calendar.IFRAME_ZORDER = 11;

Calendar.DEFAULT_CONVERTER = {
	format: function(date) {
		return date ? date.formatInputDate() : '';
	},

	parse: function(source) {
		return Date.parseInputDate(source);
	}
};

Calendar._instance = null;

Calendar.open = function(options) {
	Calendar.close();

	Calendar._instance = new Calendar(options);
	Calendar._instance.open();
};

Calendar.openForTravelStartDate = function(startField, endField, image) {

	var startField = $(startField);
	var endField = $(endField);
	var image = $(image);

	Calendar.open(
		{
			textField: startField,
			positionImage: image,
			selectableRange: { start: Date.today() },
			highlightedDates: [endField.value],
			onPreSelect: function(calendar) { HelperText.hide(calendar.textField); return true; }
		});
};

Calendar.openForTravelEndDate = function(startField, endField, image) {
	var startField = $(startField);
	var endField = $(endField);
	var image = $(image);

	Calendar.open(
		{
			textField: endField,
			startDate: startField.value,
			positionImage: image,
			selectableRange: { start: Date.today() },
			highlightedDates: [startField.value],
			onPreSelect: function(calendar) { HelperText.hide(calendar.textField); return true; }
		});
};

Calendar.close = function() {
	if (Calendar._instance) {
		Calendar._instance.close();
		Calendar._instance = null;
	}
};

Calendar._changeMonth = function(delta) {
	if (Calendar._instance) {
		Calendar._instance._changeMonth(delta);
	}
};

Calendar._createPositionFromImage = function(image) {

	var imagePos = ImageUtils.getPosition(image);

	var x = imagePos.x + image.width + 3;
	var y = imagePos.y;

	var xOver = (x + Calendar.TOTAL_WIDTH) - (document.viewport.getRight() - 17);
	if (xOver > 0) {
		x -= xOver;
		y += image.height + 3;
	}

	return new Point(x, y);
};

Calendar._selectDate = function(inputDateString) {
	if (Calendar._instance) {
		var inputDate = Date.parseMsDsYYYY(inputDateString);
		if (Calendar._instance._selectDate(inputDate)) {
			Calendar.close();
		}
	}
};