(function( $, undefined ) {

$.extend($.ui, { calendar: { version: "0.1" } });

var PROP_NAME = 'calendar';

function Calendar() {

	this.regional = []; // Available regional settings, indexed by language code

	this.regional[''] = { // Default regional settings
		prevText: 'Prev', // Display text for previous month link
		nextText: 'Next', // Display text for next month link
		currentText: 'Today', // Display text for current month link
		monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], // Names of months for drop-down and formatting
		monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
		dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
		dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
		dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
		dateFormat: 'mm/dd/yy', // See format options on parseDate
		firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
		free: 'free',
		occupied: 'occupied',
		occupiedKZW: 'occupied KZW',
		occupiedOther: 'occupied Other',
		reserved: 'reserved'
	};

	this._defaults = {		// Global defaults for all the date picker instances
		numberOfMonths: 1,	// Number of months to show at a time
		showYear: 0,		// The Year this Calender should start from. 0 if current Year. If this is 0 a offset can be spezified with showYearOffset
		showYearOffset: 0,	// If showYear is 0 than you can specifie a offset to the current Year
		showMonth: 0,		// The Month this Calender should start from. 0 if current Month. If this is 0 a offset can be spezified with showMonthOffset
		showMonthOffset: 0,	//If showMonth is 0 than you can specifie a offset to the current Month
		firstDay: 0,		// The Weekday the week starts with. 0 = So by default
		stepMonths: 1,		// Number of months to step back/forward
		showKZW: true,		// Show occupations of type kzw
		showOther: true,	// Show occupations of type other
		showReservation: true,
		showOnlyOccupiedOrFree: true,
		occupations: [],
		widthInEm: ''
	};

	$.extend(this._defaults, this.regional['']);
}

$.extend(Calendar.prototype, {
	/* Class name added to elements to indicate already configured with a date picker. */
	markerClassName: 'hasCalendar',

	/* Debug logging (if enabled). */
	log: function () {
		if (this.debug)
			console.log.apply('', arguments);
	},

	/* Override the default settings for all instances of the date picker.
	   @param  settings  object - the new settings to use as defaults (anonymous object)
	   @return the manager object */
	setDefaults: function(settings) {
		extendRemove(this._defaults, settings || {});
		return this;
	},

	/* Get a setting value, defaulting if necessary. */
	_get: function(inst, name) {
		return inst.settings[name] !== undefined ? inst.settings[name] : this._defaults[name];
	},

	/* Create a new instance object. */
	_newInst: function(target) {
		var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
		return {
			id: id,
			currentShownMonthOffset: 0
			};
	},


	_compareBelegungsDate: function( a, b ) {
		return kzw.util.comparator.dateAsc( a.from, b.from );
	},


	_attachCalendar: function(target, settings) {
		/* Replace Target */
		var newId = 'calendarBox' + new Date().getTime();
		$(target).replaceWith('<div id="' + newId + '"></div>');
		target = $('#'+newId);

		/* Instance */
		var inst = this._newInst($(target));
		inst.settings = $.extend({}, settings || {});

		/* Sort Belegungen */
		if( inst.settings['occupations'] == undefined ) {
			inst.settings['occupations'] = this._defaults['occupations'];
		};
		if( inst.settings['occupations'] !== undefined && inst.settings['occupations'].length != 0 ) {
			inst.settings['occupations'].sort( this._compareBelegungsDate );
		}

		$(target).append(this._generateHTML(inst, 0));
		this._attachNextPrevLink(inst);

	},


	_generateHTML: function(inst) {
		/*
		 * Get Values from Settings
		 */
		var widthInEm = this._get(inst, 'widthInEm');

		var occupations = this._get(inst, 'occupations');
		var showKZW = this._get(inst, 'showKZW');
		var showOther = this._get(inst, 'showOther');
		var showReservation = this._get(inst, 'showReservation');
		var showOnlyOccupiedOrFree = this._get(inst, 'showOnlyOccupiedOrFree');

		var showYear = this._get(inst, 'showYear');
		var showYearOffset = this._get(inst, 'showYearOffset');
		var showMonth = this._get(inst, 'showMonth');
		var showMonthOffset = this._get(inst, 'showMonthOffset');

		var numOfShownMonths = this._get( inst, 'numberOfMonths' );
		var dayNames = this._get(inst, 'dayNames');
		var dayNamesMin = this._get(inst, 'dayNamesMin');
		var monthNames = this._get(inst, 'monthNames');
		var monthNamesShort = this._get(inst, 'monthNamesShort');
		var weekStartDay = parseInt(this._get(inst, 'firstDay'),10);
		weekStartDay = (isNaN(weekStartDay) ? 0 : weekStartDay);

		var date_now = new Date();
		var date_firstShownDay;

		var yearOfFirstShownMonth = ( showYear == 0 ) ? date_now.getFullYear() + showYearOffset : showYear;
		var firstShownMonth = ( showMonth == 0 ) ? date_now.getMonth() + showMonthOffset + inst.currentShownMonthOffset : showMonth + inst.currentShownMonthOffset;

		var html = '';
		var style = '';
		if( widthInEm != '' ) {
			style = ' style="with:'+widthInEm+'em"';
		}
		if( numOfShownMonths > 1 ) {
			html = '<div' + style + ' class="ui-calendar-inline ui-calendar ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-calendar-multi-' + numOfShownMonths + ' ui-calendar-multi" style="width: ' + (numOfShownMonths*17) + 'em;">';
		} else {
			html = '<div' + style + ' class="ui-calendar-inline ui-calendar ui-widget ui-widget-content ui-helper-clearfix ui-corner-all">';
		}

		for( var monthIndex = 0; monthIndex < numOfShownMonths; monthIndex++ ) {
			var firstDayOfMonth = new Date( yearOfFirstShownMonth, firstShownMonth + monthIndex, 1, 0, 0, 0 );
			var leadDays = (firstDayOfMonth.getDay() - weekStartDay + 7) % 7;
			var numDaysOfMonth = 32 - new Date(yearOfFirstShownMonth, firstShownMonth + monthIndex, 32).getDate();
			var numShownWeeks = Math.ceil( ( leadDays + numDaysOfMonth) / 7);

			if( monthIndex == 0 ) {
				html += '<div class="ui-calendar-group ui-calendar-group-first">';
				html += '<div class="ui-calendar-header ui-widget-header ui-helper-clearfix ui-corner-left">';
				html += '<a title="&lt;zurück" class="ui-calendar-prev ui-corner-all"><span class="ui-icon ui-icon-circle-triangle-w">&lt;zurück</span></a>';
			} else if( monthIndex == numOfShownMonths - 1 ) {
				html += '<div class="ui-calendar-group ui-calendar-group-last">';
				html += '<div class="ui-calendar-header ui-widget-header ui-helper-clearfix ui-corner-right">';
				html += '<a title="Vor&gt;" class="ui-calendar-next ui-corner-all"><span class="ui-icon ui-icon-circle-triangle-e">Vor&gt;</span> </a>';
			} else {
				html += '<div class="ui-calendar-group ui-calendar-group-middle">';
				html += '<div class="ui-calendar-header ui-widget-header ui-helper-clearfix">';
			}

			html += '<div class="ui-calendar-title"><span title="' + monthNames[firstDayOfMonth.getMonth()] + '" class="ui-calendar-month">' + monthNamesShort[firstDayOfMonth.getMonth()] + '</span>&nbsp;<span class="ui-calendar-year">' + firstDayOfMonth.getFullYear() + '</span></div>';
			html += '</div>';
			html += '<table class="ui-calendar-calendar">';

			html += '<thead><tr>';
			var thead = '';
			for ( var dow = 0; dow < 7; dow++ ) {
				var day = (dow + weekStartDay) % 7;
				html += '<th' + ((dow + weekStartDay + 6) % 7 >= 5 ? ' class="ui-calendar-week-end"' : '') + '>' + '<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
			}
			html += '</tr></thead>';

			html += '<tbody>';
			var dayOfMonth = 1;
			for( var week = 0; week < numShownWeeks; week++ ) {
				html += '<tr>';
				for( var day = 0; day < 7; day++ ) {
					var indexOfShownDay = ( (week * 7) + day );
					if( indexOfShownDay < leadDays || indexOfShownDay >= numDaysOfMonth + leadDays ) {
						html += '<td class=" ui-calendar-other-month ui-calendar-unselectable ui-state-disabled">&nbsp;</td>';
					} else {
						var link = '';
						var htmlClass = 'ui-state-default';
						var currentDayDate = new Date( firstDayOfMonth.getFullYear(), firstDayOfMonth.getMonth(), dayOfMonth );
						for( var i = 0; i < occupations.length; i++ ) {
							var bel = occupations[i];
							if( currentDayDate < bel.from ) {
								/* Wenn diese Belegung nach dem gesuchten Tag liegt brauchen wir den Rest nicht mehr untersuchen, weil die Belegungen sind sortiert sind */
								break;
							} else if( currentDayDate <= bel.till ) {
								if( showOther && bel.type == 'other' || showKZW && bel.type == 'kzw' || showReservation && bel.type == 'reservation' ) {
									if( bel.type == 'other' ) {
										htmlClass = showOnlyOccupiedOrFree ? 'ui-state-occupied-kzw' : 'ui-state-occupied-other';
									} else if( bel.type == 'kzw' ) {
										htmlClass = showOnlyOccupiedOrFree ? 'ui-state-occupied-kzw' : 'ui-state-occupied-kzw';
									} else if( bel.type == 'reservation' ) {
										htmlClass = showOnlyOccupiedOrFree ? 'ui-state-occupied-kzw' : 'ui-state-reserved';
									}
									if( bel.from <= currentDayDate && bel.from >= currentDayDate ) {
										htmlClass += ' ui-corner-left';
									}
									if( bel.till <= currentDayDate && bel.till >= currentDayDate ) {
										htmlClass += ' ui-corner-right';
									}
									if( bel.link !== undefined ) {
										link = bel.link;
									}
								}
							}
						}
						if( link != '' ) {
							html += '<td class=""><a href="' + link + '" class="' + htmlClass + '">' + dayOfMonth + '</a></td>';
						} else {
							html += '<td class=""><span class="' + htmlClass + '">' + dayOfMonth + '</span></td>';
						}
						dayOfMonth++;
					}
				}
				html += '</tr>';
			}
			html += '</tbody>';
			html += '</table>';
			html += '</div>';
		}

		/* Footer - Lengend */
		html += '<div class="ui-calendar-footer"><table style="width: auto;"><tr>';
		html += '<td>' + this._get(inst, 'free') + ': </td><td><span class="ui-state-default">xy</span></td>';
		if( showOnlyOccupiedOrFree ) {
			html += '<td>&nbsp;</td><td>' + this._get(inst, 'occupied') + ': </td><td><span class="ui-state-occupied-kzw">xy</span></td>';
		} else {
			if( showReservation ) {
				html += '<td>&nbsp;</td><td>' + this._get(inst, 'reserved') + ': </td><td><span class="ui-state-reserved">xy</span></td>';
			}
			if( showKZW ) {
				html += '<td>&nbsp;</td><td>' + this._get(inst, 'occupiedKZW') + ': </td><td><span class="ui-state-occupied-kzw">xy</span></td>';
			}
			if( showOther ) {
				html += '<td>&nbsp;</td><td>' + this._get(inst, 'occupiedOther') + ': </td><td><span class="ui-state-occupied-other">xy</span></td>';
			}
		}
		html += '</tr></table></div>';
		html += '</div>';
		return html;
	},


	_attachNextPrevLink: function(inst) {
		var target = $('#' + inst.id);

		$(target).find(".ui-calendar-prev").click(function() {
			$.calendar._showPreviousMonths(inst);
		});

		$(target).find(".ui-calendar-next").click(function() {
			$.calendar._showNextMonths(inst);
		});

		$(target).find('button, .ui-calendar-prev, .ui-calendar-next, .ui-calendar-calendar td a')
			.bind('mouseout', function(){
				$(this).removeClass('ui-state-hover');
				if(this.className.indexOf('ui-calendar-prev') != -1) $(this).removeClass('ui-calendar-prev-hover');
				if(this.className.indexOf('ui-calendar-next') != -1) $(this).removeClass('ui-calendar-next-hover');
			})
			.bind('mouseover', function(){
				$(this).parents('.ui-calendar-calendar').find('a').removeClass('ui-state-hover');
				$(this).addClass('ui-state-hover');
				if(this.className.indexOf('ui-calendar-prev') != -1) $(this).addClass('ui-calendar-prev-hover');
				if(this.className.indexOf('ui-calendar-next') != -1) $(this).addClass('ui-calendar-next-hover');
			})
			.end()
			.find('.' + this._dayOverClass + ' a')
			.trigger('mouseover')
			.end();
	},


	_showNextMonths: function(inst) {
		inst.currentShownMonthOffset += this._get(inst, "stepMonths");
		$('#' + inst.id).children('.ui-calendar').replaceWith($.calendar._generateHTML(inst));
		this._attachNextPrevLink(inst);
	},

	_showPreviousMonths: function(inst) {
		inst.currentShownMonthOffset -= this._get(inst, "stepMonths");
		$('#' + inst.id).children('.ui-calendar').replaceWith($.calendar._generateHTML(inst));
		this._attachNextPrevLink(inst);
	}
});



/* jQuery extend now ignores nulls! */
function extendRemove(target, props) {
	$.extend(target, props);
	for (var name in props)
		if (props[name] == null || props[name] == undefined)
			target[name] = props[name];
	return target;
};

/* Determine whether an object is an array. */
function isArray(a) {
	return (a && (($.browser.safari && typeof a == 'object' && a.length) || (a.constructor && a.constructor.toString().match(/\Array\(\)/))));
};

/* Invoke the calendar functionality.
   @param  options  string - a command, optionally followed by additional parameters or
                    Object - settings for attaching new calendar functionality
   @return  jQuery object */
$.fn.calendar = function(options){

	if ( ! this.length ) {
		return this;
	}

	/* Initialise the date picker. */
	if ( ! $.calendar.initialized ) {
		$.calendar.initialized = true;
	}

	var otherArgs = Array.prototype.slice.call(arguments, 1);
	if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
		return $.calendar['_' + options + 'Calendar'].apply($.calendar, [this[0]].concat(otherArgs));
	if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
		return $.calendar['_' + options + 'Calendar'].apply($.calendar, [this[0]].concat(otherArgs));
	return this.each(function() {
		typeof options == 'string' ?
			$.calendar['_' + options + 'Calendar'].apply($.calendar, [this].concat(otherArgs)) :
			$.calendar._attachCalendar(this, options);
	});
};

$.calendar = new Calendar();
$.calendar.initialized = false;
$.calendar.uuid = new Date().getTime();
$.calendar.version = "0.1";

})(jQuery);

