/*
 * This file is part of Nuts Framework.
 * Copyright (C) 2009 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
(function($) {
	$.timepicker = $.timepicker || {};
	$.timepicker.defaults = {
		timeFormat: 'HH:mm:ss',
		showOn: 'both', // 'focus' for popup on focus, 'button' for trigger button, or 'both' for either
		showAnim: 'fadeIn', // Name of jQuery animation for popup
		showOptions: {}, // Options for enhanced animations
		duration: 'fast', // Duration of display/closure
		defaultTime: null, // Used when field is blank: actual date or time string
		appendText: '', // Display text following the input box, e.g. showing the format
		buttonText: '...', // Text for trigger button
		buttonImage: '', // URL for trigger button image
		buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
		styleClass: 'ui-timepicker-s-', // The name of the style marker class
		inlineClass: 'ui-timepicker-i', // The name of the inline marker class
		globalClass: 'ui-timepicker-g', // The name of the global marker class
		triggerClass: 'ui-timepicker-trigger' // The name of the trigger marker class
	};

	var _uuid = new Date().getTime();
	var _showing = false;
	var _$tp;
	var _$tf;
	
	var _html = '<div class="ui-timepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>';

	function zeropad(n) {
		return (n < 10 ? '0' + n : n);
	}
	
	function render($tp, s) {
//		if ($.browser.msie && parseInt($.browser.version, 10) < 7) {
//			$tp.addClass('ui-timepicker-msie6');
//		}

		var p = s.timeFormat.pattern;
		var th = (p.indexOf('h') >= 0) || (p.indexOf('H') >= 0);
		var tm = p.indexOf('m') >= 0;
		var ts = p.indexOf('s') >= 0;
		
		$tp.removeClass(s.styleClass + 1)
			.removeClass(s.styleClass + 2)
			.removeClass(s.styleClass + 3)
			.addClass(s.styleClass + ((th ? 1 : 0) + (tm ? 1 : 0) + (ts ? 1 : 0)))
			.html('<div class="ui-timepicker-tc ui-widget ui-widget-header ui-corner-all">'
					+ (th ? '<input type="text" class="ui-timepicker-thh" maxlength="2" value="00"></input>' : '')
					+ (th && (tm || ts) ? '<div class="ui-timepicker-ss1">:</div>': '')
					+ (tm ? '<input type="text" class="ui-timepicker-tmm" maxlength="2" value="00"></input>': '')
					+ ((th || tm) && ts ? '<div class="ui-timepicker-ss2">:</div>' : '')
					+ (ts ? '<input div="text" class="ui-timepicker-tss" maxlength="2" value="00"></input>' : '')
				+ '</div>'
				+ '<div class="ui-timepicker-sc ui-widget-content">'
						+ (th ? '<div class="ui-timepicker-shh"></div>' : '')
						+ (th && (tm || ts) ? '<div class="ui-timepicker-ds1 ui-widget-content"></div>' : '')
						+ (tm ? '<div class="ui-timepicker-smm"></div>' : '')
						+ ((th || tm) && ts ? '<div class="ui-timepicker-ds2 ui-widget-content"></div>' : '')
						+ (ts ? '<div class="ui-timepicker-sss"></div>' : '')
				+ '</div>')
			.find('.ui-timepicker-shh').slider({
				orientation : "vertical",
				range : 'min',
				min : 0,
				max : 23,
				step : 1,
				slide : function(evt, ui) {
					var $p = $(evt.target).closest('.ui-timepicker');
					$p.find('.ui-timepicker-thh').val(zeropad(ui.value));
					setTimeout(function() { update($p); }, 10);
				}
			}).end()
			.find('.ui-timepicker-smm').slider({
				orientation : "vertical",
				range : 'min',
				min : 0,
				max : 59,
				step : 1,
				slide : function(evt, ui) {
					var $p = $(evt.target).closest('.ui-timepicker');
					$p.find('.ui-timepicker-tmm').val(zeropad(ui.value));
					setTimeout(function() { update($p); }, 10);
				}
			}).end()
			.find('.ui-timepicker-sss').slider({
				orientation : "vertical",
				range : 'min',
				min : 0,
				max : 59,
				step : 1,
				slide : function(evt, ui) {
					var $p = $(evt.target).closest('.ui-timepicker');
					$p.find('.ui-timepicker-tss').val(zeropad(ui.value));
					setTimeout(function() { update($p); }, 10);
				}
			}).end()
			.find('.ui-timepicker-thh').keyup(function(e) {
				var $t = $(this);
				var $p = $t.closest('.ui-timepicker');
				var $s = $p.find('.ui-timepicker-shh');
				var v = $t.val();
				if (v.length == 0) {
					$s.slider('value', 0);
				}
				else if (v.match(/^([0-9]|0[0-9]|1[0-9]|2[0-3])$/) != null) {
					$s.slider('value', v);
				} 
				else {
					$t.val($s.slider('value'));
				}
				update($p);
			}).end()
			.find('.ui-timepicker-tmm').keyup(function(e) {
				var $t = $(this);
				var $p = $t.closest('.ui-timepicker');
				var $s = $p.find('.ui-timepicker-smm');
				var v = $t.val();
				if (v.length == 0) {
					$s.slider('value', 0);
				}
				else if (v.match(/^([0-9]|0[0-9]|[1-5][0-9])$/) != null) {
					$s.slider('value', v);
				} 
				else {
					$t.val($s.slider('value'));
				}
				update($p);
			}).end()
			.find('.ui-timepicker-tss').keyup(function(e) {
				var $t = $(this);
				var $p = $t.closest('.ui-timepicker');
				var $s = $p.find('.ui-timepicker-sss');
				var v = $t.val();
				if (v.length == 0) {
					$s.slider('value', 0);
				}
				else if (v.match(/^([0-9]|0[0-9]|[1-5][0-9])$/) != null) {
					$s.slider('value', v);
				} 
				else {
					$t.val($s.slider('value'));
				}
				update($p);
			});
	}

	function unrender($tp) {
		$tp.find('.ui-timepicker-shh, .ui-timepicker-smm, .ui-timepicker-sss').slider('destroy');
		$tp.find('.ui-timepicker-thh, .ui-timepicker-tmm, .ui-timepicker-tss').unbind('keyup');
	}
	
	function destroy($i) {
		var s = $i.data("timepicker");
		var $tp = s.$tp;
		
		if ($tp == _$tp) {
			if (s.trigger) {
				s.trigger.unbind('click');
				s.trigger.remove();
			}
			$i.unbind('focus', show);
		}
		else {
			unrender($tp);
			$tp.remove();
		}
		
		$i.data("timepicker", null);
	}

	function create(el, s) {
		s = $.extend({}, $.timepicker.defaults, s);

		var $i = $(el);
		
		if ($i.data("timepicker")) {
			return;
		}
		
		if (typeof(s.timeFormat) == 'string') {
			s.timeFormat = new DateFormat(s.timeFormat);
		}

		var nodeName = el.nodeName.toLowerCase();
		var inline = (nodeName == 'div' || nodeName == 'span');

		if (inline) {
			var $tp = $(_html).attr("id", "ui_tp_" + (++_uuid))
						.addClass(s.inlineClass)
						.data('target', $i)
						.appendTo(el);

			s.$tp = $tp;
			$i.data("timepicker", s);

			render($tp, s);
			
			parse.call(el);
		}
		else {
			if (!_$tp) {
				if ($.browser.msie && parseInt($.browser.version,10) < 7) {
					_$tf = $('<iframe id="ui_tp_if_' + _uuid + '" class="ui-timepicker-f" src="javascript:false;" frameborder="0"></iframe>').appendTo('body');
				}
				_$tp = $(_html).attr("id", "ui_tp_" + _uuid).addClass(s.globalClass).appendTo('body');
			}

			s.$tp = _$tp;
			$i.data("timepicker", s);

			if (s.showOn == 'focus' || s.showOn == 'both') { // pop-up date picker when in the marked field
				$i.focus(show);
			}
			if (s.showOn == 'button' || s.showOn == 'both') { // pop-up date picker when button clicked
				if (!s.trigger) {
					s.trigger = $(s.buttonImageOnly ?
							$('<img/>').addClass(s.triggerClass)
								.attr({ src: s.buttonImage, alt: s.buttonText, title: s.buttonText }) :
							$('<button type="button"></button>').addClass(s.triggerClass)
								.html(s.buttonImage == '' ? s.buttonText : $('<img/>')
								.attr({ src: s.buttonImage, alt: s.buttonText, title: s.buttonText })));
				}
				$i.after(s.trigger);
				s.trigger.click(function(evt) {
					if (!_showing) {
						if (_$tp && _$tp.is(':visible') && _$tp.data('target').get(0) == el) {
							close.call(el, evt);
						}
						else {
							$i.focus();
							if (s.showOn == 'button') {
								show.call(el, evt);
							}
						}
					}
					return false;
				});
			}
		}
	}
	
	function update($tp) {
		var $i = $tp.data('target');
		var d = getDate($tp);
		if (d && $i) {
			var s = $i.data("timepicker");
			var ds = s.timeFormat.format(d);

			$i.val(ds).trigger('change');
			if (s.onSelect) {
				s.onSelect(ds, d);
			}
		}
	}

	function getPicker($i) {
		return $i.data("timepicker").$tp;
	}
	
	function parse() {
		setTime($(this));
	}

	function getTime($i) {
		var s = $i.data("timepicker");
		var d = getDate(s.$tp);
		return d ? s.timeFormat.format(d) : null;
	}

	function getDate($tp) {
		var $i = $tp.data('target');
		if ($i) {
			var s = $i.data("timepicker");
			var d = s.timeFormat.parse($i.val() || (typeof(s.defaultTime) == 'string' ? s.defaultTime : ''))
						|| (typeof(s.defaultTime) == 'object' ? s.defaultTime : null) || new Date();
			
			var hh = $tp.find('.ui-timepicker-thh').val();
			var mm = $tp.find('.ui-timepicker-tmm').val();
			var ss = $tp.find('.ui-timepicker-tss').val();
			
			if (hh != null) {
				hh = parseInt(hh);
				d.setHours(isNaN(hh) ? 0 : hh);
			}
			if (mm != null) {
				mm = parseInt(mm);
				d.setMinutes(isNaN(mm) ? 0 : mm);
			}
			if (ss != null) {
				ss = parseInt(ss);
				d.setSeconds(isNaN(ss) ? 0 : ss);
			}
			return d;
		}
		return null;
	}
	
	function setTime($i, t) {
		var s = $i.data("timepicker");
		var d = s.timeFormat.parse(t || $i.val() 
				|| (typeof(s.defaultTime) == 'string' ? s.defaultTime : '')
			)
			|| (typeof(s.defaultTime) == 'object' ? s.defaultTime : null) 
			|| new Date();

		setDate(s.$tp, d);
		
		return s.timeFormat.format(d);
	}
	
	function setDate($tp, d) {
		if (d) {
			var hh = zeropad(d.getHours());
			var mm = zeropad(d.getMinutes());
			var ss = zeropad(d.getSeconds());
			
			$tp.find('.ui-timepicker-shh').slider('option', 'value', hh).end()
				.find('.ui-timepicker-smm').slider('option', 'value', mm).end()
				.find('.ui-timepicker-sss').slider('option', 'value', ss).end()
				.find('.ui-timepicker-thh').val(hh).end()
				.find('.ui-timepicker-tmm').val(mm).end()
				.find('.ui-timepicker-tss').val(ss).end()
			;
		}
		return d;
	}
	
	function postShow() {
		$(document).mouseup(close);
		_showing = false;
		if (_$tf) {
			_$tf.css({
				left: _$tp.css('left'),
				top: _$tp.css('top'),
				width: _$tp.outerWidth(),
				height: _$tp.outerHeight()
			});
		}
	}
	
	function postHide() {
		unrender(_$tp);
		_showing = false;
		if (_$tp) {
			_$tp.css({ left: '-9999px', top: '-9999px' });
		}
	}
	
	function show() {
		if (_showing) {
			return;
		}

		close();

		_showing = true;
			
		var $i = $(this);
		var s = $i.data("timepicker");

		render(_$tp, s);

		var of = $i.offset();
		var top = of.top + $i.outerHeight();
		var left = of.left;

		_$tp.data('target', $i).css({
			left: left,
			top: top
		});

		if ($.effects && $.effects[s.showAnim]) {
			_$tp.show(s.showAnim, s.showOptions, s.duration, postShow);
		}
		else {
			_$tp[s.showAnim || 'show']((s.showAnim ? s.duration : null), postShow);
		}

		parse.call(this);
		$i.keyup(parse);
	}
	
	function close(evt) {
		if (_showing || _$tp.is(':hidden') || !_$tp.data('target')
				|| (evt && (_$tp.data('target').get(0) == evt.target
						|| $(evt.target).closest('.ui-timepicker').attr('id') == _$tp.attr('id')))) {
			return;
		}

		_showing = true;

		if (_$tf) {
			_$tf.css({ left: '-9999px', top: '-9999px' });
		}

		var $i = _$tp.data('target');

		$(document).unbind('mouseup', close);
		$i.unbind('keyup', parse);

		if (evt) {
			var s = $i.data("timepicker");
			if ($.effects && $.effects[s.showAnim]) {
				_$tp.hide(s.showAnim, s.showOptions, s.duration, postHide);
			}
			else {
				_$tp[(s.showAnim == 'slideDown' ? 'slideUp' :
					(s.showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((s.showAnim ? s.duration : null), postHide);
			}
		}
		else {
			postHide();
		}
	}
	
	$.fn.timepicker = function(s, v) {
		switch (s) {
		case 'destroy':
			return destroy(this);
		case 'getDate':
			return this.length ? getDate(getPicker(this)) : null;
		case 'setDate':
			return this.length ? setDate(getPicker(this), v) : v;
		case 'getTime':
			return this.length ? getTime(this) : null;
		case 'setTime':
			return this.length ? setTime(this, v) : v;
		default:
			return this.each(function() {
				create(this, s);
			});
		}
	};
})(jQuery);
