var max_list_size = 20;
var MIN_LENGTH_AUTO_TEXT_CHECK = 2;

// AutoComplete Object creator to be called directly from HTML
    
function createAutoComplete(text_box_id, div_id, form_id) {
    var aNames = [];

    var input  = $(text_box_id);
    var div_id = $(div_id);
    var oForm  = $(form_id);    
        
    new AutoComplete(
        aNames,
        input,
        div_id,
        oForm,
        max_list_size
    );
}

/* The AutoCompleteDB Class   */
    
// AutoComplete DB Constructor

function AutoCompleteDB() {
    // set initial values.
    this.bEnd = false;
    this.nCount = 0;
    this.aObj = new Object;
}

// AutoComplete Member methods

AutoCompleteDB.prototype.processStrings = function(aStrObjs) {
    if ( ! aStrObjs ) return null;
    var i, n = aStrObjs.length;
    for ( i = 0; i < n; i++ )
    {
        var tmpObj = new Object();
        tmpObj['html'] = aStrObjs[i];
        tmpObj['id']   = aStrObjs[i];
        tmpObj['text'] = aStrObjs[i];
        this.add(tmpObj,false);
    }
}

AutoCompleteDB.prototype.processStringCount = function(count,letter) {
    if ( !this.aObj[letter] ) {
        this.aObj[letter] = new AutoCompleteDB();
        this.aObj[letter].nCount = count;
    }
}        
    
AutoCompleteDB.prototype.add = function(obj, bNewString) {
    var html = obj['html'];
    var id   = obj['id'];
    var text = obj['text'];

    // if at the end of the string, flag this node as an end point.
    if ( text == "" ) {
        this.html = html;
        this.id   = id;
        this.bEnd = true;
    }
    else {
        // otherwise, pull the first letter off the string
        var letter = text.substring(0,1);
        var rest   = text.substring(1,text.length);
        var new_obj = new Object();
        new_obj['html'] = html;
        new_obj['id']   = id;
        new_obj['text'] = rest;
                
        // and either create a child node for it or reuse an old one.
        var created = false;
        if ( !this.aObj[letter] ) {
            this.aObj[letter] = new AutoCompleteDB();
            created = true;
        }
        bNewString = this.aObj[letter].add(new_obj,created);
    }

    if ( bNewString ) this.nCount++;

    return bNewString;
}

AutoCompleteDB.prototype.getCount = function(str, bExact, prefix) {
    // if end of search string, return number
    if ( str == "" )
        if ( this.bEnd && bExact && (this.nCount == 1) ) return 0;
        else return this.nCount;
    
    // otherwise, pull the first letter off the string
    var letter = str.substring(0,1);
    var rest   = str.substring(1,str.length);
    prefix += letter;

    // and look for case-insensitive matches
    var nCount = 0;
    var lLetter = letter.toLowerCase();
    var uLetter = letter.toUpperCase();

    if ( !this.aObj[lLetter] && !this.aObj[uLetter] && ( prefix == letter )) {
        var list = load_location_list(letter,this);
    }
    
    if ( this.aObj[lLetter] ) {
        nCount += this.aObj[lLetter].getCount(rest,bExact && (letter == lLetter),prefix);
    }

    // In case this is a space or some char that doesn't have up/low case we
    //  drop out here...
    if ( lLetter == uLetter ) return nCount;
    if ( this.aObj[uLetter] )
        nCount += this.aObj[uLetter].getCount(rest, bExact && (letter == uLetter),prefix);
    
    return nCount;
}

AutoCompleteDB.prototype.getObjs = function(str1, str2, outObjs) {
    if ( str1 == "" )
    {
        // add matching strings to the array
        if ( this.bEnd )
            outObjs.push(this);
        
        // get strings for each child node
        for ( var i in this.aObj )
            this.aObj[i].getObjs(str1, str2 + i, outObjs);
    }
    else
    {
        // pull the first letter off the string
        var letter = str1.substring(0,1);
        var rest   = str1.substring(1,str1.length);
        
        // and get the case-insensitive matches.
        var lLetter = letter.toLowerCase();
        var uLetter = letter.toUpperCase();

        // In case this is a space or some char that doesn't have up/low case we
        //  drop out here...  (added ' rws / funnelscope)
        if ( lLetter == uLetter && lLetter != "'" ) {
            this.aObj[letter].getObjs(rest, str2 + letter, outObjs);
        }
        else {
            if ( this.aObj[lLetter] )
                this.aObj[lLetter].getObjs(rest, str2 + lLetter, outObjs);
            
            if ( this.aObj[uLetter] && uLetter != "'" )
                this.aObj[uLetter].getObjs(rest, str2 + uLetter, outObjs);
        }
    }
} 

/*  The AutoComplete Class   */
    
// AutoComplete constructor
    
function AutoComplete(aStrObjs, oText, oDiv, oForm, nMaxSize) {
    // initialize member variables
    this.oText = oText; // the text box
    this.oDiv  = oDiv;  // a hidden <div> for the popup auto-complete
    this.nMaxSize = nMaxSize;
    this.currentObj     = new Object();
    this.oForm = oForm;

    // preprocess the texts for fast access
    this.db = new AutoCompleteDB();
    this.db.processStrings(aStrObjs);
        
    // attach handlers to the text-box
    oText.AutoComplete = this;
    oText.observe('keyup', AutoComplete.prototype.onKeyup);
    //This breaks IE 8
    oText.observe('keydown', AutoComplete.prototype.onKeydown);
    oText.observe('blur',  AutoComplete.prototype.onTextBlur);
}

// AutoComplete Event Handlers

AutoComplete.prototype.onTextBlur = function() {
    if ( this.AutoComplete.oDiv.style.visibility != "hidden" ) {
        this.AutoComplete.setLocation();
        this.AutoComplete.onblur();
    }
}

AutoComplete.prototype.onblur = function(e) {
    this.oDiv.innerHTML        = "";
    this.oDiv.style.visibility = "hidden";
}

AutoComplete.prototype.onDivMouseDown = function() {
    this.AutoComplete.setLocation();
}

AutoComplete.prototype.onDivMouseOver = function() {
    this.AutoComplete.setCurrentRow(this);
}

AutoComplete.prototype.onDivMouseOut = function() {
    // do nothing
}

AutoComplete.prototype.clearLocation = function() {
    this.oText = "";
}

AutoComplete.prototype.clearCurrentObject = function() {
    this.currentObj = new Object();
}
    
AutoComplete.prototype.setLocation = function() {
    if ( !this.currentObj ) return;
    this.oText.value    = this.getCurrentText();
    //this.clearCurrentObject();
}

AutoComplete.prototype.submit = function() {
    init_page_load();
    this.oForm.submit();
}
    
AutoComplete.prototype.onKeyup = function(e) {
    
    switch (e.keyCode)
    {
        // Return/Enter
        case Event.KEY_RETURN:
            this.AutoComplete.stopEvent(e);
            this.AutoComplete.setLocation();
            this.AutoComplete.onblur();
            this.AutoComplete.submit();
            break;
        case Event.KEY_RIGHT:
            this.AutoComplete.stopEvent(e);
            this.AutoComplete.setLocation();
            this.AutoComplete.onblur();
        break;
        // Escape
        case Event.KEY_ESC:
            this.AutoComplete.onblur();
            break;
        // Up arrow
        case Event.KEY_UP:
            this.AutoComplete.stopEvent(e);
            this.AutoComplete.decrementCurrentRow();
            break;
        // Down arrow
        case Event.KEY_DOWN:
            this.AutoComplete.stopEvent(e);
            this.AutoComplete.incrementCurrentRow();
            break;
        default:
            this.AutoComplete.onchange();
    }
    
    //sets the return value of the event
    //(if false, the action is canceled).
    e.returnValue = false;
    
    return true;
}

// for onKeydown events we just need to stop normal propagation for special
//  keys.
AutoComplete.prototype.onKeydown = function(e) {
        
    switch (e.keyCode)
    {
        // Return/Enter
        case Event.KEY_RETURN:
        Event.stop(e);
        e.returnValue = false;
        break;
    }
    
    return true;
}
    
AutoComplete.prototype.stopEvent = function(e) {
    Event.extend(e);
    Event.stop(e);
}
    
// AutoComplete member methods
    
AutoComplete.prototype.onchange = function() {
    // tweaked to remove leading spaces rws/funnelscope
    var txt = this.oText.value.replace(/^\s+/, '');
    
    // count the number of strings that match the text-box value.
    if ( txt.length >= MIN_LENGTH_AUTO_TEXT_CHECK )
        var nCount = this.db.getCount(txt,false,'');
    if ( this.getCurrentRow() ) this.clearCurrentRow();
    
    // if a suitable number then show the popup-div
 // if ( (this.nMaxSize == -1 ) || ((nCount < this.nMaxSize) && (nCount > 0)) )
    if ( (this.nMaxSize == -1 ) || (nCount > 0) )
    {
        // clear the popup div.
        while ( this.oDiv.hasChildNodes() )
            this.oDiv.removeChild(this.oDiv.firstChild);
        
        // get all the matching strings from the AutoCompleteDB
        var aObj = new Array();
        this.db.getObjs(txt, "", aObj);
        
        // add each string to the popup-div
        var i, n = aObj.length;
        n = (n > this.nMaxSize) ? this.nMaxSize : n; 
        for ( i = 0; i < n; i++ )
        {
            var oDiv = document.createElement('div');
            this.oDiv.appendChild(oDiv);
            oDiv.innerHTML = aObj[i]['html'];
            oDiv.onmousedown = AutoComplete.prototype.onDivMouseDown;
            oDiv.onmouseover = AutoComplete.prototype.onDivMouseOver;
            oDiv.onmouseout = AutoComplete.prototype.onDivMouseOut;
            oDiv.AutoComplete = this;
            if ( !this.getCurrentRow() )
                this.setCurrentRow(oDiv);
        }
        this.oDiv.style.visibility = "visible";
    }
    else // hide the popup-div
    {
        this.oDiv.innerHTML = "";
        this.oDiv.style.visibility = "hidden";
    }
}

AutoComplete.prototype.getCurrentText = function() {
    if ( this.currentObj && this.currentObj['text'] ) {
        return this.currentObj['text'];
    }
    return "";
}

AutoComplete.prototype.getCurrentId = function() {
    if ( this.currentObj )
        return this.currentObj['id'];

    return "";
}

AutoComplete.prototype.getCurrentRow = function() {
    if ( this.currentObj )
        return this.currentObj['row'];

    return "";
}

AutoComplete.prototype.getCurrentHtml = function() {
    if ( this.currentObj )
        return this.currentObj['html'];

    return "";
}
    
AutoComplete.prototype.clearCurrentRow = function() {
    this.currentObj['row'].className = "AutoCompleteBackground";
    this.currentObj['id']   = null;
    this.currentObj['row']  = null;
    this.currentObj['text'] = null;
}

AutoComplete.prototype.setCurrentRow = function(divElement) {
    if ( this.getCurrentRow() )
        this.clearCurrentRow();
    
    divElement.className = "AutoCompleteHighlight";
    this.currentObj['row']  = divElement;
    this.currentObj['id']   = divElement.innerHTML;
    this.currentObj['text'] = divElement.innerHTML;
}

AutoComplete.prototype.incrementCurrentRow = function() {    
    // The element should already be extended, but well...IE
    if ( !this.currentObj['row'] ) return;
    var cur_row = Element.extend(this.currentObj['row']);
    var new_row = cur_row.next('div');
    if ( new_row ) {
        this.setCurrentRow(new_row);
    }
}

AutoComplete.prototype.decrementCurrentRow = function() {    
    // The element should already be extended, but well...IE
    if ( !this.currentObj['row'] ) return;
    var cur_row = Element.extend(this.currentObj['row']);
    var new_row = cur_row.previous('div');
    if ( new_row ) {
        this.setCurrentRow(new_row);
    }
}


