/*
	jQuery.routeplanner.js
	1.0
		First Release
	1.1
		Adds printing, more events
	1.2
		Bugfixes
	
	Copyright 2010 Tim Broddin - tim@brodd.in - http://tim.brodd.in

*/
(function($){
	window._googleMapsLoading = false;
	var directionsService;
	var directionsDisplay;
	
	var methods = {
		init: function(options) {
			var ctx = this;
			var map = false;
			var geocoder = false;
			var settings = {
				loadApi: true,             // wheter to load the google maps api
				apiUrl: 'http://maps.google.com/maps/api/js',
				region: 'de',              // which region you are in. Maps mostly to the domain suffix of your country with exceptions of .uk (gb) and .com (usa)
				language: 'de',           // language, expects a valid language code: http://spreadsheets.google.com/pub?key=p9pdwsai2hDMsLkXsoM05KQ&gid=1
				mapType: 'roadmap',         // the type of map (roadmap, satellite, hybrid or terrain 
				zoom: 13,                    // amount of zoom
				units: 'metric',
				geolocation: {
				  enabled: true,
				  minAccuracy: 166000,
				  timeout: 10
				},
				homeLocation:              // expects either an address property, or lat and lng properties
					{'address': 'Ferienhotel Pass Thurn, Familie Schöppl-Obermoser'}, 			
				cacheResults: true, 		// if enabled will use the LocalStorage (if available) to store Geocode results
				showInputFields: true,    // if disabled the input fields to type your address will be disabled
				labels: {                   // default labels for text
					inputLabel: 'Please enter your address to calculate the route to us:',
					submitLabel: 'Get directions',
					placeholderLabel: 'Enter your address here',
					customMarker: false,    // your home address is shown by default - use this to overrule that
					geolocationSearching: 'Trying to determine your current location',
					geolocationNotFound: 'Unable to determine your current location. Please enter an address.',
					geolocationFound: 'The route shown is based on a guess of your current position. You can enter another address if our guess was wrong.',
					printLabel: 'Print directions',
					printHomeLocation: 'Directions to'
				},
				printButton: true,
				directions: {
				  draggable:  true,        // allows users to drag the directions to select alternative routes
				  travelMode: 'driving'    // driving, walking or bicycling
				}
			};
			if (options) { 
				$.extend(true, settings, options);
			} 
			
			if(settings.loadApi) {
				if(!window._googleMapsLoading) {
					window._googleMapsLoading = true;
					// console.log('loading maps')
					loadGoogleMapsApi((settings.geolocation.enabled) ? 'true' : 'false');
				} else {
					// console.log('already loading maps');
				}
			} else {
				$(window).trigger('mapsApiLoaded.routeplanner');
			}			
	
			$(window).bind('mapsApiLoaded.routePlanner', function() { 
				jctx = $(ctx);
				// bind settings to context
				jctx.data('settings', settings);
				// continue setup with loading home location
				if(settings.showInputFields) {
					jctx
						.append($('<div class="routeplanner-wrapper" />')
							.append(
								$('<div class="routeplanner-direction-form" />')
									.append($('<p>').html(settings.labels.inputLabel)
										.append('<br /><input type="text" class="routeplanner-to" size="50" data-placeholder="' + settings.labels.placeholderLabel + '" ' + 
								 		'value="' + settings.labels.placeholderLabel + '" />')
										.append('<p class="routeplanner-geolocation">&nbsp;</p>')
										.append('<input class="routeplanner-button" type="button" value="' + settings.labels.submitLabel + '" />')
									)
								)
						).append($('<div class="routeplanner-directions" />')
					);
					if(settings.printButton) {
						$('.routeplanner-direction-form > p', jctx).append('<button class="routeplanner-print" type="button">' + settings.labels.printLabel + '</button>');
					}					
					width = $('.routeplanner-wrapper', jctx).width();
					height = $(jctx).height() - $('.routeplanner-direction-form').height();
				} else {
					jctx.append($('<div class="routeplanner-wrapper" />')).append($('<div class="routeplanner-directions" />'));
					width =  jctx.width();
					height = jctx.height();
				}
				
				$('.routeplanner-wrapper', jctx).prepend($('<div class="routeplanner-map" />').css({width: width, height: height}));
				$('.routeplanner-geolocation', ctx).hide();
				jctx.css('height', 'auto');
				
				// bind google maps objects
				directionsService = new google.maps.DirectionsService();
				directionsDisplay = new google.maps.DirectionsRenderer({draggable: settings.directions.draggable});
				
				if(settings.homeLocation) {
					loadHomeLocation();
				} else {
					alert('You must at least set a home location');
				}
			});
			
			$('.routeplanner-to', ctx).live('click', function() { 
				if($(this).val() == $(this).attr('data-placeholder')) {
					$(this).val('');
				}
			});
			
			$('.routeplanner-to', ctx).live('keyup', function(event) {
				if (event.keyCode == '13') {
					$('.routeplanner-direction-form input[type=button]', ctx).trigger('click');
				}
			});
			
			$('.routeplanner-print', ctx).live('click', function() { 
				$(ctx).trigger('print.routePlanner');
			});
			
			$('.routeplanner-button', ctx).live('click', function() {
				methods.geocode($('.routeplanner-to', ctx).val(), function (result) {
					$(ctx).trigger('targetFound.routePlanner', [result]);
					}, settings);
			});
			
			$(ctx).bind('targetFound.routePlanner', function(event, latLng) {
				methods.loadDirection(latLng, ctx, settings);		 
			});
			
			$(ctx).bind('locationFound.routePlanner', function(event, latLng) { 
				var options = {
					zoom: settings.zoom,
					mapTypeId: google.maps.MapTypeId[settings.mapType.toUpperCase()],
					center: latLng
				};
  				map = new google.maps.Map($('.routeplanner-map', ctx)[0], options);
  				$(ctx).data('map', map);
  				if(settings.homeLocation.address) {
  					var marker = new google.maps.Marker({
     			 		position: latLng, 
      					map: map, 
      					title: (settings.labels.customMarker.length) ? settings.labels.customMarker : settings.homeLocation.address
  					});
  				}
  				settings.homeLocation.latLng = latLng;	
				// if geolocation is on get location
				if (settings.geolocation.enabled && navigator.geolocation) {
					$('.routeplanner-geolocation').show().html(settings.labels.geolocationSearching);
  					navigator.geolocation.getCurrentPosition(
  						function(result) { 
	  						// reverse geocode
	  						if(result.coords && result.coords.accuracy <= settings.geolocation.minAccuracy) {
	  							$('.routeplanner-geolocation').html(settings.labels.geolocationFound);
	  							methods.reverseGeocode(result.coords.latitude, result.coords.longitude, function(address) { 
	  								$('.routeplanner-to', ctx).val(address).attr('data-placeholder', address);
	  								latLng = new google.maps.LatLng(result.coords.latitude, result.coords.longitude);
	  								$(ctx).trigger('targetFound.routePlanner', [latLng]);
	  							
	  							}, settings);	
	  						} else {
	  							$('.routeplanner-geolocation').html(settings.labels.geolocationNotFound);
	  						}
	  						$(ctx).trigger('ready.routePlanner');
  						}, 
  						function(e) {
  							$('.routeplanner-geolocation').html(settings.labels.geolocationNotFound);
  							$(ctx).trigger('ready.routePlanner');
  						}, 
  						{timeout: settings.geolocation.timeout*1000}
  					);
				} else {
					$(ctx).trigger('ready.routePlanner');
				} 				
  				
			});
			
			$(ctx).bind('print.routePlanner', function() {
				// destroy previous iframes
				$('iframe', ctx).remove();
				// add new iframe
				$(this).append('<iframe style="width:0px;height:0px;"></iframe>');
				// search for it
				var iframe = $('iframe', ctx);
                // search for it and hide it
				//var iframe = $('iframe', ctx).hide();
				var iframeBody =  (iframe[0].contentWindow || iframe[0].contentDocument);
				if(iframeBody.document) iframeBody = iframeBody.document;
				iframeBody.write('<html><head><title>' + settings.labels.printHomeLocation + ' ' + settings.homeLocation.address +'</title></head><body onload="this.focus(); this.print();">' + $('.routeplanner-directions').html() + '</body></html>');
				iframeBody.close();	
			
			});
	
			function loadGoogleMapsApi(sensor) {
				if(settings.language != 'auto') {
					var languageString = '&language=' + settings.language;
				} else {
					var languageString = '';
				}
				$('body').append($('<script />').attr('src', settings.apiUrl + '?sensor=' + sensor + languageString + '&callback=googleMapsApiLoaded'));
			}
			
			function loadHomeLocation() {
				if(settings.homeLocation.lat && settings.homeLocation.lng) {
					latLng = new google.maps.LatLng(settings.homeLocation.lat, settings.homeLocation.lng);
					$(ctx).trigger('locationFound.routePlanner', [latLng]);
				} else {
					methods.geocode(settings.homeLocation.address, function(result) { $(ctx).trigger('locationFound.routePlanner', [result]); }, settings);
				}				
			}
		},
		loadDirection: function(latLng, ctx, settings) {
			$(ctx).trigger('directionsLoading.routeplanner', [latLng]);
			$('.routeplanner-directions', ctx).html('');
			directionsDisplay.setMap($(ctx).data('map'));
  			directionsDisplay.setPanel($('.routeplanner-directions', ctx)[0]);
  			$('.routeplanner-print', ctx).hide();
  			
			var request = {
				destination: settings.homeLocation.latLng, 
				origin: latLng,
				optimizeWaypoints: true,
				unitSystem: google.maps.DirectionsUnitSystem[settings.units.toUpperCase()],
				travelMode: google.maps.DirectionsTravelMode[settings.directions.travelMode.toUpperCase()]
			};
			directionsService.route(request, function(result, status) {
				if (status == google.maps.DirectionsStatus.OK) {
					directionsDisplay.setDirections(result);
					$('.routeplanner-print', ctx).show();
					$(ctx).trigger('directionsLoaded.routePlanner');
				} else {
					alert('Sorry. No results. Please check your address. ' + latLng);
				}
			});	
				
		},
		geocode: function(address, callback, settings) {
			function getGeocodeCache(address) {
				if(typeof JSON !== 'undefined' && settings.cacheResults) {
					try {
    					if(typeof window.localStorage !== "undefined") {
    						return JSON.parse(localStorage.getItem(address));
    					} else {
    						return false;
    					}
 					 } catch(e){
    					return false;
  					}
				} else {
					return false;
				}
			}
			
			function setGeocodeCache(address, latLng) {
				if(typeof JSON !== 'undefined' && settings.cacheResults) {
					try {
    					if(typeof window.localStorage !== "undefined") {
    						return localStorage.setItem(address, JSON.stringify(latLng));
    					} else {
    						return false;
    					}
 					 } catch(e){
    					return false;
  					}  					
				} else {
					return false;
				}			
			}
			
			geocoder = new google.maps.Geocoder();
			// geocode
			cache = getGeocodeCache(address);	
			if(!cache) {
				geocoder.geocode( { 'address': address}, function(results, status) {
					if (status == google.maps.GeocoderStatus.OK) {
						setGeocodeCache(address, {lat: results[0].geometry.location.b, lng: results[0].geometry.location.c});
						callback(results[0].geometry.location);						
					} else {
						alert("Geocode was not successful for the following reason: " + status);
					}
				});			
			} else {
				latLng = new google.maps.LatLng(cache.lat, cache.lng);
				callback(latLng);
			}
		},
		reverseGeocode: function(lat, lng, callback, settings) {			
			function getReverseGeocodeCache(lat, lng) {
				if(typeof JSON !== 'undefined' && settings.cacheResults) {
					try {
    					if(typeof window.localStorage !== "undefined") {
    						return JSON.parse(localStorage.getItem(JSON.stringify([lat, lng])));
    					} else {
    						return false;
    					}
 					 } catch(e){
    					return false;
  					}	
				} else {
					return false;
				}
			}
			
			function setReverseGeocodeCache(lat, lng, address) {
				if(typeof JSON !== 'undefined' && settings.cacheResults) {
					try {
    					if(typeof window.localStorage !== "undefined") {
    						return localStorage.setItem(JSON.stringify([lat, lng]), JSON.stringify(address));
    					} else {
    						return false;
    					}
 					 } catch(e){
    					return false;
  					}  					
				} else {
					return false;
				}			
			}
			
			geocoder = new google.maps.Geocoder();
			// geocode
			cache = getReverseGeocodeCache(lat, lng);	
			if(!cache) {
				var latlng = new google.maps.LatLng(lat, lng);
				geocoder.geocode( { latLng: latlng}, function(results, status) {
					if (status == google.maps.GeocoderStatus.OK) {
						setReverseGeocodeCache(lat, lng, results[1].formatted_address);
						callback(results[1].formatted_address);						
					} else {
						alert("Geocode was not successful for the following reason: " + status);
					}
				});			
			} else {
				callback(cache);
			}		
		},
		navigateTo: function(address, settings) {
			var ctx = this;
			methods.geocode(address, function(latLng) { methods.loadDirection(latLng, ctx, $(ctx).data('settings')); }, $(ctx).data('settings'));
		},
		print: function() {
			var ctx = this;
			$(ctx).trigger('print.routePlanner');		
		}
	};

	$.fn.routePlanner = function(method) {
		argv = arguments;
		return this.each(function() {
			if (methods[method] ) {
      			return methods[ method ].apply( this, Array.prototype.slice.call(argv, 1));
    		} else if ( typeof method === 'object' || ! method ) {
      			options = [method];
      			
      			return methods.init.apply(this, options);
    		}	
		}); 
	};

})( jQuery );

// small helper function
function googleMapsApiLoaded() {
	$(window).trigger('mapsApiLoaded.routePlanner');
}
