/*---------------
 * jQuery Findmynearest Plugin by Engage Interactive
 * Copyright (c) 2009 Engage Interactive
 * Author: Neil Charlton
 * Version: 1.0
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * Requires: 
 	1. jQuery v1.3 or later
 	2. Google Maps API
	3. A locations datasource containing id, name, latitude and longtitude
---------------*/


(function($) {

	var Url = {

		// public method for url encoding
		encode : function (string) {
			return escape(this._utf8_encode(string));
		},

		// public method for url decoding
		decode : function (string) {
			return this._utf8_decode(unescape(string));
		},

		// private method for UTF-8 encoding
		_utf8_encode : function (string) {
			string = string.replace(/\r\n/g,"\n");
			var utftext = "";

			for (var n = 0; n < string.length; n++) {

				var c = string.charCodeAt(n);

				if (c < 128) {
					utftext += String.fromCharCode(c);
				}
				else if((c > 127) && (c < 2048)) {
					utftext += String.fromCharCode((c >> 6) | 192);
					utftext += String.fromCharCode((c & 63) | 128);
				}
				else {
					utftext += String.fromCharCode((c >> 12) | 224);
					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
					utftext += String.fromCharCode((c & 63) | 128);
				}

			}

			return utftext;
		},

		// private method for UTF-8 decoding
		_utf8_decode : function (utftext) {
			var string = "";
			var i = 0;
			var c = c1 = c2 = 0;

			while ( i < utftext.length ) {

				c = utftext.charCodeAt(i);

				if (c < 128) {
					string += String.fromCharCode(c);
					i++;
				}
				else if((c > 191) && (c < 224)) {
					c2 = utftext.charCodeAt(i+1);
					string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
					i += 2;
				}
				else {
					c2 = utftext.charCodeAt(i+1);
					c3 = utftext.charCodeAt(i+2);
					string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
					i += 3;
				}

			}

			return string;
		}

	}


	$.fn.findmynearest = function( options ){

		var ourLocs = [];
		var geocoder = "";
		var latlng = null;
		var loglat = "";
		var loglng = "";
		var ipLatLng = [];
		var defaults = {
					dataSource: "http://webservices.engageinteractive.co.uk/proxy.php?brand=strada&action=getLocations&callback=?",
					ipDataSource : "http://webservices.engageinteractive.co.uk/proxy.php?brand=none&action=ipToLatLng&callback=?",
					resultsDiv: "results",
					addressFieldId: "address",	
					ipNearest: true,
					ipAddress : "",
					ipResultDiv : "ipresult",
					searchTotal:5,
					resultsUrl: '/locations/',
					logger : true
					};
					
		var settings = $.extend( {}, defaults, options );
		
	
		queryDataSource = function()
		{
			/* get the locations json from the webservice */
			$.getJSON( settings.dataSource.replace( '&amp;', '&' ), 
				function(data){
				if( !data ) printMessage( "We could not connect, please try again" );
			  	$.each(data.items, function(i,item){
					ourLocs[item.id] = new Array( item.lng, item.lat, item.name, item.id, item.location_id );
			    });
			});
		}
		
		/* Find nearest based on the IP of the sender */
		if( settings.ipNearest )
		{
			if( settings.ipAddress != '' )
			{
					queryDataSource();
					$.getJSON( settings.ipDataSource + "&ip=" + settings.ipAddress , //+  "&ip=84.92.87.9"
						function(data){
						if( !data ) printMessage( "We could not connect, please try again" );
						
					  	$.each(data.items, function(i,item){
							var places = [];
							
							ipLatLng[i] = [ item.lat, item.lng, item.city, item.name, item.code ];
							
								var centerpoint = new GLatLng( item.lat, item.lng )

								$.each( ourLocs, function(i,n){ 
									if( this[2] != undefined )
									{
										var lat = parseFloat( this[1] );
									    var lng = parseFloat( this[0] );
									    var latlng = new GLatLng( lat, lng );
										var name = this[2];
										var id = this[3];
										var id = this[4];
										places.push( { latlng:latlng , name:name, id:id } );
									}
								});
								
								/* Calculate the distances */
								for (i=0; i<places.length; i++) {
					          		places[i].dist = places[i].latlng.distanceFrom( centerpoint );
								}

								places.sort(function (a,b) {return (a.dist - b.dist)});
							
							var iMiles = (parseFloat(places[0].dist / 1609)).toFixed(2);
							if( iMiles < 20 )
							{
								var sOut = $( "#ipresult" ).html();
								sOut = sOut.replace( /@LOCATION/, Url.decode( item.city ) );
								sOut = sOut.replace( /@NAME/, places[0].name );
								sOut = sOut.replace( /@MILES/, iMiles );
								sOut = sOut.replace( /@ID/, places[0].id );
								
								$( "#" + settings.ipResultDiv ).html( sOut ).show();
							}
							else
							{
								$( "#" + settings.ipResultDiv ).html( $( "#ip-no-results" ).text() );
							}
							var places = [];
							
							$( "#" + settings.addressFieldId ).blur();
							$( "#" + settings.addressFieldId ).focus(function(){
								$( "#search-button" ).removeAttr( "disabled" );
							});
					    });
					});
			}
		}
	
		/* check the inputs - we must have a datasource */
		checkInputs = function() { return ( settings.dataSource == '' ) }
	
		getResults = function( id, name, dist )
		{
			var km = (parseFloat(dist / 1000)).toFixed(1) + "km";
			var miles = (parseFloat(dist / 1609)).toFixed(1) + " miles away";
			return '<li><a href=\"' + settings.resultsUrl + '#' + id + '" title="Click for full details about this STRADA restaurant" class="aLocation">' + name + '</a> ' + miles + '</li>';
		}
		
		printMessage = function( sMessage )
		{
			$( "#" + settings.resultsDiv ).append( sMessage ).slideDown( "slow" );
			$( "#search-button" ).removeAttr("disabled");
		}

		return this.each( function() {
			$this = $( this );
			if( checkInputs( ) ) { alert( "You must pass a datasource URL" ); return true; };
			
			/* UI bits and peices */
			$( "#" + settings.addressFieldId ).focus(function(){
				$( this ).attr( "value", "" );
				$( "#" + settings.resultsDiv ).slideUp( "fast" );
			});
			
			
			
			/* on submit - meat of plugin is here */
			$this.submit( function(){
				
				$( "#search-button" ).attr( "disabled", "disabled" );
				//Loading
				$('.locations_loading').show();
				
				$( "#" + settings.ipResultDiv ).hide();
				$( "#" + settings.resultsDiv ).empty().hide( "fast" );
				
				if( $( "#" + settings.addressFieldId ).attr( "value" ) == '' ){
					printMessage( settings.sEmptyMessage );
					return false;
				}
				
				// special case for london  if( searchTerm.toLowerCase() == 'selfridges' ) searchTerm = "Selfridges, London";
				var sSt = $( "#" + settings.addressFieldId ).attr( "value" );
				if( sSt.toLowerCase() == 'london' ){
					var sStr = "<p>We have many  restaurants in London, please try refining your search. </p><p class=\"bottom\">\
					 			You can search a London suburb e.g Islington, London or a landmark or postcode. <br /></p>";
					printMessage( sStr );
					return false;
				}
				queryDataSource();
				
				var searchTerm = $( "#" + settings.addressFieldId ).val();
				
					var places = [];
					if (GBrowserIsCompatible()) {
				        geocoder = new GClientGeocoder();
				    }

					geocoder.getLatLng( searchTerm + ",uk", function( point ){ 
						if( !point ){
							printMessage( settings.sEmptyMessage );
						}
						else
						{
								var centerpoint = new GLatLng( point.lat(), point.lng() )
								
								loglat = point.lat();
								loglng = point.lng();
								
								$.each( ourLocs, function(i,n){ 
									if( this[2] != undefined )
									{
										var lat = parseFloat( this[1] );
									    var lng = parseFloat( this[0] );
									    var latlng = new GLatLng( lat, lng );
										var name = this[2];
										var id = this[4];
										places.push( { latlng:latlng , name:name, id:id } );
									
									}
								});
								/* Calculate the distances */
								for (i=0; i<places.length; i++) {
					          		places[i].dist = places[i].latlng.distanceFrom( centerpoint );
								}

								places.sort(function (a,b) {return (a.dist - b.dist)});
							
								var iCnt = 0;
								var sOut = '<ul>';
								for ( var key in places ) {
									if( iCnt < settings.searchTotal ){
										sOut += getResults( places[key].id, places[key].name, places[key].dist );
									}
									iCnt++;
								}
								
								/* We have some results so animate the results div */
								if( places.length > 1 ) $( "#" + settings.resultsDiv ).append( sOut + "</ul>" ).slideDown( "slow", function(){
									if( settings.logger ){
										$.post( "/locations/logfindmynearestsearch", { brand: "strada", term: searchTerm, lng: loglng, lat: loglat }, function(data){
											// posted callback
										} );
									}
								});
								
								
								
								$( "#" + settings.addressFieldId ).blur();
								$( "#" + settings.addressFieldId ).focus(function(){
									$( "#search-button" ).removeAttr( "disabled" );
								});

						}
						
						$('.locations_loading').hide();

					});
				return false;
			});
		
		});

	};

})(jQuery);