var SearchControl = Class.create({
	
	ajaxRequest: null,
	
	requestResultsDelay: 500,
	requestResultsTimeout: null,
	autoSelectSingleResult: true,
	mouseOutDelay: 5000,
	blurDelay: 250,
	placeDetailSeparator: ', ',
	noResultsText: 'No results found',
	
	// Set to true when the results div is populated with results for the text that has been typed.
	resultsAreReady: false,
	
	// Workaround for bug in IE8.
	// The first call to searchResultsDiv.hide() causes other elements to be hidden (e.g. submit button).
	resultsHaveBeenShown: false,
	
	// Remembers if the user hides the results whilst the results are being requested.
	resultsDisplayCancelled: false,
	
	// NB: The hideResultsOn.mouseout setting is read-only. Configure this through the constructor.
	hideResultsOn: { timeout: true, mouseout: true, blur: false },
	
	initialize: function(inputId, formId, searchResultsId, callback, hideResultsOn) {
		if(!$(inputId)) {
			return;
		}
		if(!$(formId)) {
			return;
		}
		if(!$(searchResultsId)) {
			return;
		}
		this.inputId = inputId;
		this.formId = formId;
		this.searchResultsId = searchResultsId;
		this.callback = function() {};
		if(callback) {
			this.callback = callback;
		}
		if (hideResultsOn)
		{
			this.hideResultsOn = hideResultsOn;
		}

		this.searchWorkers = Array();
		this.ajaxRequests = Array();
		this.ajaxRequestsBeingBuilt = false;
		this.resultsTimeout = null;
		Event.observe(inputId, 'keyup', this.handleKey.bindAsEventListener(this));
		Event.observe(inputId, 'blur', this.handleBlur.bind(this));
		Event.observe(inputId, 'focus', this.handleFocus.bind(this));
		Event.observe(formId, 'submit', this.handleAjax.bindAsEventListener(this));
		if (this.hideResultsOn.mouseout)
		{
			Event.observe(searchResultsId, 'mouseover', function() { this.hideResultsClearTimeout(); }.bind(this));
			Event.observe(searchResultsId, 'mouseout', this.hideResults.bind(this, this.mouseOutDelay));
		}
	},
	
	register: function(searchWorker) {
		this.searchWorkers.push(searchWorker);
	},
	
	handleBlur: function()
	{
		if (this.hideResultsOn.blur)
		{
			// Use a delay to allow potential click events
			// from the results list to come through to the clickHandler.
			this.hideResults( null, this.blurDelay );
		}
	},
	
	handleFocus: function()
	{
		if (this.hideResultsOn.blur)
		{
			// Show the results that were hidden on blur.
			// But first cancel any timeout being used to hide the results.
			this.hideResultsClearTimeout();
			this.showResultsNow();
		}
	},
	
	handleKey: function(evt)
	{
		// The search term has changed. Immediately hide results from previous search.
		this.hideResultsNow();
		
		// And invalidate the current set of results.
		this.resultsAreReady = false;
		
		// But don't request new results now. Wait for the user to stop typing.
		if (this.requestResultsTimeout)
		{
			window.clearTimeout( this.requestResultsTimeout );
		}
		var thisControl = this;
		this.requestResultsTimeout = window.setTimeout( function() { thisControl.handleAjax(); }, this.requestResultsDelay );
		
		Event.stop(evt);
	},
	
	handleAjax: function() {
		var searchTerm = $(this.inputId).value;
		//cancel existing requests
		for(var i = 0; i < this.ajaxRequests.length; i++) {
			if(this.ajaxRequests[i] != null) {
				this.ajaxRequests[i].transport.abort();
				this.ajaxRequests[i] = null;
			}
		}
		
		// A new request for results. Reset the cancelled flag.
		this.resultsDisplayCancelled = false;
		
		this.ajaxRequests = Array();
		this.ajaxRequestsBeingBuilt = true;
		for(var i = 0; i < this.searchWorkers.length; i++) {
			if(this.searchWorkers[i].isValid(searchTerm)) {
				this.ajaxRequests[i] = new Ajax.Request(this.searchWorkers[i].getUrl(searchTerm), { 'onComplete' : this.processResults.bind(this), 'method' : 'get' } );
			}
		}
		this.ajaxRequestsBeingBuilt = false;
	},
	
	processResults: function() {
		if (this.ajaxRequestsBeingBuilt)
		{
			// Workaround for cached AJAX requests in IE6.
			// This response handler is called before the ajaxRequests array is built.
			// Wait until the array is ready.
			var thisControl = this;
			window.setTimeout( function() { thisControl.processResults(); }, 100 );
			return;
		}
		
		for(var i = 0; i < this.ajaxRequests.length; i++) {
			if(this.ajaxRequests[i] && !this.ajaxRequests[i]._complete) {
				return;
			}
		}
		
		var results = Array();
		for(var i = 0; i < this.ajaxRequests.length; i++) {
			var tmpResult = eval(this.ajaxRequests[i].transport.responseText);
			for(var j = 0 ; j < tmpResult.length; j++) {
				results.push(tmpResult[j]);
			}
		}
		this.populateResults(results);
	},
	
	hideResultsClearTimeout: function()
	{
		if (this.resultsTimeout != null)
		{
			clearTimeout( this.resultsTimeout );
			this.resultsTimeout = null;
		}
	},
	
	hideResults: function(evt,delay) {
		this.resultsTimeout = setTimeout(
		function() {
			if(this.resultsTimeout != null) {
				this.hideResultsNow();
			}
		}.bind(this), delay);
	},
	
	hideResultsNow: function()
	{
		if (this.resultsHaveBeenShown)
		// To maintain workaround for IE8: don't check the resultsAreReady flag.
		{
			var resultsDiv = $(this.searchResultsId);
			resultsDiv.hide();
			
			this.resultsDisplayCancelled = true;
		}
	},
	
	showResultsNow: function()
	{
		if (this.resultsAreReady)
		{
			var resultsDiv = $(this.searchResultsId);
			resultsDiv.show();
		}
	},

	populateResults: function (results) {

		//get the results DIV
		var resultsDiv = $(this.searchResultsId);
		if (!this.resultsDisplayCancelled)
		{
			resultsDiv.show();
		}
		this.resultsHaveBeenShown = true;
		this.resultsAreReady = true;
		//remove any children
		resultsDiv.childElements().each(function(element) { element.remove(); });
		
		//create a new UL
		var resultsList = new Element('ul');
		resultsList.className = 'resultsList';
		 
		if(results.length == 0){
			var resultItem = new Element('li');
			resultItem.className = 'resultItem';
			resultItem.innerHTML = this.noResultsText;
			//add the LI into the UL
			resultsList.insert(resultItem);
		}
		//if there is only one result, then run the handler
		else if ((results.length == 1) && (this.autoSelectSingleResult)) {
			this.clickHandler(null, results[0]);
			return;
		}
		else {
			for(var i = 0; i < results.length; i++) {
				var result = results[i];
				var resultName = result.placeName;
				if(result.placeDetail && result.placeDetail != '') {
					resultName += this.placeDetailSeparator;
					resultName += result.placeDetail;
				} 

				//add in the bolding
			 
				//create a new LI
				var resultItem = new Element('li');
				resultItem.className = 'resultItem';
				resultItem.innerHTML = resultName;
				//add the LI into the UL
				resultsList.insert(resultItem);
			}
		}
		
		
		resultsDiv.insert(resultsList);

		//assign events to all the list items
		//add onclick event to move the map
		 
		for(var i = 0; i < results.length; i++) {
			var result = results[i];
			var resultItem = resultsDiv.childElements()[0].childElements()[i];
			Event.observe(resultItem, 'click', this.clickHandler.bindAsEventListener(this, result));
			Event.observe(resultItem, 'mouseover', function(evt) { Event.element(evt).addClassName("highlighted"); } );
			Event.observe(resultItem, 'mouseout', function(evt) { Event.element(evt).removeClassName("highlighted"); } );
		}
		
		if (this.hideResultsOn.timeout)
		{
			this.hideResultsClearTimeout();
			this.hideResults(null, this.mouseOutDelay);
		}
	},
	
	clickHandler: function (evt, result) {
		this.callback(result);
		//hide results div
		$(this.searchResultsId).hide();
	},
	
	updateParams: function(params) {
		for(var i = 0; i < this.searchWorkers.length; i++) {
			this.searchWorkers[i].params = params;
		}
	},
	
	setSearchTerm: function( searchTerm )
	{
		$(this.inputId).value = searchTerm;
		this.resultsAreReady = false;
	}
	
});

SearchWorker = Class.create({
	
	SEARCH_PARAM: 'search',
	
	params: new Hash(),
	
	initialize: function(url, isValidFunction, getCentre) {
		this.url = url;
		this.isValid = function(searchTerm) { return searchTerm.length >= 3; };
		if(isValidFunction) {
			this.isValid = isValidFunction;
		}
	},
	
	getUrl: function(searchTerm) {
		var searchURL = this.url + '?' + this.SEARCH_PARAM + "=" + searchTerm;
		if(this.params.keys().length > 0) {
			this.params.each(function(pair) {
				searchURL += '&';
				searchURL += escape(pair[0]);
				searchURL += '=';
				searchURL += escape(pair[1]);
			});
		}

		return searchURL;
	}
	
});

