﻿/// <reference path="jquery-latest-vsdoc.js" />
(function ($) {

	$.fn.timesuggest = function (options) {
		if (typeof options.minTime == "string") {
			options.minTime = time.parseNumber(options.minTime);
		}
		if (typeof options.maxTime == "string") {
			options.maxTime = time.parseNumber(options.maxTime);
		}
		var opts = $.extend({}, $.fn.timesuggest.defaults, options);

		return this.each(function () {
			var timesuggest = new suggest(this, null, opts);

			timesuggest.getSuggestions = function (val) {
				var parsed = time.getSuggestions(val);
				var suggestions = new Array();

				for (i = 0; i < parsed.length; i++) {
					var t = parsed[i];

					if (t.hour == undefined) {
						continue;

					} else if (t.minute == undefined && t.meridiem == undefined) {
						var meridiem;
						if (8 <= t.hour && t.hour <= 11)
							meridiem = 'am';
						else {
							meridiem = 'pm';
						}

						for (minute = 0; minute < 60; minute += this.options.minuteIncrement) {
							suggestions.push(new time(t.hour, minute, meridiem));
						}

					} else if (t.minute == undefined) {
						for (minute = 0; minute < 60; minute += this.options.minuteIncrement) {
							suggestions.push(new time(t.hour, minute, t.meridiem));
						}

					} else if (t.meridiem == undefined) {
						var meridiem;
						if (8 <= t.hour && t.hour <= 11)
							meridiem = 'am';
						else {
							meridiem = 'pm';
						}
						suggestions.push(new time(t.hour, t.minute, meridiem));


					} else suggestions.push(t);
				}

				suggestions.sort(function (a, b) {
					var aNum = a.toNumber();
					var bNum = b.toNumber();
					if (aNum < 600) aNum = 2400 - aNum;
					if (bNum < 600) bNum = 2400 - bNum;
					return aNum - bNum;
				});

				for (i = 0; i < suggestions.length; i++) {
					if (suggestions[i].compareTo(this.options.minTime) >= 0 && suggestions[i].compareTo(this.options.maxTime) <= 0) {
						var className;
						var timeNumber = suggestions[i].toNumber();
						if (timeNumber < 600 || timeNumber >= 1800) className = "night";
						else className = "day";
						suggestions[i] = { time: { html: "<span class='" + className + "'>" + suggestions[i].toString() + "</span>", value: suggestions[i].toString()} };
					} else {
						suggestions.splice(i, 1);
						i--;
					}
				}

				this.showSuggestions({ results: suggestions });
			};


			$(this).vldpopup({
				validate: function (field, args) {
					var str = field.val();
					if (str != "hh:mm am/pm" && str != "" && time.parseString(str) == null) {
						return "invalid time";
					} else return null;
				},
				args: {}
			});

			timesuggest.toString = function () {
				return "[timesuggest]";
			};
		});
	};

	// time picker
	$.fn.timesuggest.defaults = {
		minChars: 1,
		showColHeaders: false,
		popupClassName: "suggest timeSuggest",
		minTime: new time(0, 0),
		maxTime: new time(23, 59),
		minuteIncrement: 30,
		delay: 0
	};
})(jQuery);

function time(hour, minute, meridiem) {
	if (typeof hour == "string") {
		// TODO
		var m = /(\d{1,2})(?:\:(\d{2}))?(am|pm)?/.exec(hour);
	}

	this.hour = isNaN(hour) ? undefined : hour;
	this.minute = isNaN(minute) ? undefined : minute;
	this.meridiem = meridiem;

	if (this.hour != undefined) {
		// Convert to 12 hour time
		if (this.hour > 12) {
			this.hour -= 12;
			this.meridiem = "pm";
		} else if (this.hour == 0) {
			this.hour = 12;
			this.meridiem = "am";
		}
	}

	this.toNumber = function() {
		if (this.meridiem == 'pm' && this.hour < 12) {
			return (this.hour + 12) * 100 + this.minute;
		} else if (this.meridiem == 'am' && this.hour == 12) {
			return this.minute;
		} else return this.hour * 100 + this.minute;
	};

	this.addMinutes = function(value) {
		this.minute = parseInt(this.minute) + parseInt(value);
		if(this.minute >= 60 || this.minute < 0) {
			this.addHours(Math.floor(this.minute / 60));
			if(this.minute < 0) {
				this.minute = (60+(this.minute % 60)) % 60;
			} else this.minute = this.minute % 60;
		}

	};

	this.addHours = function(value) {
		var newHour = this.hour + value;
		if (newHour > 12) {
			if (Math.floor(this.hour / 12) % 2 == (this.hour == 12 ? 0 : 1)) {
				this.meridiem = this.meridiem == 'am' ? 'pm' : 'am';
			}
			this.hour = newHour % 12;
		} else if (newHour == 12) {
			this.hour = newHour;
			this.meridiem = this.meridiem == 'am' ? 'pm' : 'am';
		} else this.hour = newHour;
	};

	this.compareTo = function(timeValue) {
		var compareToNumber = timeValue.toNumber();
		var thisNumber = this.toNumber();
		if(thisNumber > compareToNumber) return 1;
		else if(thisNumber < compareToNumber) return -1;
		else return 0;
	};
	
	this.clone = function() {
		return new time(this.hour, this.minute, this.meridiem);
	};
}

time.prototype.toString = function() {
	return this.hour + ':' + (this.minute < 10 ? '0' : '') + this.minute + ' ' + this.meridiem;
};

time.parseNumber = function(number) {
	if (number >= 0 && number <= 12) return new time(number, 0, 'am');
	else if (number <= 23) return new time(number - 12, 0, 'pm');
	else if (number > 100) {
		var hour = Math.floor(number / 100);
		var minute = number - (hour * 100);
		if (hour > 23 || minute > 59) return null;
		else if (hour > 12) return new time(hour - 12, minute, 'pm');
		else if (hour == 12) return new time(hour, minute, 'pm');
		else if (hour == 0) return new time(12, minute, 'am');
		else return new time(hour, minute, 'am');
	}
};

time.parseString = function(text) {
	if (text.search(/^\d{1,2}:\d{2} (am|pm)$/g) != 0) return null;
	else return time.getSuggestions(text)[0];
}

time.getSuggestions = function(text) {
	// format text
	text = new String(text);
	text = text.toLowerCase();
	text = text.replace(/\.(?=[0-9])/, ':'); // replace the first dot with a colon
	text = text.replace(/\.|[b-oq-z]|\s/gi, ''); // remove all other dots, whitespace and 'm'

	var meridiem = undefined;
	var parsed = new Array();

	// strip out and store the meridiem
	var len = text.length;
	if(len >= 1) {
		meridiem = text.substring(len - 1) + 'm';
		if(meridiem == 'am' || meridiem == 'pm') {
			text = text.substring(0, len - 1);
			len = text.length;
		} else meridiem = undefined;
	}

	if(text.indexOf(':') != -1 && len <= 5) {
		// if there is a colon
		var hour = 0, minute = undefined;
		var colonIndex = text.indexOf(':');
		if(colonIndex < len - 2) minute = parseInt(text.substring(colonIndex + 1), 10);
		else if(colonIndex < len - 1) minute = parseInt(text.substring(colonIndex + 1) + '0', 10);
		if(colonIndex > 0) hour = parseInt(text.substring(0, colonIndex), 10);
		parsed.push(new time(hour, minute, meridiem));
		
	} else if(len > 0 && len <= 4) {
		// count leading zeros
		var leadingZeros = 0;
		while(text.charAt(leadingZeros) == '0') leadingZeros++;

		// if there is a number
		var number = parseInt(text, 10);

		if(number >= 1000) {
			if(leadingZeros == 0) {
				parsed.push(new time(parseInt(text.substring(0, 2), 10), parseInt(text.substring(2), 10), meridiem));
			}

		} else if(number >= 100) {
			if(leadingZeros == 1) {
				parsed.push(new time(parseInt(text.substring(0, 2), 10), parseInt(text.substring(2), 10), meridiem));
			} else if(leadingZeros == 0) {
				parsed.push(new time(parseInt(text.substring(0, 1), 10), parseInt(text.substring(1), 10), meridiem));
				parsed.push(new time(parseInt(text.substring(0, 2), 10), parseInt(text.substring(2) + '0', 10), meridiem));
			}

		} else if(number >= 10) {
			if(leadingZeros == 2) {
				parsed.push(new time(0, number, meridiem));
			} else if(leadingZeros == 1) {
				parsed.push(new time(parseInt(text.charAt(1), 10), parseInt(text.charAt(2) + '0', 10), meridiem));
			} else if(leadingZeros == 0) {
				parsed.push(new time(number, undefined, meridiem));
				parsed.push(new time(parseInt(text.substring(0, 1), 10), parseInt(text.charAt(1) + '0', 10), meridiem));
			}

		} else if(number > 0) {
			if(leadingZeros == 3) {
				parsed.push(new time(0, number, meridiem));
			} else if(leadingZeros == 2) {
				parsed.push(new time(0, number + '0', meridiem));
			} else if(leadingZeros == 1) {
				parsed.push(new time(number, undefined, meridiem));
			} else if(leadingZeros == 0) {
				parsed.push(new time(number, undefined, meridiem));
				if(number == 2 && meridiem == undefined) {
					//	for (i = 20; i < 24; i++) { parsed.push(new time(i, undefined, meridiem)); }
				} else if(number == 1 && meridiem == undefined) {
					for(i = 10; i <= 12; i++) { parsed.push(new time(i, undefined, meridiem)); }
				}
			}

		} else if(number == 0) {
			if(leadingZeros == 2) {
				parsed.push(new time(0, undefined, meridiem));
			} else if(leadingZeros == 3 || leadingZeros == 4) {
				parsed.push(new time(0, 0, meridiem));
			}
		}
	} else if(meridiem != undefined) {
		parsed.push(new time(undefined, undefined, meridiem));
	}

	// validate and reformat parsed times
	for(i = 0; i < parsed.length; i++) {
		if(parsed[i].minute >= 60 || parsed[i].hour > 24) {
			// remove time with invalid minutes or hours
			parsed.splice(i, 1);
			i--;
		} else if(parsed[i].hour == 24) {
			parsed[i].hour = 11;
			parsed[i].minute = 59;
			parsed[i].meridiem = 'pm';
		} else if(parsed[i].hour > 12 && parsed[i].meridiem == 'am') {
			// remove time with invalid meridiem
			parsed.splice(i, 1);
			i--;
		} else if(parsed[i].hour > 12) {
			// convert to 12 hour
			parsed[i].hour -= 12;
			parsed[i].meridiem = 'pm';
		} else if(parsed[i].hour == 0) {
			parsed[i].hour = 12;
			if(parsed[i].meridiem == undefined) parsed[i].meridiem = 'am';
		}
	}
	return parsed;
}


