
/**
* TIP: keep the text input obj outside the <form> containing the select object
*/
var Suggester = new Class({    

		Implements: [Options],
		options: {},
		initialize: function(textInputId, targetSelectId, optionCssClass, colorIfUnselected, bgcolorIfUnselected, methodToCall, arrayMethodToCall){
			//the text input object where we type suggestions
			this.textInputObj = $(textInputId);
			//the select object we take control of and where we eventually put their choice
			this.targetSelectObj = $(targetSelectId);
			//the css class for the options in the suggest list (needs a :hover bit in the css class)
			this.optionCssClass = optionCssClass;
			//this is the same as the normal color in the optionCssClass (no hover)
			//we need these colours when we go up and down using the keyboard
			this.COLOR_UNSELECTED = colorIfUnselected;
			//this is the same as the normal bgcolor in the optionCssClass (no hover)
			this.BGCOLOR_UNSELECTED = bgcolorIfUnselected;
			//the method to call if they select a single property (should call the house description page)
			this.methodToCall = methodToCall;
			//the method to call if they select an array of properties (should call the catalogue page)
			//this happens if there are several names remaining in the suggest box and they click on the
			//search button, or press the ENTER key. 
			//Can be null
			this.arrayMethodToCall = arrayMethodToCall;
			
			//We prepare a clickable menu option for each property in the select object
			//and put them all in a Hash for later look up
			this.prepareOptionsWithEventsHash();
		},
		
 		BACKSPACE: 8,
 		UP: 38,
 		DOWN: 40,
 		ENTER: 13,
 		
 		//index of the current line in the list
 		currentSelectedSuggest: -1,
 		//the max line reached in the list, this helps us work out when to
 		//scroll up and down when using just the keyboard
 		maxSelectedSuggest: 0,
 		 		
 		/**
 		* Holds the original option contents of the select object
 		* so that we can put it all back afterwards.
 		*/
 		origOptions: null,
 		optionsArrayReduced: null,
 		BGCOLOR_UNSELECTED: '',
 		//The two SELECTED colours are the same for all sites at the moment
 		//so we don't ask for them in the initialization variables
  		BGCOLOR_SELECTED: '#3D70CE',
 		COLOR_UNSELECTED: '#58585a',
  		COLOR_SELECTED: '#FFFFFF',  		

  		  		
		suggest: function(event){
		
			this.currentEvent = event;
			var toFind = this.textInputObj.value;
			
			var theKey = event.code;
      
      		if(this.origOptions == null){
     			this.cloneOrigOptions(this.targetSelectObj);
     		}//end if

 	 		if(theKey == this.UP || theKey == this.DOWN){
 	 			this.moveInSelect(theKey);
	 			return;
 	 		}//end if
		
 	 		
			if(theKey == this.ENTER){
				this.doEnterKey();
				return;
			}
		
     		var toFindPartsArray = toFind.toLowerCase().split(" ");
     		toFindPartsArray = this.removeZeroLengthStrings(toFindPartsArray);
     		
     		if(!$defined(toFindPartsArray) || toFindPartsArray[0] == undefined){
				//this.restoreOriginalOptions(this.targetSelectObj, this.textInputObj);
	 			if($defined($('suggestBox'))){
					//$('suggestBox').destroy();
	 				//$('suggestBox').empty();
	 				$('suggestBox').setStyle('visibility','hidden');
	 				this.showAllContentLineDivs();
					if($defined($('nameSelect'))){
						$('nameSelect').setStyle('visibility','visible');
					}
				}//end if
				this.targetSelectObj.selectedIndex = 0;
				this.currentSelectedSuggest = -1;
				this.optionsArrayReduced = null;
				return;
     		}//end if

			this.optionsArrayReduced = this.getOptionsArrayReduced(this.targetSelectObj, toFindPartsArray);

			//this.emptySelectOptions();

			if($defined(this.optionsArrayReduced)){
				this.addChosenOptionsAndShow(this.optionsArrayReduced);
			}//end if
			
			if(this.optionsArrayReduced != null && this.optionsArrayReduced.length == 1){
				//if there's only one choice left in the optionsArrayReduced box, then
				//people think they've selected it, so get the targetSelectObj ready by 
				//finding the 'chosen' text
				this.setTheSelect(this.optionsArrayReduced[0].text);
			}else{
				//otherwise reset it
				this.targetSelectObj.selectedIndex = 0;
			}

		},//end function()
		

		doEnterKey: function(){
			//if there's only one choice left people always think that they've chosen it
			//so take that choice
			if(this.optionsArrayReduced != null && this.optionsArrayReduced.length == 1){
				this.setTheSelect(this.optionsArrayReduced[0].text);
				this.methodToCall();
			}//end if


			if(this.setTheSelect(this.textInputObj.value)){
				this.methodToCall();
			}//end if

		},

		getOptionsArrayReduced: function(targetSelectObj, toFindPartsArray){		
		
		    var optionsArrayReduced = [];
			var reducedArrayCounter = 0;
		
			//now run through each house name in the list
			for(var i = 0; i < targetSelectObj.options.length; i = i + 1){
				var currentNomecasa = targetSelectObj.options[i].text;
				var nomecasaSplit = this.splitIt(currentNomecasa);
				
				//this foundCounter has to match the length of the parts array of the 
				//String to find
				var foundCounter = 0;
				
				//run through each part of the house name
				for(var j = 0; j < nomecasaSplit.length; j = j + 1){
					var currentBit = nomecasaSplit[j];
					//go through all parts of the String we're looking for
					for(var x = 0; x < toFindPartsArray.length; x = x + 1){
						//if we find the part of the String to find at the beginning of
						//the part of the house name, we can add to foundCounter
						if(nomecasaSplit[j].indexOf(toFindPartsArray[x]) == 0){
							foundCounter = foundCounter + 1;
						}//end if
					}//end for
					
					if(foundCounter >= toFindPartsArray.length){
						if(optionsArrayReduced == undefined){
						 	optionsArrayReduced = new Array(0);
						}
						optionsArrayReduced[reducedArrayCounter] = targetSelectObj.options[i];
						reducedArrayCounter = reducedArrayCounter + 1;
						break;
					}//end if
					
				}//end for

			}//end for
			return optionsArrayReduced;
		},//end function

		splitIt: function(nomecasa){
			var nomecasaSplit = nomecasa.toLowerCase().split(" ");
			//now we add splits on inverted commas as in "L'Arte della Lana"
			if(nomecasa.indexOf('\'') < 0){
				return nomecasaSplit;
			}
			var newArray = [];
			for(var i = 0; i < nomecasaSplit.length; i = i + 1){
				var currentBit = nomecasaSplit[i];
				if(currentBit.indexOf('\'') > -1){
					var tempArray = currentBit.split('\'');
					nomecasaSplit.extend(tempArray);
				}
			}//end for
			return nomecasaSplit;
		},

		
		addOptionsWithEventsDivs: function(selectDiv){
			//now run through each house name in the list
			for(var i = 0; i < this.targetSelectObj.options.length; i = i + 1){
				var currentNomecasa = this.targetSelectObj.options[i].text;
				var contentLineDiv = this.optionsWithEventsHash.get(currentNomecasa);
				if(contentLineDiv){
					contentLineDiv.inject(selectDiv);
				}
			}//end for

		},

		
		prepareOptionsWithEventsHash: function(){
			
			//IE6 only works with #none, if you want to be sure that IE6 works,
			//fill a 'javascriptEmpty' field with SiteUtility.getHrefForHidingURLs()
			var hreffer = 'javascript:;';
			if($defined($('javascriptEmpty'))){
				hreffer = $('javascriptEmpty').value;
			}

			this.optionsWithEventsHash = $H({});
			
			var widget = this;
			//now run through each house name in the list, ignoring the first empty one
			for(var i = 0; i < widget.targetSelectObj.options.length; i = i + 1){
				var currentNomecasa = widget.targetSelectObj.options[i].text;
					var contentLineDiv = new Element('a',{
						html: currentNomecasa,
						id: 'contentLineDiv' + i,
						'class': this.optionCssClass,
						href: hreffer,
						styles: {
							paddingLeft: '2px',
							display: 'block',
							height: '14px'
						}
					});
					contentLineDiv.addEvent('mouseover', function(){
						widget.setSelected(this);
					});
					contentLineDiv.addEvent('mouseout', this.unselectAll);
					contentLineDiv.addEvent('click',function(){
						widget.setTheTextBox(this);
						widget.setTheSelect(this.get('html'));
						widget.methodToCall();
					});
					this.optionsWithEventsHash.set(currentNomecasa, contentLineDiv);
			}//end for

		},

		
		addChosenOptionsAndShow: function(optionsArray){

			var widget = this;
			if(optionsArray == undefined){
				return;
			}//end if

			
			if(!$defined($('suggestBox'))){
				this.getNewSuggestBox(this.textInputObj);
			}
			
			$('suggestBox').setStyle('visibility','visible');

			
			//for IE6 otherwise the select remains above the new div...
			if($defined($('nameSelect'))){
				$('nameSelect').setStyle('visibility','hidden');
			}
			
			/*
			//IE6 only works with #none, if you want to be sure that IE6 works,
			//fill a 'javascriptEmpty' field with SiteUtility.getHrefForHidingURLs()
			var hreffer = 'javascript:;';
			if($defined($('javascriptEmpty'))){
				hreffer = $('javascriptEmpty').value;
			}
			*/
			
			this.hideAllContentLineDivs();

			//set up all the content lines with text and events
			for(var i = 0; i < optionsArray.length; i = i + 1){
				var nomecasa = optionsArray[i].text;
				var contentLineDiv = (widget.optionsWithEventsHash).get(nomecasa);
				contentLineDiv.setStyle('display','block'); 
			}//end for

		},//end function

		hideAllContentLineDivs: function(){
			for(var i = 0; i < this.targetSelectObj.options.length; i = i + 1){
				var currentNomecasa = this.targetSelectObj.options[i].text;
				var contentLineDiv = this.optionsWithEventsHash.get(currentNomecasa);
				if(contentLineDiv){
					contentLineDiv.setStyle('display','none');
				}
			}//end for
		},
		
		showAllContentLineDivs: function(){
			for(var i = 0; i < this.targetSelectObj.options.length; i = i + 1){
				var currentNomecasa = this.targetSelectObj.options[i].text;
				if(currentNomecasa != null && currentNomecasa.length > 0){
					var contentLineDiv = this.optionsWithEventsHash.get(currentNomecasa);
					if(contentLineDiv){
						contentLineDiv.setStyle('display','block');
					}//end if
				}//end if
			}//end for
		},
		
		
		setTheTextBox: function(contentLineDiv){
			this.textInputObj.value = contentLineDiv.get('html');
		},

		/**
		 * sets the value of the target select object
		 */
		setTheSelect: function(theText){

			//$each wouldn't work in Internet Explorer of course
			for(var index = 0; index < this.targetSelectObj.options.length; index = index + 1){
				if(this.targetSelectObj.options[index].text === theText){
					this.targetSelectObj.selectedIndex = index;
					return true;
				}//end if
			}//end for
			return false;
		},
		
		getNewSuggestBox: function(textInputObj){
					
			var suggestBox = new Element('div',{
				id: 'suggestBox',
				styles: {
					width: textInputObj.clientWidth + 'px',
					height: '150px',
					borderStyle: 'solid',
					borderColor: '#CCCCCC',
					borderWidth: '1px',
					backgroundColor: this.BGCOLOR_UNSELECTED,
					position: 'absolute',
					//overflow: 'auto',
					left: textInputObj.getPosition().x + 'px',
					top: (textInputObj.getPosition().y + textInputObj.clientHeight + 3) + 'px'
				}			
			});
			
			if(Browser.Engine.trident){
				suggestBox.setStyles({
					overflow: 'visible', 
					overflowX: 'hidden', 
					overflowY: 'auto'
				});
			}else{
				suggestBox.setStyle('overflow', 'auto');
			}
			
			this.addOptionsWithEventsDivs(suggestBox);
			
			suggestBox.inject(document.body);

			return suggestBox;
		},
		
		emptySelectOptions: function(){
		
			this.currentSelectedSuggest = -1;
 			this.maxSelectedSuggest = 0;
		
			for(var i = 0; i < 1000; i = i + 1){
				var contentLineDiv = $('contentLineDiv' + i);
				if(!$defined(contentLineDiv)){
					break;
				}//end if
				contentLineDiv.destroy();
			}//end for
		},//end function
		
		restoreOriginalOptions: function(targetSelectObj, textInputObj){
			this.emptySelectOptions();
		},//end function()
		
		cloneOrigOptions: function(targetSelectObj){
			this.origOptions = new Array(1);
			for(var i = 0; i < targetSelectObj.options.length; i = i + 1){
				this.origOptions[i] = targetSelectObj.options[i];
			}//end for
		},//end function()
		
		/**
		* If they accidentally put in just a space, then there will 
		* be two zero length strings in the array, which we don't want:
		* here we remove them, if there are any.
		*/
		removeZeroLengthStrings: function(toFindPartsArray){
			if(toFindPartsArray.length > 0){
				var newArray = new Array(1);
				var counter = 0;
				for(var i = 0; i < toFindPartsArray.length; i = i + 1){
					if(toFindPartsArray[i].length > 0){
						newArray[counter] = toFindPartsArray[i];
						counter = counter + 1;
					}//end if
				}//end for
				return newArray;
			}//end if
			return toFindPartsArray;
		},//end function()

		/**
		 * Takes the optionsArrayReduced object and produces a url parameter String of the
		 * properties in it
		 */
		getActionParamsFromOptionsArray: function(){
			var paramString = '?fromQuickFind=true&';
			this.optionsArrayReduced.each(function(currentOption, index){
				paramString += 'nomecasa=' + encodeURIComponent(currentOption.text) + '&'; 
			});
			paramString = paramString.substring(0, paramString.length - 1);
			return paramString;
		},
		
		/**
		* calculates the index of the current line, and graphically selects the current line in the suggestBox
		*/
		moveInSelect: function(theKey){

			if(theKey == this.UP){
				if(this.currentSelectedSuggest > -1){
					this.currentSelectedSuggest = this.currentSelectedSuggest - 1;
				}//end if
			}else{
				if($defined(this.optionsArrayReduced) && this.currentSelectedSuggest < this.optionsArrayReduced.length - 1){
					this.currentSelectedSuggest = this.currentSelectedSuggest + 1;
				}//end if
			}//end if

			if(this.currentSelectedSuggest > -1){
				this.textInputObj.value = this.optionsArrayReduced[this.currentSelectedSuggest].text;
				this.setTheSelect(this.optionsArrayReduced[this.currentSelectedSuggest].text);
				var contentLineDiv = this.optionsWithEventsHash.get(this.optionsArrayReduced[this.currentSelectedSuggest].text);
				this.setSelected(contentLineDiv);
				this.checkLineIsVisible(theKey);
			}//end if
		},//end function()
		
		/**
		* Since the list might be longer than can be contained in the suggestBox, we check if the current
		* line has gone out of view and if necessary scroll up or down to make the current selection visible
		*/
		checkLineIsVisible: function(theKey){
			var currentText = this.optionsArrayReduced[this.currentSelectedSuggest].text;
			var currentSelectLineObj = this.optionsWithEventsHash.get(currentText);
				
			var suggestBox = $('suggestBox');
			
			//the height of each line
			var lineObjHeight = currentSelectLineObj.offsetHeight;

			var myFx = new Fx.Scroll('suggestBox', {
				duration: 10
			});

			
			var bottomOfCurrentSelectLine = (this.currentSelectedSuggest + 1) * lineObjHeight;
			var bottomOfSuggestBox = suggestBox.offsetHeight;

			if(theKey == this.DOWN && bottomOfCurrentSelectLine > bottomOfSuggestBox){
				var whereTo = ((this.currentSelectedSuggest + 1) * lineObjHeight) - suggestBox.offsetHeight;
				myFx.set(0, whereTo);
				//each time we exceed the bottom maxSelectedSuggest increases
				this.maxSelectedSuggest = this.currentSelectedSuggest;
			}

			var distanceAboveMax = (this.maxSelectedSuggest - this.currentSelectedSuggest + 1) * lineObjHeight;
			

			if(theKey == this.UP && distanceAboveMax > suggestBox.offsetHeight){
				myFx.toElement(currentSelectLineObj);
				this.maxSelectedSuggest = this.maxSelectedSuggest - 1;
			}
		},

		setSelected: function(theDiv){
			this.unselectAll();
			if($defined(theDiv)){
				theDiv.setStyle('backgroundColor', this.BGCOLOR_SELECTED);
				theDiv.setStyle('color', this.COLOR_SELECTED);
			}
		},

		unselectAll: function(){			
			if(this.optionsArrayReduced){
				for(var i = 0; i < this.optionsArrayReduced.length; i = i + 1){
					var contentLineDiv = this.optionsWithEventsHash.get(this.optionsArrayReduced[i].text);
					if(contentLineDiv){
						contentLineDiv.setStyle('backgroundColor', this.BGCOLOR_UNSELECTED);
						contentLineDiv.setStyle('color', this.COLOR_UNSELECTED);
					}
				}//end for
			}//end if
		},

		closeSuggestBox: function(){
			if($defined($('suggestBox'))){
				$('suggestBox').destroy();
			}//end if
			if($defined($('nameSelect'))){
				$('nameSelect').setStyle('visibility','visible');
			}
		}
});

