var bindCalendars = function( node, meta_host, currency, language, checkin, checkout )
{
	if( checkin == null || isNaN( checkin ) )
		chekin = new Date().getTime() + ( 15 * 86400000 ) ;
	
	if( checkout == null || isNaN( checkout ) )
		chekout = checkin + 86400000;
	
	// Implements the trigger on the calendar button
	$( node ).find( "img.date.trigger" ).bind( "click", function( e )
	{
		$( this ).parent().find( "input" ).datepicker( "show" );
	} );
	
	// Implements the datepicker system on the check-in field
	$( node ).find( "input.checkin.date" ).datepicker( {
		altField: "form input[name='checkin']",
		altFormat: "@",
		defaultDate: checkin,
		minDate: 1,
		onSelect: function( e )
		{
			var checkin = $( this );
			var checkout = $( this ).parents( "form" ).find( "input.checkout.date" );
			
			if( checkin.datepicker( "getDate" ).getTime() + 86400000 > checkout.datepicker( "getDate" ).getTime() )
			{
				checkout.datepicker( "setDate", new Date( $( this ).datepicker( "getDate" ).getTime() + 86400000 ) );
			}
			
			$( "input.checkout.date" ).datepicker( "option", "minDate", new Date( $( this ).datepicker( "getDate" ).getTime() + 86400000 ) );
			
			// Date has changed, recheck availabilities
			var avail = $( this ).parents( ".availability.block" );
			checkin  = avail.find( "input.checkin.date" ).val();
			checkout = avail.find( "input.checkout.date" ).val();
			
			// Update all forms and recheck all availabilities
			$( "form" ).each( function( e )
			{
				$( this ).find( "input.checkin.date" ).val( checkin );
				$( this ).find( "input.checkout.date" ).val( checkout );
			});
			
			$( ".availability.block.opened" ).each( function( e )
			{
				checkAvailabilities( e, $( this ), meta_host, currency, language );
			});
		}
	} );
	
	// Implements the datepicker system on the check-out field
	$( node ).find( "input.checkout.date" ).datepicker( {
		altField: "form input[name='checkout']",
		altFormat: "@",
		defaultDate: checkout,
		minDate: new Date( checkin * 1000 + 86400000 ),
		onSelect: function( e )
		{
			// Date has changed, recheck availabilities
			var avail = $( this ).parents( ".availability.block" );
			var checkout = avail.find( "input.checkout.date" ).val();
			
			// Update all forms and recheck all availabilities
			$( "form" ).each( function( e )
			{
				$( this ).find( "input.checkout.date" ).val( checkout );
			});
			
			$( ".availability.block.opened" ).each( function( e )
			{
				checkAvailabilities( e, $( this ), meta_host, currency, language );
			});
		}
	} );
	
	node.find( "form input.submit" ).bind( "click", function( e )
	{
		if( !$( this ).parents( "li" ).hasClass( "unavailable" ) )
		{
			$( this ).parents( "form" ).find( "input[name='merchant']" ).val( $( this ).attr( "code" ) );
			e.stopPropagation();
		}
		else
		{
			e.stopPropagation();
			e.preventDefault();
		}
	} );
	
	// Submit the form
	node.find ( ".content ul.merchants li" ).bind( "click", function( e )
	{
		$( this ).find( "input[type='submit']:eq(0)" ).trigger( "click" );
		
		e.stopPropagation();
	} );
	
	// Close handler
	node.find( ".content span.close" ).bind( "click", function( e )
	{
		// Helpers DOM accelerators
		var avail    = $( this ).parents( ".availability" );
		var hotel    = avail.prev( ".hotel" );
		
		// Close opened items
		hotel.find( ".pricing" ).show( "fast" );
		avail.find( ".content" ).slideUp( "slow" );
		hotel.removeClass( "opened" );
		avail.removeClass( "opened" );
	} );
	
	// We must recheck availabilities upon field change
	node.find( ".content .third input" ).bind( "click", function( e )
	{
		var avail = $( this ).parents( ".availability.block" );
		var guests   = parseInt( avail.find( "input[name='guests']:checked" ).val(), 10 );
		
		// Update all forms and recheck all availabilities
		$( "form" ).each( function( e )
		{
			$( this ).find( "input[name='guests'][value='" + guests + "'] ").attr('checked','true');
		});
		
		$( "div.hotel.opened" ).next( ".availability.block" ).each( function( e )
		{
			checkAvailabilities( e, $( this ), meta_host, currency, language );
		});
	} );
};

// This is the check availability function which queries the meta engine for availabilities
var checkAvailabilities = function( e, avail, meta_host, currency, language )
{
	// No hotel provided, abort
	if( avail == null || avail.length == 0 )
		return;
	
	// Remove the timer if a search is still running
	if( avail.attr( "timer" ) != null )
	{
		clearTimeout( avail.attr( "timer" ) );
	}
	
	checkAvailabilities.timer = null;
	
	// Retrieve the query parameters from input fields
	var code     = avail.find( "input[name='hotel_code']" ).val();
	var engines  = avail.find( "input[name='set']" ).val();
	var checkin  = new Date( parseInt( avail.find( "input[name='checkin']" ).val(), 10 ) );
	var checkout = new Date( parseInt( avail.find( "input[name='checkout']" ).val(), 10 ) );
	var guests   = parseInt( avail.find( "input[name='guests']:checked" ).val(), 10 );
	
	// No guests provided, abort the search
	if( guests == 0 || isNaN( guests ) )
		return;
	
	// Notify the user that we started the search
	var items = avail.find( "ul.merchants li" );
	
	items.addClass( "searching" );
	
	items.removeClass( "unknown" );
	items.removeClass( "available" );
	items.removeClass( "unavailable" );
	
	items.find( "span.init,input.init" ).removeClass( "hidden" );
	items.find( "span:not(.init),input:not(.init)" ).addClass( "hidden" );
	
	items.unbind( "mouseover" );
	items.unbind( "mouseout" );
	
	// Ajax error handling
	$.ajaxSetup(
	{
		cache: false,
		error: function( XMLHttpRequest, textStatus, errorThrown )
		{
			var items = avail.find( "ul.merchants li.searching" );
			
			items.removeClass( "searching" );
			items.addClass( "unknown" );
			
			items.find( "span.unknown,input.unknown" ).removeClass( "hidden" );
			items.find( "span:not(.unknown),input:not(.unknown)" ).addClass( "hidden" );
			
			// Hover and click function on li elements
			items.bind( "mouseover", function( e ) { $( this ).addClass( "hover" ); } );
			items.bind( "mouseout", function( e ) { $( this ).removeClass( "hover" ); } );
		}
	} );
	
	// Generate the query parameters
	var params = "";
	
	params    += "?engine_set=" + engines;
	params    += "&arrival_date=" + $.datepicker.formatDate( "yy-mm-dd", checkin );
	params    += "&depart_date=" + $.datepicker.formatDate( "yy-mm-dd", checkout );
	params    += "&room_capacity=" + guests;
	params    += "&lang=" + language ;
	params    += "&currency=" + currency ;
	params    += "&hotel_code=" + code.replace( "@", "" );
	params    += "&search_timeout=25";
	
	if( e.shiftKey == true )
		params    += "&bypass_cache";
	
	// Initiate the start search query
	$.getJSON( meta_host + "svc/meta/hotel/availability" + params, function( e )
	{
		var event = null;
		
		// Loop each message in search of the search handle event
		for( var i = 0; i < e.length; i++ )
		{
			// Process the search handle
			if( e[ i ][ "event" ] && e[ i ][ "event" ] == "search-handle" )
			{
				event = e[ i ];
			}
			
			// Process unavailable engines
			if( e[ i ][ "event" ] && e[ i ][ "event" ] == "unavailable-engines" )
			{
				for( var j = 0; j < e[ i ][ "list" ].length; j++ )
				{
					var item = avail.find( "ul.merchants li[code='" + e[ i ][ "list" ][ j ] + "']" );
					
					item.removeClass( "searching" );
					item.addClass( "unknown" );
					
					item.find( "span.unknown,input.unknown" ).removeClass( "hidden" );
					item.find( "span:not(.unknown),input:not(.unknown)" ).addClass( "hidden" );
					
					// Hover and click function on li elements
					item.bind( "mouseover", function( e ) { $( this ).addClass( "hover" ); } );
					item.bind( "mouseout", function( e ) { $( this ).removeClass( "hover" ); } );
				}
			}
		}
		
		// If we got a search-handle result, we can show results from the search id
		if( event != null )
		{
			// Initialize the running time
			$( avail ).attr( "running", 0 );
			
			var update = function()
			{
				$.getJSON( meta_host + "svc/meta/hotel/" + event[ "meta-id" ] + "/results?start=0&length=100&search_id=" + event[ "search-id" ], function( e )
				{
					// Store the total running time
					$( avail ).attr( "running", parseInt( $( avail ).attr( "running" ), 10 ) + 2000  );
					
					var finished = false;
					
					// Loop for each message, and try to find a result
					for( var i = 0; i < e.length; i++ )
					{
						// The search has been completed, all "searching" status should be moved to "unknown"
						if( e[ i ][ "event" ] && e[ i ][ "event" ] == "end-of-results" )
						{
							finished = true;
						}
						
						// We found a result in the message
						if( e[ i ][ "event" ] && e[ i ][ "event" ] == "result" )
						{
							// The result was OK, but we couldn't fetch the price)
							if( e[ i ][ "currency" ] == "AAA" )
							{
								var item = avail.find( "ul.merchants li[code='" + e[ i ][ "supplier" ] + "']" );
								
								item.removeClass( "searching" );
								item.addClass( "available" );
								
								item.find( "span.available,input.unknown" ).removeClass( "hidden" );
								item.find( "span:not(.available),input:not(.unknown)" ).addClass( "hidden" );
								
								item.attr( "price", "unknown" );
								
								// Hover and click function on li elements
								item.bind( "mouseover", function( e ) { $( this ).addClass( "hover" ); } );
								item.bind( "mouseout", function( e ) { $( this ).removeClass( "hover" ); } );
							}
							
							// The result was OK, the currency was OK, change the merchant's status
							else if( e[ i ][ "status" ] == "OK" && e[ i ][ "currency" ] == currency )
							{
								var item = avail.find( "ul.merchants li[code='" + e[ i ][ "supplier" ] + "']" );
								
								item.removeClass( "searching" );
								item.addClass( "available" );
								
								item.find( "input.available" ).val( parseInt( e[ i ][ "price" ], 10 ) + " " + e[ i ][ "currency" ] );
								item.find( "span.available,input.available" ).removeClass( "hidden" );
								item.find( "span:not(.available),input:not(.available)" ).addClass( "hidden" );
								
								// Set the price to an attribute for debugging
								item.attr( "price", parseInt( e[ i ][ "price" ], 10 ) + " " + e[ i ][ "currency" ] );
								
								// Hover and click function on li elements
								item.bind( "mouseover", function( e ) { $( this ).addClass( "hover" ); } );
								item.bind( "mouseout", function( e ) { $( this ).removeClass( "hover" ); } );
							}
							
							// The result was not available, change the merchant's status
							else if( e[ i ][ "status" ] == "N/A" )
							{
								var item = avail.find( "ul.merchants li[code='" + e[ i ][ "supplier" ] + "']" );
								
								item.removeClass( "searching" );
								item.addClass( "unavailable" );
								
								item.find( "span.unavailable,input.unavailable" ).removeClass( "hidden" );
								item.find( "span:not(.unavailable),input:not(.unavailable)" ).addClass( "hidden" );
								
								item.unbind( "mouseover" );
								item.unbind( "mouseout" );
							}
							
							// Something bad happened
							else
							{
								var item = avail.find( "ul.merchants li[code='" + e[ i ][ "supplier" ] + "']" );
								
								item.removeClass( "searching" );
								item.addClass( "unknown" );
								
								item.find( "span.error,input.error" ).removeClass( "hidden" );
								item.find( "span:not(.error),input:not(.error)" ).addClass( "hidden" );
								
								// Hover and click function on li elements
								item.bind( "mouseover", function( e ) { $( this ).addClass( "hover" ); } );
								item.bind( "mouseout", function( e ) { $( this ).removeClass( "hover" ); } );
							}
						}
					}
					
					if( finished || parseInt( $( avail ).attr( "running" ), 10 ) > 20000 )
					{
						var items = avail.find( "ul.merchants li.searching" );
						
						items.removeClass( "searching" );
						items.addClass( "unknown" );
						
						items.find( "span.unknown,input.unknown" ).removeClass( "hidden" );
						items.find( "span:not(.unknown),input:not(.unknown)" ).addClass( "hidden" );
						
						// Hover and click function on li elements
						items.bind( "mouseover", function( e ) { $( this ).addClass( "hover" ); } );
						items.bind( "mouseout", function( e ) { $( this ).removeClass( "hover" ); } );
					}
					else
					{
						avail.attr( "timer", setTimeout( update, 2000 ) );
					}
				} );
			};
			
			update();
		}
		
		// Something bad happened, set all status to unknown
		else
		{
			avail.find( "ul.merchants li" ).removeClass( "searching" );
			avail.find( "ul.merchants li" ).addClass( "unknown" );
			
			avail.find( "ul.merchants li span.error,ul.merchants li input.error" ).removeClass( "hidden" );
			avail.find( "ul.merchants li span:not(.error),ul.merchants li input:not(.error)" ).addClass( "hidden" );
			
			// Hover and click function on li elements
			avail.find( "ul.merchants li span.error,ul.merchants li input.error" ).bind( "mouseover", function( e ) { $( this ).addClass( "hover" ); } );
			avail.find( "ul.merchants li span.error,ul.merchants li input.error" ).bind( "mouseout", function( e ) { $( this ).removeClass( "hover" ); } );
		}
	} );
};
