var _searchForm = null;
var _searchField = null;
var _searchField2 = null;
var _searchButton = null;
var _searchExch = null;

var _cache = new Object();
var _lastKey = -1;
var _supportsXML = false;
var _xmlHttp = null;
var _timeout = 0;
var _oldText = "";
var _currentText = "";
var _eventKeyCode = 0;
var _cursorPressed = false;
var _widthPadding = 0;

var _divTag = "DIV";
var _spanTag = "SPAN";
var _cursorPressed = false;

var _acDiv = null;
var _acElement = null;
var _acDivRowcount = -1;
var _acDivList = null;
var _sideWidth = 1;
var _topWidth = 1; 
var _columnDivider = 30; // percentage of space to allocate to shortname/longname
var _currentHighlightIndex = -1;
var _currentHighlightDiv = null; 

//
// key functions
//

// our "constructor"... to init all the variables
// and to set all the hooks/handlers properly
HookAC = function (oForm, oInput, oInput2, oExch, oSubmit, oPadding, colDivider) {
  _searchForm = oForm;
  _searchField = oInput;
  _searchField2 = oInput2;
  _searchExch = oExch;
  _searchButton = oSubmit;
  if (!colDivider) {
    colDivider = 30;
  };
  _columnDivider = colDivider;
  if (oPadding) { _widthPadding = oPadding; }
  
  _supportsXML = !(!getXMLHttpObject());

  _searchForm.onsubmit = searchformSubmit;
  _searchField.autocomplete = "off";
  _searchField.onblur = OnBlurHandler;
  if(_searchField.createTextRange) {
    _searchField.onkeyup = new Function("return OnKeyUpHandler(event);");
  } else {
    _searchField.onkeyup = OnKeyUpHandler;
  };
  _searchField.onsubmit = searchformSubmit;
  
  _currentText = _searchField.value;
  _oldText = _currentText;
  
  _acDiv = document.createElement(_divTag);
  _acDiv.id = "autocompleteDiv";
  _acDiv.style.borderRight = "black " + _sideWidth + "px solid";
  _acDiv.style.borderLeft = "black "+ _sideWidth + "px solid";
  _acDiv.style.borderTop = "black " + _topWidth + "px solid";
  _acDiv.style.borderBottom ="black " + _topWidth + "px solid";
  _acDiv.style.zIndex = "1";
  _acDiv.style.paddingRight = "0";
  _acDiv.style.paddingLeft = "0";
  _acDiv.style.paddingTop = "0";
  _acDiv.style.paddingBottom = "0";
  setCompleteDivSize();
  _acDiv.style.visibility = "hidden";
  _acDiv.style.position = "absolute";
  _acDiv.style.backgroundColor = "white";
  _acDiv.style.cursor = "pointer";
  document.body.appendChild(_acDiv);
  setStyleForElement(_acDiv, "mAutoComplete");

  var s = document.createElement("DIV");
  s.style.visibility = "hidden";
  s.style.position = "absolute";
  s.style.left = "-10000";
  s.style.top = "-10000";
  s.style.width ="0";
  s.style.height= "0";
  var M = document.createElement("IFRAME");
  M.completeDiv = _acDiv;
  M.name = "completionFrame";
  M.id = "completionFrame";
  s.appendChild(M);
  document.body.appendChild(s);
  
//  autoCache("", new Array(), new Array());
  _acElement = M;
  document.onkeydown = keyDownHandler;
  addResizeEvent(resizeHandler);
//  window.onresize = resizeHandler;
};

// populate our auto complete DIV with the child elements.
// All data will be populated with proper HTML/CSS formatting as well  
function displaySuggestions (targetElement, shortnameList, longnameList) {
  // clear previous divs
  while (targetElement.childNodes.length > 0) {
    targetElement.removeChild(targetElement.childNodes[0]);
  };
  // recreate new divs
  for (var i = 0; i < shortnameList.length; i++) {
    var mainHolder = document.createElement(_divTag);
    var kSpan = document.createElement(_spanTag);
    var shortName = document.createElement(_spanTag);
    var longName = document.createElement(_spanTag);   
    setStyleForElement(mainHolder, "aAutoComplete");
    // hook the mouse handlers
    mainHolder.onmousedown = acMouseDown;
    mainHolder.onmouseover = acMouseOver;
    mainHolder.onmouseout = acMouseOut;
    // set our style
    setStyleForElement(kSpan, "lAutoComplete");
    setStyleForElement(shortName, "cAutoComplete");
    setStyleForElement(longName, "dAutoComplete");
    // populate
    shortName.innerHTML = htmlencode(shortnameList[i]);
    longName.innerHTML = htmlencode(longnameList[i]);
    mainHolder.displaySpan = longName;
    // reposition
    kSpan.appendChild(longName);
    kSpan.appendChild(shortName);
    mainHolder.appendChild(kSpan);
    targetElement.appendChild(mainHolder); 
  };
};

// the script that calls the auto complete query php file
// which returns back a javascript statement to be run
function serverCall (stock) {
  // cancels previous call(s), if any
  if (_xmlHttp && _xmlHttp.readyState != 0) {
    _xmlHttp.abort();
  };
  _xmlHttp = getXMLHttpObject();
  if (_xmlHttp) {
    _xmlHttp.open('Get', './ss/irRPC.php?q=' + stock + '&exch=' + _searchExch.value, true);
    _xmlHttp.onreadystatechange = function () {
      // readyState values:
      // 0 - uninitialised
      // 1 - loading (send hasn't been called)
      // 2 - loaded but header not yet received (send called)
      // 3 - interactive (partial data received)
      // 4 - complete (all data available in the responseText property)
      if (_xmlHttp.readyState == 4 && _xmlHttp.responseText) {
        if ((_xmlHttp.responseText.charAt(0) == "<") || (_xmlHttp.responseText.charAt(0) == "")) {
          // perhaps the server timed out
          _timeout--; 
        } else {
          // process the data
          eval(_xmlHttp.responseText);
        };
      };
    };
    // send the request to server
    _xmlHttp.send(null);
  };
};

// the main loop which gets called at every timeout interval
mainLoop = function () {
  if (!_cursorPressed) {
    if (_oldText != _currentText) {
      var validText = escapeURI(_currentText);
      // search cache first
      var cacheResult = _cache[_currentText];
      if (cacheResult) {
        // save on the round trip to server
        rpcCallback(_acElement, _currentText, cacheResult[0], cacheResult[1], new Array(''));
      } else {
        _timeout++;
        if (_supportsXML) {
          serverCall(_currentText);  
        };
      };
      setInputFocus(); 
    };
  };
  _oldText = _currentText;
  setTimeout("mainLoop()", recalculateTimeout(_timeout));
  return true;
};
setTimeout("mainLoop()", 10);

function recalculateTimeout(Mb){
  return 50;
  // can be used later to calculate the latency and time it so that
  // it doesn't jam up our server
};

//
// event handlers
//
OnKeyUpHandler = function (e) {
  _eventKeyCode = e.keyCode;
  _cursorPressed = false;
  ProcessKeys();
};

// hide the autocomplete DIV when input leaves the text field
OnBlurHandler = function (e) {
  if (!_cursorPressed) {
    hideAC();
  };
};

var acMouseDown = function () {
  // get the value
  _searchField.value = htmldecode(getSpanByName(this, "cAutoComplete"));
  _searchField2.value = htmldecode(getSpanByName(this, "dAutoComplete"));
  // submit!
  searchformSubmit();  
};

var acMouseOver = function () {
  if(_currentHighlightDiv) {
    setStyleForElement(_currentHighlightDiv, "aAutoComplete");
  };
  setStyleForElement(this, "bAutoComplete");
};

var acMouseOut = function () {
  setStyleForElement(this, "aAutoComplete");
};     

function ProcessKeys() {
  // for up and down cursor keys, we highlight the next/prev selection,
  // then return back focus to the text box
  
  // 20050412 (Chee Meng)
  // added the following lines to replace
  //    _currentText = _searchField.value
  // because otherwise the RPC engine will erroneously
  // search for the entire auto-suggested term instead
  // of only what is typed by the user
  var sText = new String(_searchField.value);
  _currentText = sText.substr(0, UserTextLength(_searchField)); 
  
  if (_eventKeyCode == 40 || _eventKeyCode == 38) {
    _cursorPressed = true;
    _searchField.blur();
    setTimeout("setInputFocus();", 10);
  
    // if autocomplete box is not visible, no sense showing anything
    if (!isACVisible()) { return; };
  
    if (_eventKeyCode == 40) {
      doHighlight(_currentHighlightIndex + 1);
      return;
    } else if (_eventKeyCode == 38) {
      doHighlight(_currentHighlightIndex - 1);
      return;
    };
  } else {
    _cursorPressed = false;
      
    // now we determine whether or not to highlight potential 'answer',
    // or to hide the suggestion box completely
    _currentHighlightIndex = -1;
    var rowContainer = _acDiv.getElementsByTagName(_divTag);
    var rowCount = rowContainer.length;
    _acDivRowcount = rowCount;
    _acDivList = rowContainer;
  
    if (_currentText == "" || rowCount == 0) {
      hideAC();
      return;
    } else {
      showAC();
    };
  
    // reset everyone's highlight first before we highlight the new suggestion
    for (var i = 0; i < rowCount; i++) {
      setStyleForElement(rowContainer.item(i), "aAutoComplete");
    };

    var _userText = _currentText;
    var _suggestedText = htmldecode(getSpanByName(_acDivList.item(0), "cAutoComplete"));
    // look for a match
    var _initialMatch = _suggestedText.indexOf(_userText.toUpperCase()) == 0;
    if (_initialMatch && _eventKeyCode != 8 && _eventKeyCode != 46) { // 46 is delete
      // then we auto complete and leave it highlighted
      doHighlight(0);
      var _oldLength = _userText.length;
      // if it's not the full text
      if (_userText != _suggestedText) {
        if (_searchField.createTextRange) {
          // IE
          _searchField.value = _suggestedText;
          var _selection = _searchField.createTextRange();
          _selection.moveStart("character", _oldLength);
          _selection.select();
        } else if (_searchField.setSelectionRange) {
          // Mozilla
          _searchField.value = _suggestedText;
          _searchField.setSelectionRange(_oldLength, _suggestedText.length);
        };
      };
    };
  };
};

// auto-called when the auto complete PHP script returns the value
rpcCallback = function (frameElement, keyText, shortnameList, longnameList, unused) {
  if (!frameElement) {
    frameElement = _acElement;
  };
//  autoCache(keyText, shortnameList, longnameList);
  var b = frameElement.completeDiv;
  b.shortnameList = shortnameList;
  b.longnameList = longnameList;
  displaySuggestions(b, b.shortnameList, b.longnameList);
  ProcessKeys();
  if (_acDivRowcount > 0) {
    showAC();
  } else {
    hideAC();
  };
};

function resizeHandler(){
  setCompleteDivSize();
};

function keyDownHandler (event) {
  if (!event && window.event) {
    event = window.event;
  };
  if (event) {
    _lastKeyCode = event.keyCode;
  };
};

function doHighlight(hIndex) {
  // if out of range, or no auto complete div is created, we exit
  if (!_acDivList || _acDivRowcount <= 0) {
    return;
  };

  if (hIndex >= _acDivRowcount) {
    hIndex = _acDivRowcount - 1;
  };
  if (_currentHighlightIndex != -1 && hIndex != _currentHighlightIndex) {
    // reset previous highlight, if any
    setStyleForElement(_currentHighlightDiv, "aAutoComplete");
    _currentHighlightIndex = -1;
  };
  
  if (hIndex < 0) {
    _currentHighlightIndex = -1;
    _searchField.focus();
    return;
  };
  
  _currentHighlightIndex = hIndex;
  _currentHighlightDiv = _acDivList.item(hIndex);
  setStyleForElement(_currentHighlightDiv, "bAutoComplete");
  
  if (_cursorPressed) {
    _searchField.value = htmldecode(getSpanByName(_currentHighlightDiv, "cAutoComplete"));
  };
};

// highlights the portion of code that is auto-filled
// by our autocomplete, so that when the user types in
// new letter(s), the suggested portion is auto-overwritten

function SuggestedPortionLength (inputBox) {
  var textLength = -1;
  if (inputBox.createTextRange) {
    var tempRange = document.selection.createRange().duplicate();
    textLength = tempRange.text.length;
  } else if (inputBox.setSelectionRange) {
    textLength = inputBox.selectionEnd - inputBox.selectionStart;
  };
  return textLength;  
};

function UserTextLength (inputBox) {
  var textLength = 0;
  if (inputBox.createTextRange) {
    var tempRange = document.selection.createRange().duplicate();
    tempRange.moveEnd("textedit", 1);
    textLength = inputBox.value.length - tempRange.text.length;
  } else if (inputBox.setSelectionRange) {
    textLength = inputBox.selectionStart;
  } else {
    textLength = -1;
  };
  return textLength;
};

//
// helper functions for object creation
//

// gets the content of an element based on its tag name
function getSpanByName(parentElement, className) {
  if (!parentElement) {
    return null;
  };
  var childElement = parentElement.getElementsByTagName(_spanTag);
  if (childElement) {
    for (var i = 0; i < childElement.length; i++) {
      if (childElement[i].className == className) {
        return childElement[i].innerHTML;
      };
    };
  };
};

// returns a valid URI with all the necessary characters escaped
function escapeURI (uriAddress) {
  if (encodeURIComponent) {
    return encodeURIComponent(uriAddress);
  };
  if (escape) {
    return escape(uriAddress);
  };
};

function unescapeURI (uriAddress) {
  if (decodeURIComponent) {
    return decodeURIComponent(uriAddress);
  };
};

function htmlencode (t) {
  return t.toString().replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
};

function htmldecode (t) {
    return t.toString().replace(/&amp;/g,"&").replace(/&quot;/g,"\"").replace(/&lt;/g,"<").replace(/&gt;/g,">");
};

function setStyleForElement(c, name){
  c.className = name;
  switch (name.charAt(0)) {
    case "m":
      c.style.fontSize  = "13px";
      c.style.fontFamily = "arial,sans-serif";
      c.style.wordWrap = "break-word";
      break;
    case "l":
      c.style.display = "block";
      c.style.paddingLeft = "3";
      c.style.paddingRight = "3";
      c.style.height = "16px";
      c.style.overflow = "hidden";
      break;
    case "a":
      c.style.backgroundColor = "white";
      c.style.textAlign = "left";
      c.style.color = "black";
      if (c.displaySpan) {
        c.displaySpan.style.color = "green";
      }
      break;
    case "b":
      c.style.backgroundColor = "#3366cc";
      c.style.color = "white";
      c.style.textAlign = "left";
      if (c.displaySpan) {
        c.displaySpan.style.color = "white";
      }
      break;
    case "c":
      c.style.width = _columnDivider + "%";
      c.style.cssFloat = "left";
      c.style.textAlign = "left";
      break;
    case "d":
      c.style.cssFloat = "right";
      c.style.styleFloat = "right";
      c.style.width = 100 - _columnDivider + "%";
      c.style.fontSize = "10px";
      c.style.textAlign = "right";
      c.style.color = "navy";
      c.style.paddingTop = "3px";
      break;
  };
};

// toggle visibility of auto complete layer
function hideAC(){
  document.getElementById("autocompleteDiv").style.visibility = "hidden";
};

function showAC(){
  document.getElementById("autocompleteDiv").style.visibility = "visible";
  setCompleteDivSize();
};

function isACVisible() {
  return (document.getElementById("autocompleteDiv").style.visibility == "visible");
};

// simple caching mechanism
function autoCache(keyword, shortnameList, longnameList) {
  _cache[keyword] = new Array(shortnameList, longnameList);  
};

setInputFocus = function () {
  _searchField.focus();
};

setSubmitFocus = function () {
  _searchButton.focus();
};

function searchformSubmit() {
  hideAC();
  _searchField.focus();

//  getStockCode(encodeURI(_searchField.value));
//  _searchField.value = encodeURI(_searchField.value);
  _searchForm.submit();
  return true;
};

function setCompleteDivSize(){
  if(_acDiv){
    _acDiv.style.left = calculateOffsetLeft(_searchField) + "px";
    _acDiv.style.top = calculateOffsetTop(_searchField) + _searchField.offsetHeight - 1 + "px";
    _acDiv.style.width = calculateWidth() + "px";
  };
};

// calculate width of inputField... Note browser specific adjustments...
function calculateWidth(){
  if(navigator && navigator.userAgent.toLowerCase().indexOf("msie") == -1) {
    return _widthPadding + _searchField.offsetWidth - _sideWidth * 2;
  } else {
    return _widthPadding + _searchField.offsetWidth;
  };
};

function calculateOffsetLeft(r){
  return GetAttr(r, "offsetLeft");
};

function calculateOffsetTop(r){
  return GetAttr(r, "offsetTop");
};

function GetAttr(r, attr){
  var kb = 0;
  while (r){
    kb += r[attr]; 
    r = r.offsetParent;
  };
  return kb;
};