// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2009
// http://flock.com
//
// This file may be used under the terms of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
//
// END FLOCK GPL

const FLOCK_LIVE_SEARCH_CLASSNAME = "Flock Live Search";
const FLOCK_LIVE_SEARCH_CLASSID =
  Components.ID("{c711a35c-8949-4d01-9676-47e71f1a78f4}");
const FLOCK_LIVE_SEARCH_CONTRACTID = "@flock.com/live-search/flock;1";


const CC = Components.classes;
const CI = Components.interfaces;
const CR = Components.results;
const CU = Components.utils;

CU.import("resource:///modules/FlockXPCOMUtils.jsm");
CU.import("resource:///modules/FlockStringBundleHelpers.jsm");

function FlockLiveSearch() {
  this._logger = CC["@flock.com/logger;1"].createInstance(CI.flockILogger);
  this._logger.init("flocklivesearch");

  this._prefs = CC["@mozilla.org/preferences-service;1"]
                .getService(CI.nsIPrefBranch);
}

FlockLiveSearch.prototype = new FlockXPCOMUtils.genericComponent(
  FLOCK_LIVE_SEARCH_CLASSNAME,
  FLOCK_LIVE_SEARCH_CLASSID,
  FLOCK_LIVE_SEARCH_CONTRACTID,
  FlockLiveSearch,
  CI.nsIClassInfo.SINGLETON,
  [CI.flockISearchService]
);

FlockLiveSearch.prototype._xpcom_categories = [
  { category: "flockISearchService", entry: "flock" }
];

FlockLiveSearch.prototype.__defineGetter__("serviceName",
function FLS_getter_serviceName() {
  return flockGetString("search/search", "flock.search.api.flock");
});

FlockLiveSearch.prototype.__defineGetter__("shortName",
function FLS_getter_shortName() {
  return "flock";
});

FlockLiveSearch.prototype.__defineGetter__("icon",
function FLS_getter_icon() {
  return "rdf:http://flock.com/rdf#icon";
});

FlockLiveSearch.prototype.getFullResultsURL =
function FLS_getFullResultsURL(aQuery) {
  return null;
};

FlockLiveSearch.prototype.search =
function FLS_search(aQuery, aNumResults, aSearchListener) {
  var normalizedQuery = this._normalizeQuery(aQuery);

  this._logger.debug('query: "' + aQuery + '" => "' + normalizedQuery + '"');

  var showHistory =
    this._prefs.getBoolPref("flock.search.flyout.showHistory") &&
    this._prefs.getBoolPref("flock.service.indexer.indexWebHistory");

  var searchWorker = new SearchWorker(this._logger, aSearchListener, aQuery);

  if (showHistory) {
    searchWorker.search(normalizedQuery, ["bookmark", "history"], aNumResults);
  } else {
    searchWorker.search(normalizedQuery, ["bookmark"], aNumResults);
  }
};

FlockLiveSearch.prototype._normalizeQuery =
function FLS__normalizeQuery(aQuery) {
  var input = aQuery;
  var output;
  var in_quotes = false;

  // strip leading spaces
  while (input.length && input[0]==" ") {
    // chop off the first character
    input = input.substr(1);
  }

  // Convert to lowercase for the wildcard search
  input = input.toLowerCase();

  // add leading plus
  output = "+";
  while (input.length) {
    if (input[0] == '"') {
      if (in_quotes) {
        in_quotes = false;
      } else {
        in_quotes = true;
        // FIXME: remove spaces from the start of quoted strings
      }
    }

    // escape special chars
    if ("&|!(){}[]^~*?:\\".indexOf(input[0]) >= 0) {
      output = output + "\\";
    }

    output = output + input[0];

    if (!in_quotes && input[0] == " " && input.length > 1) {
      // put a plus before every word
      output = output + "+";
    }

    input = input.substr(1);
  }

  // remove trailing spaces
  var removed_spaces = false;
  while (output.length > 0 && output[output.length - 1] == " ") {
    output = output.substr(0, output.length - 1);
    removed_spaces = true;
  }

  // if we're in a quoted string we should close that
  if (in_quotes) {
    output = output + "\"";
  }

  // if theres an incomplete word
  if ((!removed_spaces) && (output[output.length - 1] != "\"")) {
    // search for partial matches
    output = output + "*";
  }

  return output;
};

function SearchWorker(aLogger, aSearchListener, aOriginalQuery) {
  this._logger = aLogger;
  this._searchListener = aSearchListener;
  this._originalQuery = aOriginalQuery;
}

SearchWorker.prototype = new FlockXPCOMUtils.genericComponent(
  FLOCK_LIVE_SEARCH_CLASSNAME + " Worker",
  "",
  "",
  SearchWorker,
  0,
  [CI.flockILuceneSearchCallback]
);

SearchWorker.prototype.search =
function SW_search(aQuery, aTypes, aMaxResults) {
  var searchService = CC["@flock.com/lucene;1"].getService(CI.flockILucene);

  this._maxResults = aMaxResults;

  this._resultsBookmarks = [];
  this._resultsHistory = [];

  this._searchesOutstanding = 0;

  for each (let type in aTypes) {
    searchService.search(aQuery, type, aMaxResults, this);
    this._searchesOutstanding++;
  }
};

SearchWorker.prototype.handleResults =
function SW_handleResults(aNumResults, aResults) {
  this._logger.debug("handleResults: hits: " + aNumResults);

  if (aNumResults > 0) {
    while (aResults.hasMoreElements()) {
      var result = aResults.getNext();
      this._logger.debug("foundResults: " + result.type + " " + result.URI);

      if (!result.title) {
        // No title, no useful UI to show. Filter out.
        continue;
      }

      switch (result.type) {
        case "bookmark":
          this._resultsBookmarks.push({ url: result.URL, title: result.title });
          break;
        case "history":
          this._resultsHistory.push({ url: result.URL, title: result.title });
          break;
      }
    }
  }

  this._searchesOutstanding--;

  if (this._searchesOutstanding == 0) {
    this._returnResults();
  }
};

SearchWorker.prototype._addResults =
function SW__addResults(aResults, aStarIcon) {
  var maxResults = Math.min(aResults.length, this._maxResults);

  for (let i = 0;
       i < maxResults && this._results.length < this._maxResults;
       i++)
  {
    var result = aResults[i];

    // skip duplicate bookmarks (online bookmarks)
    if (this._urlHash[result.url] == true) {
      continue;
    }

    this._urlHash[result.url] = true;

    result.icon = aStarIcon ? "chrome://flock/skin/common/star16.png"
                            : "chrome://flock/skin/search/documentIcon16.png";

    this._results.push(result);
  }
};

SearchWorker.prototype._returnResults =
function SW__returnResults() {
  this._urlHash = {};
  this._results = [];

  this._addResults(this._resultsBookmarks, true);
  this._addResults(this._resultsHistory, false);

  this._searchListener.foundResults(this._results, "flock",
                                    this._originalQuery);
};

var gComponentsArray = [FlockLiveSearch];

var NSGetModule =
  FlockXPCOMUtils.generateNSGetModule(FLOCK_LIVE_SEARCH_CLASSNAME,
                                      gComponentsArray);
