var LongLatGUI = {
		
	input:null,
	hiddens:null,
	delay:null,

	shell:null,
	geocoder:null,
	
	keyDown_bfx:null,
	keyUp_bfx:null,
	onBlur_bfx:null,
	onFocus_bfx:null,
	
	prep_suggestion:false,
	suggestions:null,
	
	item_class:'suggestion',
	selected_class:'selected',
	
	init:function(input, hiddens, delay)
	{
		
		this.input = input;
		this.input.setAttribute('autocomplete','off');
		
		if(this.geocoder == null)
			this.geocoder = new GClientGeocoder();
		
		this.hiddens = hiddens;
		
		this.delay = delay || 250;
		
		this.keyDown_bfx = this.onKeyDown.bindAsEventListener(this);
		this.keyUp_bfx = this.onKeyUp.bindAsEventListener(this);
		this.onBlur_bfx = this.onBlur.bindAsEventListener(this);
		this.onFocus_bfx = this.onFocus.bindAsEventListener(this);
		
		Event.observe(this.input, 'focus', this.onFocus_bfx);
	},
	
	onBlur:function(event)
	{
		setTimeout(this.select.bind(this), this.delay);
	},
	
	onFocus:function(event)
	{
		this.build();
		// bound event listeners
		Event.observe(this.input, 'keydown', this.keyDown_bfx);
		Event.observe(this.input, 'keyup', this.keyUp_bfx);
		Event.observe(this.input, 'blur', this.onBlur_bfx);
	},
	
	onKeyUp:function(event)
	{
		if (this.prep_suggestion)
		{
			this.geocoder.getLocations(this.input.value, this.suggest.bind(this));
			this.prep_suggestion = false;
		}
		
		return;
	},
	
	onKeyDown:function(event)
	{
		if(!this.shell)
			return;

		var selected = this.shell.down('.'+this.selected_class);
		
		switch (event.keyCode) {
		
		case Event.KEY_ESC:
			return;
			
		case Event.KEY_UP:
			var prev = selected.previous(); 
			if(prev)
			{
				prev.addClassName(this.selected_class);
				selected.removeClassName(this.selected_class);
			}
			return;
		
		case Event.KEY_DOWN:
			var next = selected.next(); 
			if(next)
			{
				next.addClassName(this.selected_class);
				selected.removeClassName(this.selected_class);
			}
			return;			
		case Event.KEY_TAB:
			this.select();
			this.destroy();
			return;
	    case Event.KEY_RETURN:
	    	Event.stop(event);
			this.select();
			return;
		default:
			this.prep_suggestion = true;
			return;
		}		
	},
	
	select:function(evt)
	{
		if(!this.shell)
			return;
		
		var selected = this.shell.down('.'+this.selected_class);
		var i = (selected) ? selected.id.replace(this.item_class,'') : 0;
		
		var location = this.suggestions[i];
		
		//update the address field
		this.input.value = this.address(location);
		// create the keys and set the form
		var keys = new Hash(this.hiddens);
		var form = this.input.form;
		
		// update the hidden fields
		keys.each(function(pair){
			form.elements[pair.value].value = "";
			form.elements[pair.value].value = this[pair.key](location);
		}.bind(this));
		
		// all done
		this.hide();
		
		// fire event
        this.input.fire('location:selected', {
            input_id: this.input.getAttribute('id'),
            location: location
        });
	},
	
	suggest:function(response)
	{
		this.shell.childElements().each(function(child){child.remove()});
		this.position();

		if (!response || response.Status.code != 200) {
			this.shell.hide();
	    } else {
	    	
	    	this.shell.show();
	    	
	        var places = response.Placemark;
	        this.suggestions = new Array();
	        
	        places.each(function(place, i)
	        {
	        	if(!place.AddressDetails.Country)
	        		return;
	        	
	        	this.suggestions[i] = place;
	        	
	        	var suggestion = MyBuilder.make('p',{id:this.item_class+i},this.item_class,place.address);
	        	this.shell.appendChild(suggestion);
	        	
	        	// mouseout of suggestion
	        	Event.observe($(suggestion),"mouseover",function(evt){
	        		
	        		var selected = this.shell.down('.'+this.selected_class);
	        		if(selected)
	        			selected.removeClassName(this.selected_class);
	        		
	        		var e = Event.element(evt);
	        		e.addClassName(this.selected_class);
	        		
	        	}.bind(this));
	        	
	        	// mouseover suggestions
	        	Event.observe($(suggestion),"mouseout",function(evt){
	        		
	        		var e = Event.element(evt);
	        		e.removeClassName(this.selected_class);
	        		
	        	}.bind(this));

	        	// suggestion is clicked
	        	Event.observe($(suggestion),"click",this.select.bind(this));

	        	
	        }.bind(this));
	        
	        if(this.shell.down())
	        	this.shell.down().addClassName(this.selected_class);
	        else
	        	this.shell.hide();
	        
	        
	    }
	},
	
	hide:function()
	{
		this.shell.hide();
	},
	
	destroy:function()
	{
		Event.stopObserving(this.input, 'keydown', this.keyDown_bfx);
		Event.stopObserving(this.input, 'keyup', this.keyUp_bfx);
		Event.stopObserving(this.input, 'blur', this.onBlur_bfx);
	    
	    if(this.shell)
	    	this.shell.remove();
	    this.shell = null;
	},
	
	build:function()
	{
		
		if(this.shell)
			return;
		
		this.shell = MyBuilder.make('div',{id:this.input.id+'_complete'},'autocomplete',null);
		// append to the body tag
		var body = document.getElementsByTagName("body").item(0);
		body.appendChild($(this.shell));
		
		Position.absolutize($(this.shell));
		this.shell = $(this.shell);
		var dim = this.input.getDimensions();
		$(this.shell).setStyle({zIndex:9999,height:'auto',width:(dim.width-2)+'px',display:'none'});
		
		this.position();
	},
	
	position:function()
	{
		// position the shell
		var pos = Position.cumulativeOffset(this.input);
		var dim = this.input.getDimensions();
		var left = pos[0];
		var top	= pos[1] + dim.height;
		$(this.shell).setStyle({left:left+'px',top:top+'px'});
	},
	
	country_code:function(location)
	{
		return location.AddressDetails.Country.CountryNameCode;
	},
	
	country:function(location)
	{
		return location.AddressDetails.Country.CountryName;
	},
	
	address:function(location)
	{
		return location.address;
	},
	
	display_address:function(location)
	{
		// city
		city = this.city(location);
		region = this.region(location);
		country = this.country(location);
		accuracy = this.accuracy(location);

		if (country === 'USA')
		{
			if (accuracy<=2 || (accuracy===4 && city===undefined))  // Virginia, USA or Bronx, New York, USA 
				return this.address(location);
			else if (city && region)
				return city + ', ' + region;
			else if (region)
				return city;
			else if (city)
				return region + ', ' + country;
			else
				return country;
		}
		else
		{
			if (city && country)
				return city + ', ' + country;
			else if (region && country)
				return region + ', ' + country;
			else
				return country;	
		}
	},
	
	// state
	region:function(location)
	{
		return (location.AddressDetails.Country.AdministrativeArea) ? location.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName : "";
	},
	
	city:function(location)
	{
		var locality = this.locality(location);
		if(locality)
			return (locality.LocalityName) ? locality.LocalityName : (locality.SubAdministrativeAreaName) ? locality.SubAdministrativeAreaName : (locality.AddressLine) ? locality.AddressLine[0] : "";
	},
	
	locality:function(location)
	{
		return (location.AddressDetails.Country.Locality) ? location.AddressDetails.Country.Locality : 
				(location.AddressDetails.Country.AdministrativeArea) ? (location.AddressDetails.Country.AdministrativeArea.Locality || location.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea) : 
				(location.AddressDetails.Country.SubAdministrativeArea) ? (location.AddressDetails.Country.SubAdministrativeArea.Locality || location.AddressDetails.Country.SubAdministrativeArea.SubAdministrativeArea) : location.AddressDetails.Country;
	},
	
	postal_code:function(location)
	{
		var locality = this.locality(location);
		if(locality)
			return (locality.PostalCode) ? locality.PostalCode.PostalCodeNumber : '';
		return "";
	},
	
	accuracy:function(location)
	{
		return location.AddressDetails.Accuracy;
	},
	
	longitude:function(location)
	{
		return location.Point.coordinates[0];
	},
	
	latitude:function(location)
	{
		return location.Point.coordinates[1];
	}
	
};
