var KEY_TIMER_DELAY = 250;

function ComboBox(Textbox, SuggestionProvider)
{
  this.CurrentSuggestionIndex = -1;
  this.DropDown = null;
  this.SuggestionProvider = SuggestionProvider;
  this.Textbox = Textbox;
  this.TimeoutID = null;
  this.UserText = Textbox.value;
  this.SavedWindowOnLoad = window.onload;
  var Self = this;
  window.onload = function() {
    Self.Init();
    if(Self.SavedWindowOnLoad)
      Self.SavedWindowOnLoad();
  }
}

ComboBox.prototype.AutoSuggest = function (Suggestions, TypeAhead)
{
  this.CurrentSuggestionIndex = -1;
  this.UserText = this.Textbox.value;
  if(Suggestions.length>0)
  {
    if(TypeAhead)
      this.TypeAhead(Suggestions[0]);
    this.ShowSuggestions(Suggestions);
  }
  else
    this.ShowHideSuggestions(false);
};

ComboBox.prototype.CreateDropDown = function ()
{
  this.DropDown = document.createElement('div');
  this.DropDown.className = 'suggestions';
  this.DropDown.style.visibility = 'hidden';
  this.DropDown.style.width = this.Textbox.offsetWidth;
  document.body.appendChild(this.DropDown);
  var Self = this;
  this.DropDown.onmousedown = function (Event)
  {
    Event = Event || window.event;
    Target = Event.target || Event.srcElement;
    Self.Textbox.value = Target.firstChild.nodeValue;
    Self.Textbox.focus();
    Self.ShowHideSuggestions(false);
    if(Event.preventDefault)
      Event.preventDefault();
  };
  this.DropDown.onmouseover = function (Event)
  {
    Event = Event || window.event;
    Target = Event.target || Event.srcElement;
    Self.HighlightSuggestion(Target);
  };
  this.DropDown.style.left = GetPosX(this.Textbox) + 'px';
  this.DropDown.style.top = (GetPosY(this.Textbox)+this.Textbox.offsetHeight) + 'px';
};

ComboBox.prototype.GoToSuggestion = function (Diff)
{
  var SuggestionNodes = this.DropDown.childNodes;
  if(SuggestionNodes.length>0)
  {
    var Node = null;
    if (Diff>0)
      if(this.CurrentSuggestionIndex<SuggestionNodes.length-1)
        Node = SuggestionNodes[++this.CurrentSuggestionIndex];
      else
        Node = null;
    else
      if(this.CurrentSuggestionIndex>0)
        Node = SuggestionNodes[--this.CurrentSuggestionIndex];
      else if(this.CurrentSuggestionIndex==-1)
        Node = SuggestionNodes[this.CurrentSuggestionIndex = SuggestionNodes.length-1];
      else
        Node = null;
    this.ShowHideSuggestions(true);
    this.HighlightSuggestion(Node);
    if(Node)
      this.Textbox.value = Node.firstChild.nodeValue;
    else
    {
      this.Textbox.value = this.UserText;
      SelectTextboxRange(this.Textbox, this.UserText.length, this.UserText.length);
    }
  }
};

ComboBox.prototype.HandleKeyDown = function (Event)
{
  switch(Event.keyCode)
  {
    case 38: // up arrow
      this.GoToSuggestion(-1);
      break;
    case 40: // down arrow
      this.GoToSuggestion(1);
      break;
    case 27: // esc
      this.Textbox.value = this.UserText;
      SelectTextboxRange(this.Textbox, this.UserText.length, 0);
      /* falls through */
    case 13: // enter
      if(this.DropDown.style.visibility=="visible")
      {
        this.ShowHideSuggestions(false);
        Event.returnValue = false;
        if(Event.preventDefault)
          Event.preventDefault();
      }
      break;
    default: // other key
      if(Event.keyCode < 32 || (Event.keyCode >= 33 && Event.keyCode < 46) || (Event.keyCode >= 112 && Event.keyCode <= 123))
      {
        // ignore
      }
      else
        this.ShowHideSuggestions(false);
      break;
  }
};

ComboBox.prototype.HandleKeyUp = function (Event)
{
  this.SuggestionProvider.Abort();
  var KeyCode = Event.keyCode;
  var RequestTypeAhead = null;
  if(this.CurrentSuggestionIndex==-1)
    this.UserText = this.Textbox.value;
  // for backspace (8) and delete (46), shows suggestions without typeahead
  if (KeyCode == 8 || KeyCode == 46)
    RequestTypeAhead = false;
  // make sure not to interfere with non-character keys
  else if (KeyCode < 32 || (KeyCode >= 33 && KeyCode < 46) || (KeyCode >= 112 && KeyCode <= 123))
  {
    // ignore
  }
  else
    // request suggestions from the suggestion provider with typeahead
    RequestTypeAhead = true;
  if(RequestTypeAhead!=null)
  {
    clearTimeout(this.TimeoutID);
    var Self = this;
    this.TimeoutID = setTimeout( function () {
              Self.SuggestionProvider.RequestSuggestions(Self, RequestTypeAhead);
            }, KEY_TIMER_DELAY);
  }
};

ComboBox.prototype.ShowHideSuggestions = function (Visible)
{
  if(Visible)
    this.DropDown.style.visibility = "visible";
  else
    this.DropDown.style.visibility = "hidden";
};

ComboBox.prototype.HighlightSuggestion = function (SuggestionNode)
{
  this.CurrentSuggestionIndex = -1;
  for (var i = 0; i<this.DropDown.childNodes.length; i++)
  {
    var Node = this.DropDown.childNodes[i];
    if(Node==SuggestionNode)
    {
      Node.className = "current";
      this.CurrentSuggestionIndex = i;
    }
    else //???shouldn't browser handle this? if (Node.className=="current")
      Node.className = "";
  }
};

ComboBox.prototype.Init = function ()
{
  var Self = this;
  this.Textbox.onkeyup = function (Event)
  {
    if(!Event)
      Event = window.event;
    Self.HandleKeyUp(Event);
  };
  this.Textbox.onkeydown = function (Event)
  {
    if(!Event)
      Event = window.event;
    Self.HandleKeyDown(Event);
  };
  this.Textbox.onblur = function () {
    Self.ShowHideSuggestions(false);
  };
  this.CreateDropDown();
};

ComboBox.prototype.ShowSuggestions = function (Suggestions)
{
  var Div = null;
  this.DropDown.innerHTML = "";
  for(var i = 0; i<Suggestions.length; i++)
  {
    Div = document.createElement("div");
    Div.appendChild(document.createTextNode(Suggestions[i]));
    this.DropDown.appendChild(Div);
  }
  this.ShowHideSuggestions(true);
};

ComboBox.prototype.TypeAhead = function (Suggestion)
{
  if(this.Textbox.createTextRange || this.Textbox.setSelectionRange)
  {
    if(this.Textbox.value==Suggestion)
      return;
    var UserTextLength = this.Textbox.value.length;
    this.Textbox.value = this.Textbox.value+Suggestion.substr(UserTextLength);
    SelectTextboxRange(this.Textbox, UserTextLength, Suggestion.length);
  }
};
