//
// 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 SM_CONTRACTID = "@flock.com/search-manager;1";
const SM_CLASSID    = Components.ID("{438ca78d-41b5-4831-8a93-c4fb5c346bde}");
const SM_CLASSNAME  = "Flock Search Manager";

const CATEGORY_SEARCH_SERVICE = "flockISearchService";

const PREF_SEARCH_ELSEWHERE_ENGINES = "flock.search.elsewhereEngines";
const PREF_LIVESEARCH_ENABLED_SVCS = "flock.liveSearch.enabledServices";
const PREF_LIVESEARCH_ORDERED_SVCS = "flock.liveSearch.orderedServices";

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

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


function SearchManager() {
  this._logger = CC["@flock.com/logger;1"].createInstance(CI.flockILogger);
  this._logger.init("searchmanager");
  this._logger.info("starting up...");

  this._nameToServiceMap = {};
  this._serviceToNameMap = {};

  this._registerServices();
}

SearchManager.prototype = new FlockXPCOMUtils.genericComponent(
  SM_CLASSNAME,
  SM_CLASSID,
  SM_CONTRACTID,
  SearchManager,
  CI.nsIClassInfo.SINGLETON,
  [CI.flockISearchManager]
);

SearchManager.prototype._registerServices =
function SM__registerServices() {
  var catman = CC["@mozilla.org/categorymanager;1"]
               .getService(CI.nsICategoryManager);

  var entries = catman.enumerateCategory(CATEGORY_SEARCH_SERVICE);
  while (entries.hasMoreElements()) {
    try {
      var entry = entries.getNext().QueryInterface(CI.nsISupportsCString).data;
      var contractID = catman.getCategoryEntry(CATEGORY_SEARCH_SERVICE, entry);

      this._logger.info("Registering search service: " + entry);
      var service = CC[contractID].getService(CI.flockISearchService);
      this._registerAPI(service, entry);
    }
    catch (ex) {
      this._logger.error("Error getting search services: " + ex);
    }
  }
}

SearchManager.prototype._registerAPI =
function SM__registerAPI(aService, aShortName) {
  this._logger.debug("registerAPI " + aService + " " + aShortName);
  this._nameToServiceMap[aShortName] = aService;
  this._serviceToNameMap[aService] = aShortName;
}

SearchManager.prototype.getServiceFromShortname =
function SM_getServiceFromShortname(aShortName) {
  return this._nameToServiceMap[aShortName];
}

SearchManager.prototype._getServicesEnumerator =
function SM__getServicesEnumerator(aShortNames)
{
  let serviceNames = [];
  for each (let shortName in aShortNames) {
    let serviceName = this._nameToServiceMap[shortName];
    if (serviceName) {
      serviceNames.push(serviceName);
    }
  }
  let enumerator = {
    _serviceNames: serviceNames,
    hasMoreElements: function serviceEnumerator_hasMoreElements() {
      return (this._serviceNames.length > 0);
    },
    getNext: function serviceEnumerator_getNext() {
      return this._serviceNames.shift();
    }
  };
  return enumerator;
}

SearchManager.prototype.__defineGetter__("services",
function SM_getter_services() {
  return this._getServicesEnumerator([k for (k in this._nameToServiceMap)]);
});

SearchManager.prototype.__defineGetter__("orderedServices",
function SM_getter_orderedServices() {
  var names = this._getServiceNamesFromPref(PREF_LIVESEARCH_ORDERED_SVCS);
  if (names.length > 0) {
    return this._getServicesEnumerator(names);
  } else {
    return this.services;
  }
});


SearchManager.prototype.__defineGetter__("enabledServices",
function SM_getter_enabledServices() {
  var names = this._getServiceNamesFromPref(PREF_LIVESEARCH_ENABLED_SVCS);
  return this._getServicesEnumerator(names);
});

SearchManager.prototype.__defineGetter__("searchElsewhereList",
function SM_getter_searchElsewhereList() {
  var enumerator = {
    _engines: this._getSearchElsewhereList(),
    hasMoreElements: function searchElsewhereEnumerator_hasMoreElements() {
      return (this._engines.length > 0);
    },
    getNext: function searchElsewhereEnumerator_getNext() {
      return this._engines.shift();
    }
  };
  return enumerator;
});

SearchManager.prototype._getServiceNamesFromPref =
function SM__getServiceNamesFromPref(aPrefName) {
  var prefs = CC["@mozilla.org/preferences-service;1"]
              .getService(CI.nsIPrefBranch);
  if (prefs.getPrefType(aPrefName) == CI.nsIPrefBranch.PREF_STRING) {
    var pref = "";
    try {
      pref = prefs.getComplexValue(aPrefName, CI.nsIPrefLocalizedString).data;
    }
    catch (ex) {
      pref = prefs.getCharPref(aPrefName);
    }
    if (pref) {
      return pref.split(",").filter(function flt(aSvc) {
                                       return Boolean(aSvc);
                                    });
    }
  }
  return [];
}

SearchManager.prototype.isServiceEnabled =
function SM_isServiceEnabled(aShortName) {
  var names = this._getServiceNamesFromPref(PREF_LIVESEARCH_ENABLED_SVCS);
  return (names.indexOf(aShortName) != -1);
}

SearchManager.prototype.search =
function SM_search(aQuery, aNumResults, aSearchListener, aDataSource) {
  var searchEngines = this.enabledServices;
  while (searchEngines.hasMoreElements()) {
    var searchEngine = searchEngines.getNext()
                                    .QueryInterface(CI.flockISearchService);
    searchEngine.search(aQuery, aNumResults, aSearchListener, aDataSource);
    aSearchListener.searchStarted(searchEngine.shortName);
  }
}

SearchManager.prototype._getSearchElsewhereList =
function SM__getSearchElsewhereList() {
  var names = this._getServiceNamesFromPref(PREF_SEARCH_ELSEWHERE_ENGINES);

  var elsewhereEngines = [];
  var browserSearchService = CC["@mozilla.org/browser/search-service;1"]
                             .getService(CI.nsIBrowserSearchService);

  for each (var engine in browserSearchService.getEngines({})) {
    if (names.indexOf(engine.name) >= 0) {
      elsewhereEngines.push(engine);
    }
  }

  return elsewhereEngines;
}

var gComponentsArray = [SearchManager];

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