//Copyright (c) 2009 Amazon.com, Inc., and its Affiliates.
//All rights reserved.
//Not to be reused without permission
//$DateTime$ 

//JavaScript for autocompleting user input on a specified text field with specified data values

// binds a method to a new 'owner' if needed 
Function.prototype.bind = function (obj) {
    var fn = this;
    return function () {
        var args = [this];
        
        for (var i = 0, ix = arguments.length; i < ix; i++) {
            args.push(arguments[i]);
        }
        return fn.apply(obj, args);
    };
};   

function searchSuggest(searchBoxId, searchFormId, hiddenInputId, submitButtonId, data, values)
{

    //public variables
    this.searchBox = document.getElementById(searchBoxId);
    this.searchFormName = searchFormId;
    this.formElement = document.getElementById(searchFormId);
    this.hiddenInput = document.getElementById(hiddenInputId);
    this.submitButton = document.getElementById(submitButtonId);
    this.data = data;
    this.values = values;
    
    //Private variables
    var curSelection = -1;  //the current selection index in the suggestion list
    var curSuggestions = []; //array of suggestions returned from the server
    var curSize = 0; //the number of suggestions displayed (can be less than the number returned from the server)
    var curText = ""; //the text that was currently entered in the search box
    var curTextSel = ""; //the text that is currently selected with up, down keys
    var hideDelayTimerId = null; //timer id used to delay the hiding of the suggestions list
    var searchSuggestionTimerId = null; //timer id used to delay the execution of a new search suggestion request
    var maxSuggestions = 7; //maximum number of suggestions that will be displayed
    var prevWndResizeEventHandler = null; //existing event handler
    var prevWndOnLoadEventHandler = null;
    var isMsie = false;
    var suggestRequest = null;
    var suggestionsLoaded = false; //to prevent hooking the events twice
    var suggestDiv = document.getElementById('srch_sggst');
    var firstSuggestionFoundIndex = -1;//an index into the master data list indicating where the first suggestion option was found
    var sugDivPrefix = 'issDiv';
    
    //public methods
    this.init = function()
        { 

            if (suggestionsLoaded) { return;}
            
            isMsie = navigator.userAgent.toLowerCase().indexOf("msie") != -1;
            if (this.searchBox)
                {
                    curText = this.searchBox.value;
                    this.searchBox.setAttribute("autocomplete", "off");
                    this.attachEventListener(this.searchBox, "keydown", this.onKeyDownEvent);
                    this.attachEventListener(this.searchBox, "keyup", this.onKeyUpEvent);
                    this.attachEventListener(this.searchBox, "keypress", this.onKeyPressedEvent);
                    this.attachEventListener(this.searchBox, "blur", this.onFocusLost);
                    
                    prevWndResizeEventHandler = window.onresize; //save previous one
                    this.attachEventListener(window, "resize", this.onWindowResized);
                  
                    suggestionsLoaded = true;
                }

        };
    
    this.onKeyDownEvent = function(el, event)
        {
            var key = event.keyCode;

            switch(key)
                {
                case 40: this.moveDown();
                         this.stopEvent(event);
                    break;
                case 38: this.moveUp();
                         this.stopEvent(event);
                    break;
                }
        }.bind(this);
    
    this.onKeyUpEvent = function(el, event)
        {
            var key = event.keyCode;
            switch(key)
                {
                case 13: this.HideSuggestionsDiv();break;
                case 40: break;
                case 38: break;
                case 37: break;//left arrow
                case 39: break;//right arrow
                default: 
                    {
                        var val = this.searchBox.value;
                        if (val != curText)
                            {
                                curText = val;
                                this.setSearchSuggestionTimeout();
                            }
                    }
                    break;
                }
        }.bind(this);
    
    this.onKeyPressedEvent = function(event)
        {
            if (event.keyCode == 27 && this.getSearchSuggest().style.display != "none") 
                {
                    this.setSuggestionHideTimeout();
                    this.searchBox.value = curText;
                    return false;
                }
        }.bind(this);
    
    this.onFocusLost= function(event)
        {
            this.setSuggestionHideTimeout();
        }.bind(this);
    
    this.onWindowResized = function(event)
        {
            this.getSearchSuggest().style.width = this.searchBox.offsetWidth;
            if (prevWndResizeEventHandler)
                {
            prevWndResizeEventHandler(event);
                }
        }.bind(this);    
     
    this.moveDown = function()
        {

            if (curSize <= 0) return;
            try
                {
                    this.unhighlightCurrentSuggestion();
                    
                    if (curSelection >= curSize - 1)
                        {
                            curSelection = -1;
                        }
                    else
                        {
                            ++curSelection;
                        }
                    
                    this.highlightCurrentSuggestion(true);
                }
            catch(ex)
                {
                }
        };
    
    this.moveUp = function()
        {
            if (curSize <= 0) return;
            try
                {
                    this.unhighlightCurrentSuggestion();
                    
                    if (curSelection < 0)
                        {
                            curSelection = curSize - 1;
                        }
                    else
                        {
                            --curSelection;
                        }
                    
                    this.highlightCurrentSuggestion(true);
                }
            catch(ex)
                {
                }
        };
    
    this.stopEvent = function(event)
        {
            if (isMsie)
                {
                    event.cancelBubble = true;
                }
            else
                {
                    event.preventDefault();
                }
        };

    this.findMatches = function()
        {
            firstSuggestionFoundIndex = -1;
            
            var low = 0;
            var high = data.length -1;
            var mid = -1;
            var dataPrefix ='';
            var curPrefix = curText.toLowerCase() ;
            
            while( low <= high)
                {
                    mid = (low + high) / 2;
                    mid = Math.floor(mid);
                    
                    dataPrefix = (this.data[mid].substr(0,curText.length)).toLowerCase();
                    
                    if(dataPrefix < curPrefix)
                        {
                            low = mid + 1;
                            continue;
                        }
                    
                    if(dataPrefix > curPrefix)
                        {
                            high = mid - 1;
                            continue;
                        }
                    
                    if(dataPrefix == curPrefix)
                        {
                            firstSuggestionFoundIndex = mid;
                            high = mid - 1;
                            continue;
                        }
                }
            
            if(firstSuggestionFoundIndex != -1)
                {
                    var i = firstSuggestionFoundIndex;
                    
                    do{
                        curSuggestions.push(this.data[i]);
                        i = i + 1;
                    }while(this.data[i] && (this.data[i].substr(0,curText.length)).toLowerCase() == curPrefix );
                }        
        };
    
    this.updateSuggestions = function()
        {
            curText = this.searchBox.value;
            
            if (curText.length == 0)
                {
                this.HideSuggestionsDiv();
                return;
                }
        
            this.findMatches();
            this.displaySuggestions();
        };
    
    this.displaySuggestions = function()
        {
            try
                {
                    curSize = Math.min(maxSuggestions, curSuggestions.length);
                }
            catch(e)
                {
                    curSize = 0;
                }

            var ss = this.getSearchSuggest();
            ss.innerHTML = '';
            if (curSize > 0)
                {
                    ss.style.display = "";
                }
            else
                {
                    this.HideSuggestionsDiv();
                }
            
            var sugDivId = '';
            for(i = 0; i < curSize; ++i)
                {
                    sugDivId = sugDivPrefix + i;
                    //This isn't done with DOM as some IE versions do not support dynamically added attributes.
                    var suggest = '<div id="'+ sugDivId + '"';
                    suggest += 'class="suggest_link">' + this.getFormattedSuggestionLine(curSuggestions[i]) + '</div>';
                    ss.innerHTML += suggest;
                }
            
            if (curSize > 0)
                {
                    ss.innerHTML += '<div id="issdivhdr" align="right">&nbsp;</div>';
                }       
            
            for(i = 0; i < curSize; ++i)
                {
                    sugDivId = sugDivPrefix + i;
                    this.attachEventListener(document.getElementById(sugDivId), "mouseover", this.suggestOver);
                    this.attachEventListener(document.getElementById(sugDivId), "mouseout", this.suggestOut);
                    this.attachEventListener(document.getElementById(sugDivId), "click", this.setSearchByIndex);
                }
        };
    
    //Click function
    this.setSearchByIndex = function(input)
        {
            var element = input.id ? input : window.event.srcElement;
            var divId = element.id;
            var index = divId.substr(6);
            
            this.setSearch(curSuggestions[index], index);
            this.getSearchSuggest().innerHTML = '';
            // this.formElement.submit();
        }.bind(this);
    
    //Setting the hidden field's value
    this.setSearch = function(value, indexInList)
        { 
            this.searchBox.value = value;
            if (indexInList >= 0)
                {
                    var valuesIndex = parseInt(firstSuggestionFoundIndex) + parseInt(indexInList);
                    this.hiddenInput.value =  this.values[valuesIndex];
                }
        };
    
    //Mouse over function
    this.suggestOver = function(el, event)
        {
            if(!el.style)
                {
                    el = window.event.srcElement ? window.event.srcElement : window.event.originalTarget ;
                }
            el.style.cursor = "default";
            this.unhighlightCurrentSuggestion();
            divId = el.id;
            curSelection = divId.substr(6);
            this.highlightCurrentSuggestion(false);
        }.bind(this);
    
    //Mouse out function
    this.suggestOut = function(el, event)
        {
            if(!el.style)
                {
                    el = window.event.srcElement ? window.event.srcElement : window.event.originalTarget ;
                }
            this.unhighlightSuggestion(el);
        }.bind(this);
    
    this.highlightSuggestion = function(div_value)
    {
        div_value.className = 'suggest_link_over';
    };
    
    this.unhighlightSuggestion = function(div_value)
    {
        div_value.className = 'suggest_link';
    };
    
    this.highlightCurrentSuggestion = function(updateSearchBox)
        {
            if (updateSearchBox)
                {
                    if (curSelection == -1)
                        {
                            this.setSearch(curText, -1);
                        }
                    else
                        {
                            this.setSearch(curSuggestions[curSelection], curSelection);
                        }
                }
            var selection = document.getElementById(sugDivPrefix +  curSelection);
            this.highlightSuggestion(selection);
        };
    
    this.unhighlightCurrentSuggestion = function()
        {
            var selection = null;
            try
                {
                    selection = document.getElementById(sugDivPrefix + curSelection);
                }
            catch(e) {}
            
            if (selection)
                {
                    this.unhighlightSuggestion(selection);
                }
        };
    
    this.getFormattedSuggestionLine = function(curSuggestion)
        {
            var lowercaseCurrentText = curText.toLowerCase();
            var lowercaseCurrentSuggestion = curSuggestion.toLowerCase();
            var len = curText.length;
            var start = lowercaseCurrentSuggestion.indexOf(lowercaseCurrentText);
            if (start == -1)
                {
                    return curSuggestion;
                }
            
            return curSuggestion.substr(0, start) + "<b>" + curSuggestion.substr(start, len) + "</b>" + curSuggestion.substr(start + len);
        };
    
    this.setSuggestionHideTimeout = function()
        {
            hideDelayTimerId = setTimeout(function() 
             {
              return (function()
               {  
                hideDelayTimerId = null;
                this.HideSuggestionsDiv();
               }); 
             }().bind(this), 300);
        };
    
    this.setSearchSuggestionTimeout = function()
        {
            if (searchSuggestionTimerId)
                {
                    clearTimeout(searchSuggestionTimerId);
                    searchSuggestionTimerId = null;
                }
            searchSuggestionTimerId = setTimeout(function() 
                {
                 return (function()
                  {
                      curSuggestions = [];  
                      this.updateSuggestions();
                      searchSuggestionTimerId = null;
                      curSelection = -1;
                  }); 
                }().bind(this), 100);
        };
    
    this.HideSuggestionsDiv = function()
        {
            curSize = 0;
            
            this.getSearchSuggest().innerHTML = '';
            this.getSearchSuggest().style.display = "none";
            curSelection = -1;
        };
    
    this.findPos = function(obj)
        {
            var curleft = curtop = 0;
            if (obj.offsetParent)
                {
                    do
                        {
                            curleft += obj.offsetLeft;
                            curtop += obj.offsetTop;
                        } while (obj = obj.offsetParent);
                }
            return [curleft,curtop];
        };
    
    this.createSuggestionDiv = function(parent)
        {

	var ip = jQuery("#helpsearch");
	var offset = ip.offset();
	var top = offset.top + ip.height();
        var left = offset.left;

	var suggestionDiv = document.createElement("div");
	suggestionDiv.style.border = "1px solid #5c8295";
	suggestionDiv.style.backgroundColor= "white";
	suggestionDiv.style.color = "black";
	
	suggestionDiv.style.width = parent.offsetWidth;
	suggestionDiv.style.zIndex= "130";
	suggestionDiv.style.display = "none";
	suggestionDiv.id = "srch_sggst";
	var attachTo = parent.parentNode;

        suggestionDiv.style.position = "absolute";
	if (isMsie)
	{
            var gp = attachTo.parentNode;	
	    attachTo = gp;
	}
	attachTo.appendChild(suggestionDiv);
	return suggestionDiv;
        };
    
    //This is created lazily. Make sure you always use the getter.
    this.getSearchSuggest = function()
        {
            if (!suggestDiv)
                {
                    suggestDiv = this.createSuggestionDiv(this.searchBox);
                }
            return suggestDiv;
        };
    
    this.attachEventListener =  function(elemObj, eventType, eventHandler)
        {
            var eventName = "on" + eventType;
            if (elemObj.addEventListener) {
                elemObj.addEventListener(eventType, eventHandler, false);
            } else if (elemObj.attachEvent) {
                elemObj.attachEvent(eventName, eventHandler);
            } else {
                var f = elemObj[eventName];
                elemObj[eventName] = function() {
                    var res1 = f.apply(this, arguments);
                    var res2 = eventHandler.apply(this, arguments);
                    if (res1 == undefined) {
                        return res2;
                    } else {
                        if (res2 == undefined) {
                            return res1;
                        } else {
                            return res2 && res1;
                        }
                    }
                };
            }
            
        };    
}//end searchSuggestor

