var DWRSuggest = Class.create();
DWRSuggest.prototype = {
	_provider : null,
	_textbox : null,
	_textbox_id : null,
	_output : null,
	_url : null,
	_type : null,
	_suggestion : null,
	_lastKey : null,
	_itemFunc : null,
	_onselect : null,
	_currentData : [],
	_pageSize: 10,
	_pageNo: 1,

	initialize: function(tb, id, prov, out, current, type, itemFunc, pageSize) {
		this._textbox = $(tb);
		this._textbox_id = $(id);
		this._provider = prov;
		this._output = $(out);
		this._textbox_current = $(current);
		this._type = type;
		this._itemFunc = itemFunc;
	    this._pageSize = pageSize || 10;
	    
	    Event.observe(document.body,'click',this._onDocumentClick.bind(this));
	    this._textbox.onkeyup = this._suggest.bindAsEventListener(this);
	    this._textbox.onkeydown = this._kbNavigate.bindAsEventListener(this);
	    this._output.onblur = this._onOutputBlur.bindAsEventListener(this);
	    this._textbox.onfocus = this._selectText.bindAsEventListener(this);
        this._textbox.suggest = this;
    },
    
    _onDocumentClick: function(event) {
    	log('document.body click');
    	// hide if not a click on nav
    	var targetText = Event.element(event).innerHTML;
    	if( targetText != 'next' && targetText != 'prev') {
    		this._hide(event);
    	}
    },
    
    setItemSelect: function(select) {
    	this._onselect = select;
    },
    
    _onOutputBlur: function(event) {
    	log('output.onblur');
    	//this._hide();//commented out because it breaks pagination in IE
    },

    _reset: function() {
        this._lastSearch = null;
        this._lastReqTime = null;
        this.firstLi = null;
    },

    _createDiv : function(){
		this._output = document.createElement("DIV");
		this._output.id="completeDiv";
		this._output.style.borderRight="black 1px solid";
		this._output.style.borderLeft="black 1px solid";
		this._output.style.borderTop="black 1px solid";
		this._output.style.borderBottom="black 1px solid";
		this._output.style.zIndex="10";
		this._output.style.paddingRight="0";
		this._output.style.paddingLeft="0";
		this._output.style.paddingTop="0";
		this._output.style.paddingBottom="0";
	    this._setDivSize();
		this._output.style.visibility="hidden";
		this._output.style.position="absolute";
		this._output.style.backgroundColor="white";
		document.body.appendChild(this._output);
	},
	
	_setDivSize :  function(){
		if(this._output){
			if (this._type == 'right' ) {
				var l = this._calculateLeft(this.textbox) - 394;
				var t = this._calculateTop(this.textbox) -122;
				this._output.style.left= l + "px";
				this._output.style.top= t + "px";
				if ($('autosuggestIFrame')) {
					$('autosuggestIFrame').style.left= l +" px";;
					$('autosuggestIFrame').style.top= t + "px";
				}				
			} else {
				this._output.style.left=this._calculateLeft(this.textbox)+"px";
				this._output.style.top=this._calculateTop(this.textbox)+"px";
			}
	  }
	},
	
	_setFrameSize :  function(){
		if($('autosuggestIFrame')){
			$('autosuggestIFrame').style.left=this._calculateLeft(this.textbox)+"px";
			$('autosuggestIFrame').style.top=this._calculateTop(this.textbox)+"px";
		}
	},

	_calculateWidth :  function(){
	  if(navigator&&navigator.userAgent.toLowerCase().indexOf("msie")==-1){
	    return this._textbox.offsetWidth-2;
	  }else{
	    return this._textbox.offsetWidth;
	  }
	},
	
	_calculateTop :  function(){
	  if(navigator&&navigator.userAgent.toLowerCase().indexOf("msie")==-1){
	    return Position.cumulativeOffset(this._textbox)[1] + 20;
	  } else {
	    return Position.cumulativeOffset(this._textbox)[1] + 20;  
	  }
	},
	
	_calculateLeft :  function(){
	  if(navigator&&navigator.userAgent.toLowerCase().indexOf("msie")==-1){
	    return Position.cumulativeOffset(this._textbox)[0];
	  } else {
	    return Position.cumulativeOffset(this._textbox)[0];  
	  }
	},
	
	_calculateOffsetLeft :  function(r){
	  return this._Ya(r,"offsetLeft")
	},
	
	_calculateOffsetTop :  function(r){
	  return this._Ya(r,"offsetTop")
	},
	
	_Ya :  function(r,attr){
	  var kb=0;
	  while(r){
	    kb+=r[attr];
	    r=r.offsetParent
	  }
	  return kb
	},
	
	_hide :  function(andDoUpdate) {
		log('_hide: andDoUpdate=' + andDoUpdate);
		if (this._output) {
			this._output.style.visibility = 'hidden';
		}
		if ($('autosuggestIFrame')) {
			$('autosuggestIFrame').style.display='none';
			$('autosuggestIFrame').style.visibility = 'hidden';
		}
		
		if (andDoUpdate) {
			this._doUpdate();
		}
        this._reset();
    },
    
    _selectText : function() {
    	if (this._textbox) {
    		this._textbox.select();
    	}
    },
	
	_update :  function(name, code) {
		this._textbox.value = name;
		if (this._textbox_id) {
			this._textbox_id.value = code;
			this._textbox_current.value = name;
		}
		log('hiding after update');
		this._hide();
	},
	
    _callbackProxy: function(reqTime) {
        return function(data) {
            if (this._lastReqTime && this._lastReqTime > reqTime) {
                log("lastReqTime was " + this._lastReqTime + " so discarding response for reqTime: " + reqTime);
                return;
            }
            log("processing response for reqTime: " + reqTime);
            this._callback(data);
            this._lastReqTime = reqTime;
        };
    },

	_callback :  function(data) {
        this._suggestion='';        
        if (data == null || data.length < 1) {
        	log('no data - hiding');
            this._hide();
            return;
        }
        log("data is" + data.length);
        // compare with current, only update if different
        if (this.firstLi != null) {
            log("comparing results");
            var update = false;
            if (data.length != this._currentData.length) {
            	// if the new data is a different length to the current data
            	update = true;
            } else {
            	// check each element of the two data sets
            	for (var i = 0; i < data.length; i++) {
            		if (data[i][1] != this._currentData[i][1]) {
            			update = true;
            			break;
            		}
            	}
            }
                      
            if (! update) {
                log("no update required")
                return;
            }
        }
        
        this._currentData = data;
		this._pageNo = 1;
        this._buildList(data);
	},
	
	_buildList: function(data) {
        log("doing update");
        // do update
        this.firstLi = null;
        var ul = document.createElement("UL");
        var startIndex = (this._pageNo - 1) * this._pageSize;
        var endIndex = this._pageNo * this._pageSize;
        if (endIndex > data.length) {
        	endIndex = data.length;
        }
        for (var i = startIndex; i < endIndex; i++){
            if (data[i] == null) {
                continue;
            }
            var li = document.createElement("LI");
            var bits = this._itemFunc(data[i]);
            li.locationName = bits[0];
            li.locationCode = bits[1];
            li.innerHTML = li.locationName;
            ul.appendChild(li);
            Event.observe(li, 'click', function(event) {
                this._rowSelected(Event.element(event));
                if (this._onselect) {
                	this._onselect();
                }
            }.bindAsEventListener(this));
            Event.observe(li, 'mouseover', function(event) {
                this._setSelectedRow(Event.element(event));
            }.bindAsEventListener(this));
            if (this.firstLi == null) {
                this.firstLi = li;
            }
        }
        // add 'next' and/or 'prev' links
        var numPages = this._numPages();
        if (numPages > 1) {
			var li = document.createElement('LI');
			li.isNav = true;
			li.style.textAlign = 'center';
			// build HTML
			var nav = '';
			if (this._pageNo > 1) {//prev
	        	nav = '<span style="float: left; text-decoration: underline; font-size: 13px; color: #2087CA;">prev</span>';
	        }
	        if (this._pageNo < numPages) {//next
	        	nav += '<span style="text-decoration: underline; font-size: 13px; color: #2087CA;">next</span>';
	        }
	        li.innerHTML = nav;

	        // attach event handlers
	        if (this._pageNo > 1) {//prev
	            Event.observe(li.firstChild, 'click', function(event) {
	                this._prevPage(Event.element(event));
	            }.bindAsEventListener(this));
	            Event.observe(li.firstChild, 'mouseover', function(event) {
	            	li.firstChild.style.textDecoration = 'none';
	            });
				Event.observe(li.firstChild, 'mouseout', function(event) {
	            	li.firstChild.style.textDecoration = 'underline';
	            });
	        }
	        if (this._pageNo < numPages) {//next
	        	if (this._pageNo == 1) {
	        		li.lastChild.style.cssFloat = 'left';
	        	}
	            Event.observe(li.lastChild, 'click', function(event) {
	                this._nextPage(Event.element(event));
	            }.bindAsEventListener(this));
	            Event.observe(li.lastChild, 'mouseover', function(event) {
	            	li.lastChild.style.textDecoration = 'none';
	            });
				Event.observe(li.lastChild, 'mouseout', function(event) {
	            	li.lastChild.style.textDecoration = 'underline';
	            });
	        }
	        
	        ul.appendChild(li);
        }
        this._output.innerHTML = '';
        this._output.appendChild(ul);
        this._setDivSize();
        if ($('autosuggestIFrame')) {
            $('autosuggestIFrame').style.visibility='visible';
        }
        this._output.style.visibility='visible';
        if (this.firstLi)
            this._setSelectedRow(this.firstLi);
        if (this._suggestion.length>0 && this._lastKey!=8) {
            if(this._textbox.value==data.term) this._typeahead();
        }
	},

    _rowSelected: function(li) {
        this._update(li.locationName, li.locationCode);
    },

    _kbNavigate:  function(event) {
		var consumed = false;
		if (!event) {
            //alert('getting window event');
	        event=(window.event);
	    }
	    if (event && event.keyCode) {
			var code = event.keyCode;
			var selectedLi = this._getSelectedRow();
            if (code == Event.KEY_DOWN) {
                // down arrow, go down a selected row if possible
                if (selectedLi.nextSibling && ! selectedLi.nextSibling.isNav) {
                    this._setSelectedRow(selectedLi.nextSibling);
                }
                consumed = true;
			} else if (code == Event.KEY_UP) {
                // up arrow, go up a selected row if possible
				if (selectedLi.previousSibling) {
                    this._setSelectedRow(selectedLi.previousSibling);
                }
				consumed = true;
			} else if (code == Event.KEY_RETURN) {
                // enter keypress - update textbox with data from selected row.
				log('enter key consumed');
				consumed = this._doUpdate();
			}
		}
        return !consumed;
	},
	
    _setSelectedRow: function(element) {
        var li = this.firstLi;
        while (li) {
            if (li == element) {
                highlight(li, '#E7F2F8');
                li.selected = true;
            } else {
                highlight(li, 'white');
                li.selected = false;
            }
            li = li.nextSibling;
        }
    },

    _getSelectedRow: function() {
        var li = this.firstLi;
        while (li) {
            if (li.selected) {
                return li;
            }
            li = li.nextSibling;
        }
        return null;
    },
    
    _nextPage: function() {
    	if (this._pageNo < this._numPages()) this._pageNo++;
    	this._buildList(this._currentData);
    },
    
    _prevPage: function() {
    	if (this._pageNo > 1) this._pageNo--;
    	this._buildList(this._currentData);
    },
    
    _numPages: function() {
    	return Math.ceil(this._currentData.length / this._pageSize);
    },

	_doUpdate :  function() {
		log('_doUpdate');
        var selectedLi = this._getSelectedRow();
        if (selectedLi) {
            this._rowSelected(selectedLi);
            if (this._onselect) {
            	this._onselect();
            }
			return true;
		}
		return false;
	},
	
	_suggest :  function(event) {
	    if(!event){
	        event=(window.event);
	    }
	    if(!event || !event.keyCode){
            return;
        }
        var code = event.keyCode;
        this._lastKey = code;
        if(code!=8&&(code < 32 || (code >= 33 && code <= 46) || (code >= 112 && code <= 123))){
            return;
        }
        this._deferredSuggest();
    },

    _deferredSuggest: function() {
        if (this._searchTimeoutId) {
            clearTimeout(this._searchTimeoutId);
        }
        this._searchTimeoutId = setTimeout("DWRSuggest.getByElement('" + this._textbox.id + "').doSuggest();", 200);
	},

    doSuggest: function() {
        this._searchTimeoutId = null;
        var partial = this._textbox.value;
        partial = partial.replace(/^\s+/, "").replace(/\s+$/, "");
        if (partial.length < 3) {
            log("less than 3 letters - hiding")
            this._hide();
        } else if (this._lastSearch != partial) {
            this._lastSearch = partial;
            log("searching on " + partial)
            this._provider(this._callbackProxy(new Date().getTime()).bind(this), partial);
        }
    },

    _selectRange :  function(start, end) {
	    if (this._textbox.setSelectionRange){
	        this._textbox.setSelectionRange(start, end);
	    }else 
	    if(this._textbox.createTextRange){
	        var range = this._textbox.createTextRange();
	        range.moveStart("character", start);
	        range.moveEnd("character",end-this._textbox.value.length);
	        range.select();
	    }
	    this._textbox.focus();
	},
	
	_typeahead :  function () {
	    if(this._textbox.setSelectionRange||this._textbox.createTextRange){
		    var partial = this._textbox.value;
		    var pl = partial.length;
		    var sl = this._suggestion.length;
		    this._textbox.value = this._suggestion;
		    this._selectRange(pl,sl);
		    this._textbox.focus();
	    }
	}
}

// Class functions
Object.extend(DWRSuggest, {
    getByElement: function(element) {
        return $(element).suggest;
    }
});
