/*
  PFrameworkCS API

  Copyright (C) 2010 by Gregor Kališnik <gregor@unimatrix-one.org>
  Copyright (C) 2010 by Unimatrix-One (www.unimatrix-one.org)

  This javascript is part of the PFramework CMS system. Any unauthorised
  use outside of this system is strictly prohibited.
*/

// Requires prototype, psourceelements, pextends
var PAutosuggest = Class.create({
  initialize: function(id, output, source)
  {
    this.input = null;
    this.output = $(output);
    this.source = null;
    this.curPos = -1;
    this.size = 0;
    this.timer = 0;
    this.hideTimer = 0;
    this.lastQuery = '';
    this.data = new Array();
    this.selectionCB = null;
    this.clickedCB = null;
    
    this.navigateHandler = this.navigateCB.bindAsEventListener(this);
    this.queryHandler = this.queryCB.bindAsEventListener(this);
    this.hideHandler = this.hide.bindAsEventListener(this);
    this.suggestionsHandler = this.suggestionsCB.bindAsEventListener(this);
    
    this.setInput(id);
    this.setSource(source);
    
    this.focusHandler = this.focus.bindAsEventListener(this);
    this.clickHandler = this.clickCB.bindAsEventListener(this);
  },
  
  setInput: function(id)
  {
    if (this.input != null) {
      this.input.stopObserving('keydown', this.navigateHandler);
      this.input.stopObserving('keyup', this.queryHandler);
      this.input.stopObserving('focus', this.queryHandler);
      this.input.stopObserving('blur', this.hideHandler);
    }
    this.input = $(id);
    this.input.observe('keydown', this.navigateHandler);
    this.input.observe('keyup', this.queryHandler);
    this.input.observe('focus', this.queryHandler);
    this.input.observe('blur', this.hideHandler);
  },
  
  setSource: function(source)
  {
    if (this.source != null)
      this.source.stopObserving('PSource:requestFinished', this.suggestionsHandler);
    this.data = new Array();
    this.lastQuery = '';
    this.curPos = -1;
    
    this.source = source;
    this.source.observe('PSource:requestFinished', this.suggestionsHandler);
  },
  
  queryCB: function(e)
  {
    switch (e.keyCode) {
      case Event.KEY_UP:
      case Event.KEY_DOWN:
      case Event.KEY_LEFT:
      case Event.KEY_RIGHT:
        if (this.output.visible()) {
          this.input.selectionEnd = this.input.getValue().length;
          this.input.selectionStart = this.input.selectionEnd;
        }
        break;
      
      case Event.KEY_ESC:
        this.hide();
        break;
      
      default:
        var text = this.input.getValue();
        if (this.lastQuery == text) {
          if (this.lastQuery.length > 0)
            this.show();
          return;
        }
        
        this.lastQuery = text;
        if (this.timer)
          clearTimeout(this.timer);
        var thisVar = this;
        this.timer = setTimeout(function() {
          thisVar.source.query(text);
          thisVar.timer = 0;
        }, 100);
    }
  },
  
  navigateCB: function(e)
  {
    switch (e.keyCode) {
      case Event.KEY_UP:
        this.moveUp();
        break;
      
      case Event.KEY_DOWN:
        this.moveDown();
        break;
      
      case Event.KEY_LEFT:
        this.moveLeft();
        break;
      
      case Event.KEY_RIGHT:
        this.moveRight();
        break;
      
      default:
        break;
    }
  },
  
  clickCB: function(e)
  {
    this.deselect();
    var element = e.element();
    if (element.tagName == 'DIV')
      element = element.down('span');
    else if (element.tagName == 'IMG')
      element = element.next('span');
    element = element.next('span');
    this.curPos = parseInt(element.innerHTML);
    this.select();
    
    if (this.clickedCB != null)
      this.clickedCB(this.data[this.curPos]);
  },
  
  moveDown: function()
  {
    if (this.curPos >= 0 && this.curPos + 2 >= this.size)
      return;
    
    this.deselect();
    if (this.curPos < 0)
      this.curPos = 0;
    else
      this.curPos += 2;
    this.select();
  },
  
  moveUp: function()
  {
    if (this.curPos - 2 < 0)
      return;
      
    this.deselect();
    this.curPos -= 2;
    this.select();
  },
  
  moveLeft: function()
  {
    if (this.curPos - 1 < 0)
      return;
      
    this.deselect();
    this.curPos -= 1;
    this.select();
  },
  
  moveRight: function()
  {
    if (this.curPos + 1 >= this.size)
      return;
    
    this.deselect();
    this.curPos += 1;
    this.select();
  },
  
  deselect: function()
  {
    var item = this.output.down('div', this.curPos);
    if (item)
      item.removeClassName('selected');
  },
  
  select: function()
  {
    var item = this.output.down('div', this.curPos);
    if (item)
      item.addClassName('selected');
    
    if (this.selectionCB != null)
      this.selectionCB(this.data[this.curPos]);
  },
  
  suggestionsCB: function(e)
  {
    if (e.memo.code != 'successful')
      return;
    
    this.data = e.memo.data;
    this.output.update('');
    
    if (this.selectionCB != null)
      this.selectionCB(null);
      
    if (this.data.length == 0) {
      this.hide();
      return;
    }
    
    var output = this.output;
    var clickHandler = this.clickHandler;
    var i = 0;
    this.data.each(function (item) {
      var div = new Element('div', {href: item.url}).update(item.title.ptruncate(40) + '<br/><span>' + item.description.replace('\n', '<br />') + '</span>');
      div.insert({top: '<img src="' + item.image.source + '" height="' + item.image.height + 'px" width="' + item.image.width + 'px" align="left" />'});
      var pos = new Element('span', {style: 'display:none;'});
      pos.update(i);
      div.insert(pos);
      i += 1;
      
      output.insert({bottom: div});
      div.observe('mousedown', clickHandler);
    });
    
    this.size = this.data.length;
    this.curPos = -1;
    this.show();
  },
  
  show: function()
  {
    if (this.lastQuery == '')
      return;
    
    if (this.hideTimer) {
      clearTimeout(this.hideTimer);
      this.hideTimer = 0;
      return;
    }
    this.output.show();
    var offset = this.input.cumulativeOffset();
    this.output.setStyle({
      left: offset.left + 'px',
      top: (offset.top + this.input.getHeight()) + 'px'
    });
  },
  
  focus: function()
  {
    this.input.focus();
    this.show();
  },
  
  hide: function()
  {
    var output = this.output;
    if (this.hideTimer)
      clearTimeout(this.hideTimer);
    var thisVar = this;
    this.hideTimer = setTimeout(function () {
      output.hide();
      thisVar.hideTimer = 0;
    }, 100);
  }
});
