if (typeof W4 == 'undefined' || !W4) {
    var W4 = {};
}

W4.service = function() {

  var Connect = YAHOO.util.Connect;

  return {
    /**
     * Given a W4.ui.ScannedPage, sends the text of that page to the entity
     * detection service, and then locates the detected entities in the page. 
     * The result is an array of objects, each of which has the following
     * properties: 'type' is the type of entity that was detected (can be
     * 'person', 'city', or 'country'), 'name' is the canonical entity name, 
     * 'text' is the actual text that refers to the entity, 'locator' is a 
     * locator object specifying the location of that text on the page, and
     * 'resource' is the URL of the page. This array will be passed to the 
     * specified callback function.
     *
     * @method detectEntities
     * @param {W4.ui.ScannedPage} page  The page to look for entities in
     * @param {Function} callback  The function to call with results
     * @param {Object} scope  The scope in which to call the callback function
     */
    detectEntities: function(page, callback, scope) {
      if (! scope) { scope = this; }
      var cb = {
        success: function(o) { 
          var page = o.argument.page;
          var results = YAHOO.lang.JSON.parse(o.responseText);
          var found = [];
          for (var type in results) {
            var detections = results[type];
            var prev_result = null;
            for (var i = 0; i < detections.length; i++) {
              if (prev_result) {
                var result = page.find(detections[i].text, 
                  prev_result.last_line, prev_result.last_word + 1);
              } else {
                var result = page.find(detections[i].text);
              }
              if (! result) {
                YAHOO.log('Could not find "' + detections[i].text + '"');
                continue;
              }
              detections[i].resource = location.href + '#' + page.id;
              detections[i].locator = result.locator;
              found.push(detections[i]);
              prev_result = result;
            }
          }
          o.argument.callback.call(o.argument.scope, page, found);
        },
        failure: function(o) {
          _logFailure(o);
          o.argument.callback.call(o.argument.scope, o.argument.page, null);
        },
        argument: { page: page, callback: callback, scope: scope }
      };
      _post('/oldw4/detect/', cb, 'text/plain', page.getText());
    },

    /**
     * Given an array of entity detections (see detectEntities), publishes
     * them to the remote entity repository. The callback function will be
     * called with two arguments: an array containing the detections that
     * were successfully published, and an array containing the detections
     * that failed to be published.
     *
     * @method publishDetections
     * @param {Array} detections  an array of entity detections
     * @param {Function} callback  The function to call with results
     * @param {Object} scope  The scope in which to call the callback function
     */
    publishDetections: function(detections, callback, scope) {
      if (! detections) { return; }
      if (! scope) { scope = this; }
      var handler = {
        succeeded: [],
        failed: [],
        success: function(o) {
          this.succeeded.push(o.argument);
          this.checkFinished();
        },
        failure: function(o) {
          _logFailure(o);
          this.failed.push(o.argument);
          this.checkFinished();
        },
        checkFinished: function() {
          if (this.succeeded.length + this.failed.length == detections.length) {
            callback.call(scope, this.succeeded, this.failed);
          }
        }
      };
      for (var i = 0; i < detections.length; i++) {
        var cb = {
          scope: handler,
          success: handler.success,
          failure: handler.failure,
          argument: detections[i]
        };
        var json = YAHOO.lang.JSON.stringify(detections[i]);
        _post('/oldw4/who/detections/', cb, 'application/json', json);
      }
    }
  };

  function _post(uri, callback, mimetype, data) {
    Connect.setDefaultPostHeader(false);
    Connect.initHeader('Content-Type', mimetype + '; charset=UTF-8');
    Connect.asyncRequest('POST', uri, callback, data);
    Connect.setDefaultPostHeader(true);
  };

  function _logFailure(o) {
    var message = 'Call to web service failed: ';
    if (o.responseText) {
      message += o.responseText;
    } else {
      message += o.statusText;
    }
    YAHOO.log(message);
  }
    
}();
