(function() {

	'use strict';



	var L = require('leaflet');

	var Autocomplete = require('./autocomplete');

	var Localization = require('./localization');



	function selectInputText(input) {

		if (input.setSelectionRange) {

			// On iOS, select() doesn't work

			input.setSelectionRange(0, 9999);

		} else {

			// On at least IE8, setSeleectionRange doesn't exist

			input.select();

		}

	}



	module.exports = L.Class.extend({

		includes: ((typeof L.Evented !== 'undefined' && L.Evented.prototype) || L.Mixin.Events),



		options: {

			createGeocoder: function(i, nWps, options) {

				var container = L.DomUtil.create('div', 'leaflet-routing-geocoder'),

					input = L.DomUtil.create('input', '', container),

					remove = options.addWaypoints ? L.DomUtil.create('span', 'leaflet-routing-remove-waypoint', container) : undefined;



				input.disabled = !options.addWaypoints;



				return {

					container: container,

					input: input,

					closeButton: remove

				};

			},

			geocoderPlaceholder: function(i, numberWaypoints, geocoderElement) {

				var l = new Localization(geocoderElement.options.language).localize('ui');

				return i === 0 ?

					l.startPlaceholder :

					(i < numberWaypoints - 1 ?

						L.Util.template(l.viaPlaceholder, {viaNumber: i}) :

						l.endPlaceholder);

			},



			geocoderClass: function() {

				return '';

			},



			waypointNameFallback: function(latLng) {

				var ns = latLng.lat < 0 ? 'S' : 'N',

					ew = latLng.lng < 0 ? 'W' : 'E',

					lat = (Math.round(Math.abs(latLng.lat) * 10000) / 10000).toString(),

					lng = (Math.round(Math.abs(latLng.lng) * 10000) / 10000).toString();

				return ns + lat + ', ' + ew + lng;

			},

			maxGeocoderTolerance: 200,

			autocompleteOptions: {},

			language: 'en',

		},



		initialize: function(wp, i, nWps, options) {

			L.setOptions(this, options);



			var g = this.options.createGeocoder(i, nWps, this.options),

				closeButton = g.closeButton,

				geocoderInput = g.input;

			geocoderInput.setAttribute('placeholder', this.options.geocoderPlaceholder(i, nWps, this));

			geocoderInput.className = this.options.geocoderClass(i, nWps);



			this._element = g;

			this._waypoint = wp;



			this.update();

			// This has to be here, or geocoder's value will not be properly

			// initialized.

			// TODO: look into why and make _updateWaypointName fix this.

			geocoderInput.value = wp.name;



			L.DomEvent.addListener(geocoderInput, 'click', function() {

				selectInputText(this);

			}, geocoderInput);



			if (closeButton) {

				L.DomEvent.addListener(closeButton, 'click', function() {

					this.fire('delete', { waypoint: this._waypoint });

				}, this);

			}



			new Autocomplete(geocoderInput, function(r) {

					geocoderInput.value = r.name;

					wp.name = r.name;

					wp.latLng = r.center;

					this.fire('geocoded', { waypoint: wp, value: r });

				}, this, L.extend({

					resultFn: this.options.geocoder.geocode,

					resultContext: this.options.geocoder,

					autocompleteFn: this.options.geocoder.suggest,

					autocompleteContext: this.options.geocoder

				}, this.options.autocompleteOptions));

		},



		getContainer: function() {

			return this._element.container;

		},



		setValue: function(v) {

			this._element.input.value = v;

		},



		update: function(force) {

			var wp = this._waypoint,

				wpCoords;



			wp.name = wp.name || '';



			if (wp.latLng && (force || !wp.name)) {

				wpCoords = this.options.waypointNameFallback(wp.latLng);

				if (this.options.geocoder && this.options.geocoder.reverse) {

					this.options.geocoder.reverse(wp.latLng, 67108864 /* zoom 18 */, function(rs) {

						if (rs.length > 0 && rs[0].center.distanceTo(wp.latLng) < this.options.maxGeocoderTolerance) {

							wp.name = rs[0].name;

						} else {

							wp.name = wpCoords;

						}

						this._update();

					}, this);

				} else {

					wp.name = wpCoords;

					this._update();

				}

			}

		},



		focus: function() {

			var input = this._element.input;

			input.focus();

			selectInputText(input);

		},



		_update: function() {

			var wp = this._waypoint,

			    value = wp && wp.name ? wp.name : '';

			this.setValue(value);

			this.fire('reversegeocoded', {waypoint: wp, value: value});

		}

	});

})();

