var Cathmhaol = window.Cathmhaol || {};

/**
* @library     Cathmhaol.DateInput
* @description Creates a popup calendar that will allow the user to select a date. The constructor allows the user to set the form input, the default date, and the helper object.
* @author      Robert King
* @requires    Cathmhaol.Calendar
*
* @param {node|string} formInput    An HTML form input
* @param {date|string} defaultDate  A value that is a date or can be evaluated as a date
* @param {node|string} helper       A DOM node or HTML string that can be clicked.
* @param {string} iso_639_1_code    An ISO 639-1 Alpha-3 language code.
* @param {integer} dwstart          An integer between 0 (Sunday) and 6 (Saturday) representing the first day of the week.
*
* @example var oBirthdayHelp = new Cathmhaol.DateInput("birthday");
* @example var oAnniversaryHelp = new Cathmhaol.DateInput("anniversary", "1/1/1970");
* @example var oDateHelp = new Cathmhaol.DateInput("birthday", "", '<img src="http://www.cathmhaol.com/images/calendar.gif">', "it");
*/
Cathmhaol.DateInput = function(formInput, defaultDate, helper, iso_639_1_code, dwstart) {
	this._initialize(formInput, defaultDate, helper, iso_639_1_code, dwstart);
};

Cathmhaol.DateInput.prototype = {
	/**
	* @method       close
	* @description  Closes and destroys the calendar popup. If a date was selected, that date is set as the value of the input.
	* @returns {void}
	* @param {Event} evt  The triggering event
	*/
	close: function(evt) {
		evt = evt || window.event;
		var target = evt.target || evt.srcElement;
		this._destroy();
		if (target) {
			try {
				var d = target.title.split("-");
				if (d.length == 3) {
					var yy = parseFloat(d[0]);
					var mm = parseFloat(d[1])-1;
					var dd = parseFloat(d[2]);
					if (mm < 0) { yy--; mm = 12+mm; }
					this._value = new Date(yy, mm, dd);
					if (this.input) {
						this.input.value = (this._value.getMonth()+1) + "/" + this._value.getDate() + "/" + this._value.getFullYear();
					}
 				}
			} catch(err) {
			}
		}
		return;
	},

	/**
	* @method       getLanguageCode
	* @description  Returns the language code being used
	* @returns {string}
	*/
	getLanguageCode: function() {
		return this._languageCode;
	},

	/**
	* @method       getLanguageCodes
	* @description  An array of language codes available
	* @returns {Array}
	*/
	getLanguageCodes: function() {
		var codes = new Array();
		for (var languageCode in Cathmhaol.Calendar.DAYS) {
			codes.push(languageCode);
		}
		return codes;
	},

	/**
	* @property     Day of the week that is the first
	* @description  Cathmhaol.Calendar.SUNDAY, Cathmhaol.Calendar.MONDAY, Cathmhaol.Calendar.TUESDAY, Cathmhaol.Calendar.WEDNESDAY, Cathmhaol.Calendar.THURSDAY, Cathmhaol.Calendar.FRIDAY, Cathmhaol.Calendar.SATURDAY
	* @type {integer}
	*/
	firstDay: Cathmhaol.Calendar.SUNDAY,

	/**
	* @method       open
	* @description  Initializes the popup.
	* @returns {void}
	* @param {integer} top   Top coordinate
	* @param {integer} left  Left coordinate
	*/
	open: function(top, left) {
		this._top = top || this._top;
		this._left = left || this._left;

		this._dnoCalendar = document.createElement("div");
		this._dnoCalendar.style.backgroundColor = "#fff";
		this._dnoCalendar.style.border = "1px solid #000";
		this._dnoCalendar.style.filter = "alpha(opacity=100)";
		this._dnoCalendar.style.opacity = 1;
		this._dnoCalendar.style.marginLeft = "-8px";
		this._dnoCalendar.style.marginTop = "-8px";
		this._dnoCalendar.style.padding = "3px";
		this._dnoCalendar.style.textAlign = "center";
		this._dnoCalendar.style.width = "100%";
		this._dnoCalendar.style.zIndex = document.body.style.zIndex + 10;

		var oTitle = document.createElement("table");
		oTitle.cellPadding = "0";
		oTitle.cellSpacing = "0";
		oTitle.style.margin = "0";
		oTitle.style.padding = "0";
		oTitle.style.width = "100%";
		var oRow = oTitle.insertRow(-1);

		this._panelTitle = oRow.insertCell(-1);
		this._panelTitle.style.cursor = "default";
		this._panelTitle.style.fontFamily = "Verdana";
		this._panelTitle.style.fontSize = ".7em";
		this._panelTitle.style.fontStyle = "normal";
		this._panelTitle.style.fontVariant = "small-caps";
		this._panelTitle.style.fontWeight = "bold";
		this._panelTitle.style.textAlign = "left";
		this._panelTitle.style.verticalAlign = "bottom";
		this._panelTitle.style.width = "80%";

		var oWinCtrl = oRow.insertCell(-1);
		oWinCtrl.style.textAlign = "right";
		oWinCtrl.style.verticalAlign = "bottom";
		oWinCtrl.style.width = "20%";

		this._panelBtnClose = this._button();
		this._panelBtnClose.className = "button close";
		this._panelBtnClose.style.fontSize = ".5em";
		this._panelBtnClose.style.fontWeight = "bold";
		this._panelBtnClose.style.padding = "0";
		this._panelBtnClose.value = "X";
		oWinCtrl.appendChild(this._panelBtnClose);

		this._dnoCalendar.appendChild(oTitle);

		this._panelHeader = document.createElement("div");
		this._panelHeader.style.clear = "both";
		this._panelHeader.style.cursor = "default";
		this._panelHeader.style.cssFloat = "none";
		this._panelHeader.style.fontFamily = "Verdana";
		this._panelHeader.style.fontSize = "1.2em";
		this._panelHeader.style.styleFloat = "none";
		this._panelHeader.style.width = "100%";

		this._dnoCalendar.appendChild(this._panelHeader);

		var oControl = document.createElement("table");
		oControl.cellPadding = "0";
		oControl.cellSpacing = "0";
		oControl.style.margin = "0";
		oControl.style.padding = "0";
		oControl.style.width = "100%";
		var oRow = oControl.insertRow(-1);

		var oCtrl_Prev = oRow.insertCell(-1);
		oCtrl_Prev.style.textAlign = "left";
		oCtrl_Prev.style.verticalAlign = "bottom";
		oCtrl_Prev.style.width = "50%";

		this._panelBtnPrev = this._button();
		this._panelBtnPrev.className = "button previous";
		this._panelBtnPrev.value = "<";
		oCtrl_Prev.appendChild(this._panelBtnPrev);

		var oCtrl_Next = oRow.insertCell(-1);
		oCtrl_Next.style.textAlign = "right";
		oCtrl_Next.style.verticalAlign = "bottom";
		oCtrl_Next.style.width = "50%";

		this._panelBtnNext = this._button();
		this._panelBtnNext.className = "button next";
		this._panelBtnNext.value = ">";
		oCtrl_Next.appendChild(this._panelBtnNext);

		this._dnoCalendar.appendChild(oControl);

		this._panelBody = document.createElement("table");
		this._panelBody.className = "calendar";
		this._panelBody.cellPadding = "1px";
		this._panelBody.cellSpacing = "2px";
		this._panelBody.style.clear = "both";
		this._panelBody.style.cssFloat = "none";
		this._panelBody.style.styleFloat = "none";
		this._panelBody.style.width = "100%";

		this._dnoCalendar.appendChild(this._panelBody);

		this._panel = document.createElement("div");
		this._panel.style.backgroundColor = "#ccc";
		this._panel.style.filter = "alpha(opacity=100)";
		this._panel.style.left = this._left+"px";
		this._panel.style.opacity = 1;
		this._panel.style.padding = "5px";
		this._panel.style.position = "absolute";
		this._panel.style.textAlign = "center";
		this._panel.style.top = this._top+"px";
		this._panel.style.width = "200px";
		this._panel.style.zIndex = document.body.style.zIndex + 1;
		this._panel.appendChild(this._dnoCalendar);

		var scope = this;
		this._panelBtnClose.onclick = function(e) { scope._destroy.apply(scope, [e]); }
		this._panelBtnNext.onclick = function(e) { scope._forward.apply(scope, [e]); }
		this._panelBtnPrev.onclick = function(e) { scope._back.apply(scope, [e]); }

		document.body.insertBefore(this._panel, document.body.firstChild);

		return;
	},

	/**
	* @method       setLanguageCode
	* @description  Sets the language code being used. Returns true if set, false if not.
	* @returns {boolean}
	* @param {string} languageCode  One of the members of Cathmhaol.Calendar.DAYS
	*/
	setLanguageCode: function(languageCode) {
		if (Cathmhaol.Calendar.isSupportedLanguage(languageCode)) {
			this._languageCode = languageCode;
			return true;
		}
		return false;
	},

	/**
	* @method       show
	* @description  Displays the calendar popup at the specified position.
	* @returns {void}
	* @param {integer} top   The pixel position of the top edge of the popup
	* @param {integer} left  The pixel position of the left edge of the popup
	*/
	show: function(top, left) {
		this._top = top || this._top;
		this._left = left || this._left;

		if (!this._panel) { this.open(this._top, this._left); } else { this._panel.style.width = "200px"; }

		this._value = this._getValue();

		this._panelTitle.innerHTML = this._value.getFullYear();
		this._panelHeader.innerHTML = Cathmhaol.Calendar.MONTHS[this.getLanguageCode()][this._value.getMonth()].longName;

		while (this._panelBody.hasChildNodes()) {
			this._panelBody.removeChild(this._panelBody.firstChild);
		}

		var iWidth = 0;
		var oHeader = this._panelBody.insertRow(-1);
		var c = 0;
		while (c < 7) {
			var i = this.firstDay + c;
			i = (i < 7 ? i : i - 7);
			var oDay = this._formatHeader(oHeader.insertCell(-1));
			var sShort = Cathmhaol.Calendar.DAYS[this.getLanguageCode()][i].shortName;
			var sLong = Cathmhaol.Calendar.DAYS[this.getLanguageCode()][i].longName;
			oDay.innerHTML = (sShort ? sShort : sLong);
			oDay.title = sLong;
			c++;
		}

		var dValue = new Date(this._value.getFullYear(), this._value.getMonth(), 1);
		var oWeek = this._panelBody.insertRow(-1);
		for (var c = 0; c < (Cathmhaol.Calendar.dayOfTheWeek(dValue) - this.firstDay); c++) {
			var oDay = this._formatBoundary(oWeek.insertCell(-1));
		}

		var scope = this;
		for (var c = 1; c <= Cathmhaol.Calendar.daysInMonth(this._value); c++) {
			if (oWeek.cells.length == 7) { oWeek = this._panelBody.insertRow(-1); }
			var dValue = new Date(this._value.getFullYear(), this._value.getMonth(), c);
			var oDay = this._formatWeekday(oWeek.insertCell(-1));
			oDay.title = dValue.getFullYear() + "-" + (dValue.getMonth() < 9 ? "0" : "") + (dValue.getMonth() + 1) + "-" + (dValue.getDate() < 10 ? "0" : "") + dValue.getDate();
			oDay.onclick = function(e) { scope.close.apply(scope, [e]); }
			oDay.innerHTML = c.toString();
		}

		for (var c = 7 - oWeek.cells.length; c > 0; c--) {
			var oDay = this._formatBoundary(oWeek.insertCell(-1));
		}

		this._setHeight(this._setWidth());
		this._moveToReadable();
		return;
	},

	/**
	* @private
	* @method       _back
	* @description  Go to the previous month
	* @returns {void}
	* @param {event} evt  Triggering event
	*/
	_back: function(evt) {
		evt = evt || window.event;
		var iYear = this._value.getFullYear();
		var iMonth = this._value.getMonth() - 1;
		if (iMonth < 0) {
			iMonth = 11;
			iYear = iYear - 1;
		}
		this._value = new Date(iYear, iMonth, 1);
		this.show();
		return;
	},

	/**
	* @private
	* @method       _button
	* @description  Creates a button
	* @returns {node}
	*/
	_button: function() {
		var oBtn = document.createElement("input");
		oBtn.style.background = "transparent";
		oBtn.style.border = "1px solid #000";
		oBtn.style.cursor = "pointer";
		oBtn.style.fontFamily = "Verdana"
		oBtn.style.fontSize = ".7em"
		oBtn.style.fontStyle = "normal";
		oBtn.style.fontVariant = "small-caps";
		oBtn.style.fontWeight = "bold";
		oBtn.style.margin = "0";
		oBtn.style.width = "auto";
		oBtn.type = "button";
		return oBtn;
	},

	/**
	* @private
	* @method       _destroy
	* @description  Destroys the popup panel
	* @returns {void}
	* @param {event} evt  Triggering event
	*/
	_destroy: function(evt) {
		if (this._panel) {
			var oDomParent = this._panel.parentNode;
			if (oDomParent) { oDomParent.removeChild(this._panel); }
			this._panel = null;
		}
		return;
	},

	/**
	* @private
	* @method       _formatBoundary
	* @description  Formats the table cells that lie outside of the month.
	* @param {TableCell} oTableCell  To be formatted.
	* @returns {TableCell}
	*/
	_formatBoundary: function(oTableCell) {
		oTableCell.style.backgroundColor = "#ccc";
		oTableCell.style.border = "1px solid #000";
		oTableCell.style.fontFamily = "Verdana";
		oTableCell.style.fontSize = "10px";
		oTableCell.style.height = "3em";
	},

	/**
	* @private
	* @method       _formatHeader
	* @description  Formats the table cells that are the headers for the weekday columns.
	* @param {TableCell} oTableCell  To be formatted.
	* @returns {TableCell}
	*/
	_formatHeader: function(oTableCell) {
		oTableCell.style.cursor = "default";
		oTableCell.style.fontSize = "10px";
		oTableCell.style.overflow = "hidden";
		oTableCell.style.padding = "0";
		oTableCell.style.textAlign = "center";
		oTableCell.style.verticalAlign = "bottom";
		return oTableCell;
	},

	/**
	* @private
	* @method       _formatWeekday
	* @description  Formats the table cells that contain days
	* @returns {TableCell}
	* @param {TableCell} oTableCell  To be formatted.
	*/
	_formatWeekday: function(oTableCell) {
		oTableCell.style.border = "1px solid #000";
		oTableCell.style.cursor = "pointer";
		oTableCell.style.fontFamily = "Verdana";
		oTableCell.style.fontSize = "10px";
		oTableCell.style.height = "3em";
		oTableCell.style.textAlign = "right";
		oTableCell.style.verticalAlign = "top";
		oTableCell.style.width = "14.3%";
		return oTableCell;
	},

	/**
	* @private
	* @method       _forward
	* @description  Go to the next month
	* @returns {void}
	* @param {event} evt  Triggering event
	*/
	_forward: function(evt) {
		evt = evt || window.event;
		var iYear = this._value.getFullYear();
		var iMonth = this._value.getMonth() + 1;
		if (iMonth > 11) {
			iMonth = 0;
			iYear = iYear + 1;
		}
		this._value = new Date(iYear, iMonth, 1);
		this.show();
		return;
	},

	/**
	* @private
	* @method       _initialize
	* @description  Initializes the hashes that contain the month and day names.
	* @returns {void}
	* @param {node|string} formInput    An HTML form input
	* @param {date|string} defaultDate  A value that is a date or can be evaluated as a date
	* @param {node|string} helper       A DOM node or HTML string that can be clicked.
	* @param {string} iso_639_1_code    An ISO 639-1 Alpha-3 language code.
	* @param {integer} dwstart          An integer between 0 (Sunday) and 6 (Saturday) representing the first day of the week.
	*/
	_initialize: function(formInput, defaultDate, helper, iso_639_1_code, dwstart) {
		try {
			if (formInput) { if (typeof formInput == "string") { this.input = document.getElementById(formInput); } else { this.input = formInput; } }
			if (defaultDate) { if (typeof defaultDate == "string") { defaultDate = new Date(defaultDate); } this._value = defaultDate; }

			if (this.input) {
				var scope = this;

				if (helper) {
					if (typeof helper == "string") {
						var oDiv = document.createElement("div");
						oDiv.innerHTML = helper;
						helper = oDiv.childNodes[0];
					}
				} else {
					helper = document.createElement("input");
					helper.type = "button";
					helper.value = "?";
				}
				helper.onclick = function(e) { scope._popup.apply(scope, [e]); }

				this._insertAfter(helper, this.input);
			}
			if (iso_639_1_code) { this.setLanguageCode(iso_639_1_code); }
			if (dwstart) { this.firstDay = dwstart; }
		} catch (err) {
			return;
		}
	},

	/**
	* @private
	* @method       _insertAfter
	* @description  Inserts a DOM node after another.
	* @returns {void}
	* @param {Node} nodeToInsert       The DOM node to insert into the DOM
	* @param {Node} nodeToInsertAfter  The DOM node the nodeToInsert should be inserted after
	*/
	_insertAfter: function(nodeToInsert, nodeToInsertAfter) {
		var parent = nodeToInsertAfter.parentNode;
		var nextSibling = nodeToInsertAfter.nextSibiling;
		if (nextSibling) {
			nextSibling.insertBefore(nodeToInsert);
		} else if (parent) {
			parent.appendChild(nodeToInsert);
		}
		return;
	},

	/**
	* @private
	* @method       _getValue
	* @description  Gets the value associated with the calendar
	* @returns {date}
	*/
	_getValue: function() {
		if (this._value) { return this._value; }
		if (this.input.value && this.input.value != "") { return new Date(this.input.value); }
		return new Date();
	},

	/**
	* @private
	* @property     _languageCode
	* @description  Language code to use
	* @type {string}
	* @default "en"
	*/
	_languageCode: "en",

	/**
	* @private
	* @property     _left
	* @description  The pixel position of the left edge of the popup.
	* @type {integer}
	*/
	_left: null,

	/**
	* @private
	* @method       _moveToReadable
	* @description  Moves the popup to a readable section.
	* @returns {void}
	*/
	_moveToReadable: function() {
		var heightOfScreen = parseFloat(window.innerHeight ? window.innerHeight : (document.body.clientHeight ? document.body.clientHeight : 0));
		var widthOfScreen = parseFloat(window.innerWidth ? window.innerWidth : (document.body.clientWidth ? document.body.clientWidth : 0));

		var heightOfPanel = parseFloat(this._panel.clientHeight);
		var leftOfPanel = parseFloat(this._panel.style.left.replace(/px/, ""));
		var topOfPanel = parseFloat(this._panel.style.top.replace(/px/, ""));
		var widthOfPanel = parseFloat(this._panel.clientWidth);

		if ((topOfPanel + heightOfPanel) > heightOfScreen) {
			this._top = (heightOfScreen - heightOfPanel - 3);
			this._panel.style.top = this._top+"px";
		}
		if ((leftOfPanel + widthOfPanel) > widthOfScreen) {
			this._left = (widthOfScreen - widthOfPanel - 3);
			this._panel.style.left = this._left+"px";
		}
	},

	/**
	* @private
	* @method       _panel
	* @description  The popup
	* @type {Node}
	*/
	_panel: null,

	/**
	* @private
	* @method       _popup
	* @description  Displays the calendar popup
	* @returns {void}
	* @param {Event} evt
	*/
	_popup: function(evt) {
		var evt = evt || window.event;
		this._left = (evt && evt.clientX ? evt.clientX : this._left && this._left > 0 ? this._left : 0);
		this._top  = (evt && evt.clientY ? evt.clientY : this._top  && this._top  > 0 ? this._top  : 0);
		this.show(this._top, this._left);
	},

	/**
	* @private
	* @method       _setHeight
	* @description  Sets the height of the popup based on the width of the cells.
	* @returns {void}
	* @param {integer} h The height of the cell
	*/
	_setHeight: function(h) {
		for (var r = 1; r < this._panelBody.rows.length; r++) {
			for (var c = 0; c < this._panelBody.rows[r].cells.length; c++) {
				this._panelBody.rows[r].cells[c].style.height = h+"px";
			}
		}
		return;
	},

	/**
	* @private
	* @method       _setWidth
	* @description  Sets the width of the popup based on the content width of the header.
	* @returns {integer}
	*/
	_setWidth: function() {
		var iWidth = 0;
		for (var c = 0; c < this._panelBody.rows[0].cells.length; c++) {
			if (this._panelBody.rows[0].cells[c].clientWidth > iWidth) { iWidth = this._panelBody.rows[0].cells[c].clientWidth; }
		}
		this._panel.style.width = ((iWidth * 7) + (parseFloat(this._panelBody.cellSpacing.replace(/px/, "")) * 6) + (parseFloat(this._panelBody.cellSpacing.replace(/px/, "")) * 7)+10)+"px";
		return iWidth;
	},

	/**
	* @private
	* @property     _top
	* @description  The pixel position of the top edge of the popup.
	* @type {integer}
	*/
	_top: null,

	/**
	* @private
	* @method       _value
	* @description  Date value to use
	* @type {date}
	*/
	_value: null
}
