var Autocomplete = Class.create({
  initialize: function(element,array,options){
    this.setOptions(options);
    if(!element) {
      this.element = new Element('input',{'type':'hidden'});
      $(document.body).insert(this.element);
    } else {
      this.element = (element);
    }
    this.listArray = array;
    this.prepareTagger();
  },
  prepareTagger: function() {
    this.update = new Element('div',{'class':this.options.className}).hide();
    Element.insert(this.element,{after:this.update});
    this.prepareIE();
  },
  moveToElement: function(new_element) {
    this.hideTagger();
    this.element = $(new_element);
    this.setPosition();
    this.showTagger();
    this.element.setAttribute('autocomplete','off');
  },
  showTagger: function(event) {
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 1);
    this.registerTaggerEvents();
    this.setPosition();
  },
  hideTagger: function() {
    this.active = false;
    if(this.iefix) this.iefix.hide();
    this.unregisterTaggerEvents();
    this.update.hide();
  },
  registerTaggerEvents: function() {
    this.mouseDownEvent = this.onMouseDown.bindAsEventListener(this);
    this.keyDownEvent = this.onKeyDown.bindAsEventListener(this)
    Event.observe(document, "mousedown", this.mouseDownEvent);
    Event.observe(this.element,'keydown',this.keyDownEvent);
  },
  unregisterTaggerEvents: function() {
    Event.stopObserving(document, "mousedown", this.mouseDownEvent);
    Event.stopObserving(this.element,'keydown',this.keyDownEvent);
  },
  setPosition: function() {
    Position.clone(this.element, this.update, {setHeight: false,setWidth:false,offsetTop: this.element.getHeight()});
  },
  prepareIE: function() {
    if(!this.iefix && (Prototype.Browser.IE) && (Element.getStyle(this.update, 'position')=='absolute')) {
      new Insertion.After(this.update,
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;width:0;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
  },
  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(true)});
    this.iefix.setStyle({'zIndex':'1'});
    this.update.setStyle({'zIndex':'2'});
    this.iefix.show();
  },
  onMouseDown: function(event) {
    var x = Event.pointerX(event);
    var y = Event.pointerY(event);
    if(!Position.within(this.update,x,y) && Event.element(event) != this.element) this.hideTagger();
  },
  onKeyDown: function(event) {
    switch(event.keyCode) {
      case Event.KEY_RIGHT:
      case Event.KEY_RETURN:
        if(this.active) {
          this.selectEntry(event);
        }
      case Event.KEY_TAB:
      case Event.KEY_ESC:
        if(!this.active) return;
        this.hideTagger();
        Event.stop(event);
        return;
      case Event.KEY_LEFT:

        return;
      case Event.KEY_UP:
        if(!this.active) return;
        this.markPrevious();
        Event.stop(event);
        return;
      case Event.KEY_DOWN:
        if(!this.active) return;
        this.markNext();
        Event.stop(event);
        return;
      default:
        if(this.observer) clearTimeout(this.observer);
        this.observer = setTimeout(function(){this.updateChoices()}.bind(this), 0*1000);
    }
  },
  markPrevious: function() {
    this.index = (this.index > 0) ? this.index-1 : this.entryCount-1;
    var entry = this.getEntry(this.index);
    if(entry) {
      this.render();
    }
  },
  markNext: function() {
    this.index = (this.index < this.entryCount-1) ? this.index+1 : 0
    var entry = this.getEntry(this.index);
    if(entry) {
      this.render();
    }
  },
  getEntry: function(index) {
    return $A(this.update.getElementsByTagName('li'))[index];
  },
  updateChoices: function() {
    this.update.update();
    var list = this.buildList(this.listArray);
    if (!list) {
      this.active = false;
      return this.update.hide();
    } else {
      this.active = true;
    }
    this.update.appendChild(list);
    var input = $F(this.element);
    var entrys = $A(this.update.getElementsByTagName('li'));
    entrys.each(function(el,i){
      el.autocompleteIndex = i;
      Event.observe(el, "mouseover", this.onHover.bindAsEventListener(this));
      Event.observe(el, "click", this.selectEntry.bindAsEventListener(this));
    }.bind(this));
    this.entryCount = entrys.length;
    this.index = (input.blank() || !this.entryCount) ? -1 : (this.options.selectFirstEntry) ? 0 : -1;
    this.render();
    this.setDimensions();
    this.update[(input.blank() && !this.options.showQuickList && this.options.tip.blank())?'hide':'show']();
  },
  selectEntry: function() {
    if (this.index<0) return;
    var entry = this.getEntry(this.index);
    this.element.value = (this.options.clearAfterSelection) ? this.options.labelText : entry.profile;
    this.hideTagger();
    if(this.options.onSelect) this.options.onSelect(entry.profile);
  },
  onHover: function(event) {
    this.index = Event.findElement(event,'li').autocompleteIndex;
    this.render();
    Event.stop(event);
  },
  render: function() {
    if (this.entryCount<=0) return;
    this.update.getElementsBySelector('ul li.hover').invoke('removeClassName','hover')
    if (this.index>=0) this.getEntry(this.index).addClassName("hover");
  },
  buildList: function(listArray) {
    var input     = $F(this.element);
    if (input.blank() && !this.options.showQuickList) return null;
    var ret       = [];
    var ul = document.createElement('ul');
    if(!input.blank()) {
      listArray = this.findMatches(listArray).slice(0,this.options.arrayLimit);
      if(listArray.length < 1) return null;
    }
    listArray.slice(0,this.options.arrayLimit).each(function(elem){
      var user_name = elem
      var li = $(document.createElement('li'));
      li.profile = user_name;
      try {
        if(!input.blank()) {
          input.split(" ").each(function(el,i) {
            if(el) user_name = user_name.replace(new RegExp("("+el+")","ig"),"[{$1}]")
          });
          user_name = user_name.replace(/\[\{/g,"<em>").replace(/\}\]/g,"</em>");
        }
      } catch (e) {
        //error parsing names (parentheses break it)
      }
      li.update(user_name);
      ul.appendChild(li);
    }.bind(this))
    return ul;
  },
  findMatches: function(listArray){
    var input = $F(this.element).strip();
    var maxMatches = input.split(" ").length;
    var total_matches = [];
    listArray.each(function(elem){
      var name = elem
      var matches = 0;
      input.split(" ").each(function(el) {
        if(el) matches += this.numMatch(el, name);
      }.bind(this));
      if(matches >= maxMatches) total_matches.push([matches, elem]);
    }.bind(this))
    total_matches.sort(function(a, b){
      return ((a[0] > b[0]) ? -1 : ((a[0] < b[0]) ? 1 : 0));
    });
    return total_matches.map(function(el){ return el[1];});
  },
  numMatch: function(name, user_name){
    var matchNum = 0;
    try {
      user_name.split(" ").each(function(el){
        if(el && el.match(new RegExp('^'+name, "ig")))
          matchNum +=  1;
      });
    } catch (e) {
      //error - probably parentheses
    }
    return matchNum;
  },
  setDimensions: function() {
    this.update.setStyle({width:"auto",height:"auto"})
    var nw = (this.update.getWidth() > this.element.getWidth()) ? this.update.getWidth() : this.element.getWidth();
    var nh = "auto";
    if (this.update.getHeight() >= this.options.maximumHeight) {
      nw += 18; //for scrollbar
      var nh = this.options.maximumHeight+'px';
    }
    this.update.setStyle({'width':nw+'px','height':nh})
  },
  setOptions: function(options) {
    this.options = Object.extend({
      onSelect: new Function(),
      onRemove: new Function(),
      clearAfterSelection: false,
      selectFirstEntry:false,
      arrayLimit: 10,
      className:"autocomplete",
      maximumHeight:250,
      showQuickList:false
    }, (options || {}));
  }
});
