/**
 * @requires javelin-install
 *           javelin-util
 *           javelin-request
 *           javelin-typeahead-source
 * @provides javelin-typeahead-ondemand-source
 * @javelin
 */

JX.install('TypeaheadOnDemandSource', {

  extend : 'TypeaheadSource',

  construct : function(uri) {
    JX.TypeaheadSource.call(this);
    this.uri = uri;
    this.haveData = {
      '' : true
    };
  },

  properties : {
    /**
     * Configures how many milliseconds we wait after the user stops typing to
     * send a request to the server. Setting a value of 250 means "wait 250
     * milliseconds after the user stops typing to request typeahead data".
     * Higher values reduce server load but make the typeahead less responsive.
     */
    queryDelay : 125,
    /**
     * Auxiliary data to pass along when sending the query for server results.
     */
    auxiliaryData : {}
  },

  members : {
    uri : null,
    lastChange : null,
    haveData : null,

    didChange : function(raw_value, force) {
      this.lastChange = JX.now();
      var value = this.normalize(raw_value);

      if (this.haveData[value]) {
        this.matchResults(raw_value);
      } else {
        // If we have data for any prefix of the query, send those results
        // back immediately. This allows "alinc" -> "alinco" to show partial
        // results without the UI flickering. We'll still show the loading
        // state, and then can show better results once we get everything
        // back.
        for (var ii = value.length - 1; ii > 0; ii--) {
          var substr = value.substring(0, ii);
          if (this.haveData[substr]) {
            this.matchResults(raw_value, true);
            break;
          }
        }

        this.waitForResults();

        var send_request = JX.bind(
          this,
          this.sendRequest,
          this.lastChange,
          value,
          raw_value);

        if (force) {
          send_request();
        } else {
          setTimeout(send_request, this.getQueryDelay());
        }
      }
    },

    sendRequest : function(when, value, raw_value) {
      if (when != this.lastChange) {
        return;
      }
      var r = new JX.Request(
        this.uri,
        JX.bind(this, this.ondata, this.lastChange, raw_value));
      r.setMethod('GET');
      r.setData(JX.copy(this.getAuxiliaryData(), {q : value, raw: raw_value}));
      r.send();
    },

    ondata : function(when, raw_value, results) {
      if (results) {
        for (var ii = 0; ii < results.length; ii++) {
          this.addResult(results[ii]);
        }
      }

      var value = this.normalize(raw_value);
      this.haveData[value] = true;

      if (when != this.lastChange) {
        return;
      }

      this.matchResults(raw_value);
    }
  }
});
