(function($) {

$.extend($.fn, {
	datefromto: function(poOpts,poCallback)
	{
		var debug = ((typeof console != 'undefined' 
			&& location.href.match(/datefromtodebug\=/))? 
		console : 
		{
			group: function(){},
			groupEnd: function(){},
			log:function(){},
			info:function(){},
			warn: function(){},
			error: function(){},
			table: function(){}
		});
	
		debug.group('jquery.datefromto');
		
		var aRestrictions = (typeof poOpts.restrictions != 'undefined')?
			poOpts.restrictions : [];
		
		var oDefRes = 
		{	
			iMinStayDays: 1,
			iMaxStayDays: 0,
			iMinPersons: 1,
			iMaxPersons: 10,
			sArrivalDays: '0,1,2,3,4,5,6',
			sDepartureDays: '0,1,2,3,4,5,6',
			iPrebookingDays: 1,
			bBlock: false
		};
		
		var getRestriction = function(iDate)
		{
			//console.group('getRestriction');
			
			var oRestriction = oDefRes;
			jQuery.each(aRestrictions,function(index, oCurrRes)
			{
				if(iDate >= oCurrRes.iFromDate && iDate <= oCurrRes.iToDate)
				{
					oRestriction = oCurrRes;
					// break loop
					return false;
				}
			});
			
			oRestriction.aArrivalDays = (oRestriction.sArrivalDays)?
				oRestriction.sArrivalDays.split(','): [];
				
			oRestriction.aDepartureDays = (oRestriction.sDepartureDays)? 
				oRestriction.sDepartureDays.split(',') : [];
			
			//console.log('returning',oRestriction);
			//console.groupEnd();
			return oRestriction;
		};
		
		var getIntersectionRestriction = function(iFromDate, iToDate)
		{
			var oRestr = oDefRes;
			
			jQuery.each(aRestrictions,function(index, oCurrRes)
			{
				// if given "from" date greater than or equal to "from" date 
				// of current restriction and given "from" date is smaller than 
				// "to" date of current restriction
				// or
				// given "to" date is smaller than or equal to "to" date of 
				// current restriction and given "to" date is greater than 
				// "from" date of current restriction 
				if((iFromDate >= oCurrRes.iFromDate 
					&& iFromDate < oCurrRes.iToDate) 
						|| (iToDate <= oCurrRes.iToDate 
							&& iToDate > oCurrRes.iFromDate))
				{
					// use minstay days of current restriction if it's 
					// greater then minstay days of intersection restriction
					oRestr.iMinStayDays = 
						((oCurrRes.iMinStayDays > oRestr.iMinStayDays)? 
							oCurrRes.iMinStayDays : oRestr.iMinStayDays);
					
					// use maxstay days of current restriction if maxstay days of 
					// intersection restriction is zero and maxstay days of current
					// restriction is greater than zero
					// 
					// also use maxstay days of current restriction if maxstay days
					// of current restriction is smaller than maxstay days of 
					// intersection restriction
					oRestr.iMaxStayDays = 
						((oRestr.iMaxStayDays == 0 && oCurrRes.iMaxStayDays > 0)? 
							oCurrRes.iMaxStayDays : ((oRestr.iMaxStayDays != 0 
								&& oCurrRes.iMaxStayDays < oRestr.iMaxStayDays)? 
									oCurrRes.iMaxStayDays : oRestr.iMaxStayDays));
				}
			});
			
			// if minstay days greater then maxstay days
			if(oRestr.iMinStayDays > oRestr.iMaxStayDays && oRestr.iMaxStayDays != 0)
			{
				// set minstay days to maxstay days
				oRestr.iMinStayDays = oRestr.iMaxStayDays;
			}
			
			// return intersection restriction
			return oRestr;
		};
		
		var restrictFromDate = function(poDate)
		{
			var oRestriction = getRestriction(poDate.getUtcTimestamp());
			var aReturn = (oRestriction.aArrivalDays
				.contains((poDate.getDay()).toString()) == -1)? [false] : [true];
			
			return aReturn;
		};
		
		var restrictToDate = function(poDate)
		{
			return [true];
			var oRestriction = getRestriction(poDate.getUtcTimestamp());
			var aReturn = (oRestriction.aDepartureDays
				.contains((poDate.getDay()).toString()) == -1)? [false] : [true];
			
			return aReturn;
		};
		
		var refreshDates = function()
		{	
			debug.group('refreshDates');
			
			// select current "from" date
			oFromDate = oFrom.datepicker('getDate');
			
			// select current "to" date
			oToDate = oTo.datepicker('getDate');
			
			// destroy old datepicker if existing
			oFrom.datepicker('destroy');
			oTo.datepicker('destroy');

			// delete old date values
			oFrom.attr('value','');
			oTo.attr('value','');
			
			
			// get restriction for "from" date
			//console.log('trying to get restriction for from date:',oFromDate);
			oFromRestriction = getRestriction(oFromDate.getUtcTimestamp());
			
			var iInvalidResCount = 0;
			
			// if we have no arrival days (restriction invalid)
			while(oFromRestriction.aArrivalDays.length < 1)
			{
				if(iInvalidResCount >= 100)
				{
					debug.error('tried over 100x to find a valid '
						+'restriction. exiting now!');
					
					if(typeof poCallback == "function")
					{
						poCallback();
					}
					
					return;
				}
				
				// setting from date to "from" restrictions "to" date +1d
				oFromDate.setByTimestamp(oFromRestriction.iToDate + 86400);
				
				debug.warn('found no arrival days. setting "from" date to '
						+'"to" date +1d of current restriction',oFromDate);
				
				// getting new from restriction
				oFromRestriction = getRestriction(oFromDate.getUtcTimestamp());
				
				iInvalidResCount++;
			}
			
			// check if we have to change the "from" date by "from" restriction
			// if current date + prebooking days > "from" date
			if(oFromRestriction.iPrebookingDays 
				&& (Date.getFlatTime().getUtcTimestamp() 
					+ (oFromRestriction.iPrebookingDays * 86400)) 
						> oFromDate.getUtcTimestamp())
			{
				// set "from" date to current date + prebooking days
				oFromDate.setByTimestamp(Date.getFlatTime().getTimestamp() 
					+ (oFromRestriction.iPrebookingDays * 86400));
				
				debug.log('prebooking days:',oFromRestriction.iPrebookingDays,
					', setting "from" date to',oFromDate);
			}

			// if "from" date is not in allowed arrival days
			if(oFromRestriction.aArrivalDays 
				&& oFromRestriction.aArrivalDays.contains((oFromDate.getDay()).toString()) == -1)
			{	
				debug.log('date not allowed by arrival days:',
					oFromDate,',day: ',(oFromDate.getDay()),
					', allowed days',oFromRestriction.aArrivalDays);
				
				var oCurrDate = oFromDate;
				var iDaysToAdd = 0;
				while(oFromRestriction.aArrivalDays.contains((oCurrDate.getDay()).toString()) == -1)
				{
					//console.info(oFromRestriction.aArrivalDays,oCurrDate.getDay());
					// add one day
					oCurrDate.setByTimestamp(oCurrDate.getTimestamp() + 86400);
					iDaysToAdd++;
					

					if(iDaysToAdd >= 365)
					{
						debug.warn('days to add >= 365. executing '
							+'callback without restriction:'+oFromRestriction);
						
						if(typeof poCallback == "function")
						{
							poCallback();
						}
						
						debug.groupEnd();
						return;
					}
				}
				
				// set "from" date to next possible arrival date!
				oFromDate.setByTimestamp(oCurrDate.getTimestamp());
				
				// set "to" date
				oToDate.setByTimestamp(oToDate.getTimestamp()
					+ (86400 * iDaysToAdd));
			}
			
			// get intersection restriction
			//var oIntRestriction = getIntersectionRestriction(
			//	oFromDate.getUtcTimestamp(), oToDate.getUtcTimestamp());
			
			// for now just use "from" restriction!
			var oIntRestriction = oFromRestriction;
			
			/**
			 * Fixme:
			 * relocate this to a method!
			 */
			// check if we have to change the "to" date by intersection restriction
			// if todate < (fromdate + minstay)
			if((oIntRestriction.iMinStayDays 
				&& (oToDate.getUtcTimestamp() < (oFromDate.getUtcTimestamp() 
					+ (oIntRestriction.iMinStayDays * 86400)))))
			{
				// set "to" date to "from" date + minstay days
				oToDate.setByTimestamp(oFromDate.getTimestamp() 
					+ (oIntRestriction.iMinStayDays * 86400));
			}
			// else if todate > (fromdate + maxstay)
			else if((oIntRestriction.iMaxStayDays 
				&& (oToDate.getUtcTimestamp() > (oFromDate.getUtcTimestamp() 
					+ (oIntRestriction.iMaxStayDays * 86400)))))
			{
				// set "to" date to "from" date + maxstay days
				oToDate.setByTimestamp(oFromDate.getTimestamp() 
					+ (oIntRestriction.iMaxStayDays * 86400));
			}
			/**
			 * Fixme END
			 */
			
			// for now just use from restriction for to!
			oToRestriction = oFromRestriction;
			
			// fetch "to" restriction if current "to" date is greater than 
			// "to" date from "from" restriction. Else also use
			// "from" restriction as "to" restriction
			//oToRestriction = ((oToDate.getUtcTimestamp() > oFromRestriction.iToDate)? 
			//		getRestriction(oToDate.getUtcTimestamp()): oFromRestriction);
			
			
			// calc mindate for from/to datepicker
			
			// set "from" mindate to today + prebooking days
			var oMinDateFrom = new Date();
			oMinDateFrom.setByTimestamp(new Date().getTimestamp() 
				+ (86400 * oFromRestriction.iPrebookingDays));
			
			// set "to" mindate to "from" mindate
			var oMinDateTo = new Date().setByTimestamp(oMinDateFrom.getTimestamp());
			
			/**
			 * Fixme:
			 * relocate this to a method!
			 */
			// check if we have to change the "to" mindate by intersection restriction
			// if todate < (fromdate + minstay)
			if((oIntRestriction.iMinStayDays 
				&& (oMinDateTo.getUtcTimestamp() < (oFromDate.getUtcTimestamp() 
					+ (oIntRestriction.iMinStayDays * 86400)))))
			{
				// set "to" date to "from" mindate + minstay days
				oMinDateTo.setByTimestamp(oFromDate.getTimestamp() 
					+ (oIntRestriction.iMinStayDays * 86400));
			}
			// else if todate > (fromdate + maxstay)
			else if((oIntRestriction.iMaxStayDays 
				&& (oMinDateTo.getUtcTimestamp() > (oFromDate.getUtcTimestamp() 
					+ (oIntRestriction.iMaxStayDays * 86400)))))
			{
				// set "to" mindate to "from" date + maxstay days
				oMinDateTo.setByTimestamp(oFromDate.getTimestamp() 
					+ (oIntRestriction.iMaxStayDays * 86400));
			}
			/**
			 * Fixme END
			 */
			
			//console.log('setting mindate to',oMinDate);
			
			// init datepicker for "from" field
			oFrom.datepicker({
				dateFormat : 'dd.mm.yy',
				yearRange: '0:+1',
				minDate: oMinDateFrom,
				onSelect: refreshDates,
			    beforeShowDay: restrictFromDate
			});
			
			// init datepicker for "to" field
			// restrict "to" datepicker to new mindate
			oTo.datepicker({
				dateFormat : 'dd.mm.yy',
				yearRange: '0:+1',
				minDate: oMinDateTo,
				beforeShowDay: restrictToDate
			});
			
			// if we have a maximum stay restriction
			if(oToRestriction.iMaxStayDays)
			{
				// calculate maxdate
				var oMaxDate = new Date();
				oMaxDate.setByTimestamp(oFromDate.getTimestamp()
					+ (oToRestriction.iMaxStayDays * 86400));
				
				// restrict datepicker to new maxdate
				oTo.datepicker('option',{
				    maxDate: oMaxDate
				});
			}
			else
			{
				oTo.datepicker('option',{
				    maxDate: false
				});
			}
			// set "from" datepicker to new "from" date
			oFrom.datepicker('setDate',oFromDate);
			
			// set "to" datepicker to new "to" date
			oTo.datepicker('setDate',oToDate);
			
			
			if(typeof poCallback == "function")
			{
				poCallback(oFromRestriction);
			}
			
			debug.groupEnd();
		};

		// if all necessary members given
		if(typeof poOpts == 'object'
			&& typeof poOpts.target != 'undefined')
		{
			// get jquery object for from/to element
			var oFrom = $(this);
			var oTo   = $(poOpts.target);

			// if both elements found
			if(oFrom && oTo)
			{
				// destroy old datepicker if existing
				oFrom.datepicker('destroy');
				oTo.datepicker('destroy');

				// delete old date values
				oFrom.attr('value','');
				oTo.attr('value','');

				// set "from" date to today + 1
				oFromDate = new Date();
				oFromDate.setDate(new Date().
					getDate() + 1);

				// init datepicker for "from" field
				oFrom.datepicker({
					dateFormat : 'dd.mm.yy',
					onSelect: refreshDates
				});

				// set "to" date to today + 2
				oToDate = new Date();
				oToDate.setDate(oFromDate.getDate() + 1);

				// init datepicker for "to" field
				oTo.datepicker({
					dateFormat : 'dd.mm.yy'
				});


				// if "from" date given
				if(typeof poOpts.from != 'undefined' && poOpts.from)
				{
					// set initial "from" date by given date strings
					oFrom.datepicker('setDate', poOpts.from);
				}
				else
				{
					// set initial "from" date
					oFrom.datepicker('setDate',oFromDate);
				}

				// if "to" date given
				if(typeof poOpts.to != 'undefined' && poOpts.to)
				{
					// set initial "to" date by given date strings
					oTo.datepicker('setDate', poOpts.to);
				}
				else
				{
					// set initial "to" date
					oTo.datepicker('setDate',oToDate);
				}

				// refresh the datepickers
				refreshDates();
			}
		}
		
		debug.groupEnd();
	}
});
})(jQuery);

Array.prototype.contains = function(pmKey)
{
	return jQuery.inArray(pmKey,this);
};

Date.prototype.getUtcTimestamp = function()
{
    return Math.round(Date.UTC(this.getFullYear(), 
        this.getMonth(),this.getDate(),this.getHours(),
        this.getMinutes(), this.getSeconds()) / 1000);
};

Date.prototype.getTimestamp = function()
{
    return Math.round(this.getTime() / 1000);
};


Date.prototype.setMixedDate = function(pmDate)
{
    if(typeof pmDate != 'undefined' && pmDate)
    {
        if(typeof pmDate == 'number')
        {
            this.setTime(new Date(pmDate * 1000).getTime());
        }
        else if(typeof pmDate == 'string')
        {
           var aMatch = pmDate.match(/([0-9]{2})\.([0-9]{2})\.([0-9]{4})/);
           if(aMatch.length > 0)
           {
               this.setTime(new Date(aMatch[3],aMatch[2]-1,aMatch[1]).getTime());
           }
        }
    }
    
    return this;
};

Date.getFlatTime = function()
{
	var oDate = new Date();
	return new Date(oDate.getFullYear(), 
		oDate.getMonth(),oDate.getDate());
}

Date.prototype.setByTimestamp = Date.prototype.setMixedDate; 
