//
// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2008
// 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_SEARCH_MANAGER_CID      = Components.ID('{438ca78d-41b5-4831-8a93-c4fb5c346bde}');

const nsISupports                   = Components.interfaces.nsISupports;
const nsIClassInfo                  = Components.interfaces.nsIClassInfo;
const nsIFactory                    = Components.interfaces.nsIFactory;
const nsIProperties                 = Components.interfaces.nsIProperties;
const nsILocalFile                  = Components.interfaces.nsILocalFile;
const nsIFile                       = Components.interfaces.nsIFile;
const nsIIOService                  = Components.interfaces.nsIIOService;
const nsIFileProtocolHandler        = Components.interfaces.nsIFileProtocolHandler;
const nsIPreferenceService          = Components.interfaces.nsIPrefBranch;
const nsIBrowserSearchService       = Components.interfaces.nsIBrowserSearchService
const flockISearchManager           = Components.interfaces.flockISearchManager;


const FLOCK_SEARCH_MANAGER_CONTRACTID = "@flock.com/search-manager;1";
const DIRECTORY_SERVICE_CONTRACTID    = '@mozilla.org/file/directory_service;1';
const LOCAL_FILE_CONTRACTID           = '@mozilla.org/file/local;1';
const PREFERENCES_CONTRACTID          = '@mozilla.org/preferences-service;1';
const IO_SERVICE_CONTRACTID           = '@mozilla.org/network/io-service;1';
const SEARCH_SERVICE_CONTRACID        = '@mozilla.org/browser/search-service;1';

const SEARCHELSEWHERE_EXCLUSION_PREF  = "flock.search.excludedEngines";
const LIVESEARCH_ENABLED_SVCS_PREF    = "flock.liveSearch.enabledServices";
const LIVESEARCH_ORDERED_SVCS         = "flock.liveSearch.orderedServices";

const nsIRDFRemoteDataSource        = Components.interfaces.nsIRDFRemoteDataSource;
const nsIRDFDataSource              = Components.interfaces.nsIRDFDataSource;
const flockISearchService           = Components.interfaces.flockISearchService;

const nsIXMLHttpRequest             = Components.interfaces.nsIXMLHttpRequest;
const XMLHTTPREQUEST_CONTRACTID     = '@mozilla.org/xmlextras/xmlhttprequest;1'

const flockIError = Components.interfaces.flockIError;
const FLOCK_ERROR_CONTRACTID = '@flock.com/error;1'
var SEARCH_API_URL = "";


function flockSearchManager() {
  debug('>> flockSearchManager is being constructed\n');
  this.mName2ServiceMap = {};
  this.mService2NameMap = {};
  this.mPreferredServices = {};
  this.mPrefService = Components.classes[PREFERENCES_CONTRACTID].getService(nsIPreferenceService);
  this.mBrowserSearchService = Components.classes[SEARCH_SERVICE_CONTRACID].getService(nsIBrowserSearchService);
    
  var catmgr = Components.classes["@mozilla.org/categorymanager;1"]
                         .getService (Components.interfaces.nsICategoryManager);
  var enum = catmgr.enumerateCategory("flockISearchService");
  while(enum.hasMoreElements()) {
    try {
      var obj = enum.getNext();
      debug('>> flockSearchManager obj ' + obj +'\n');
      var supportsString = obj.QueryInterface(Components.interfaces.nsISupportsCString);
      var shortName = supportsString.toString();
      var cid = catmgr.getCategoryEntry("flockISearchService", shortName);
      var svc = Components.classes[cid].getService(Components.interfaces.flockISearchService);
      this.registerAPI(svc, shortName);
    } catch(e) {
      debug('>>> EXCEPTION ' + e + '\n');
    }
  }
}

flockSearchManager.prototype.mStates = {};
flockSearchManager.prototype.mListeners = new Array();

flockSearchManager.prototype.addListener = function(aListener) {
  if (aListener == null) {
    throw  "Listener is null!!";
  }
  this.mListeners.push(aListener);
}

flockSearchManager.prototype.removeListener = function(aListener) {
  for(var i=0;i<this.mListeners.length;++i) {
    if(aListener==this.mListeners[i]) {
      this.mListeners.splice(i,1);
      break;
    }
  }
}

flockSearchManager.prototype.registerAPI = function(aService, aShortname) {
  debug('registerAPI ' + aService + ' ' + aService.shortName +'\n');
  this.mName2ServiceMap[aShortname] = aService;
  this.mService2NameMap[aService] = aShortname;
}

flockSearchManager.prototype.getServiceFromShortname = function(aShortname) {
  return this.mName2ServiceMap[aShortname];
}

flockSearchManager.prototype.__defineGetter__('services', function () { 
  var ar = new Array();
  for(var p in this.mName2ServiceMap) {
    ar.push(this.mName2ServiceMap[p]);
  }
  return {
    getNext: function() {
      var rval = ar.shift();
      return rval;
    },
    hasMoreElements: function() {
      return (ar.length>0);
    }
  }
})

flockSearchManager.prototype.__defineGetter__('orderedServices', function () { 
  var orderedServices = this.getServicesInOrder();
  if (!orderedServices.length) return this.services;
  
  var ar = [];
  for (var i=0; i < orderedServices.length; i++) {
    var svc = this.getServiceFromShortname(orderedServices[i]);
    if (svc) {
      ar.push(svc);
    }
  }
  
  return {
    getNext: function() {
      var rval = ar.shift();
      return rval;
    },
    hasMoreElements: function() {
      return (ar.length>0);
    },
  }
})


flockSearchManager.prototype.__defineGetter__('enabledServices', function () { 
  var enabledList = this.getEnabledServices();
  var ar = [];
  
  for (var i=0; i < enabledList.length; i++) {
    var svc = this.getServiceFromShortname(enabledList[i]);
    if (svc) {
      ar.push(svc);
    }
  }
  
  return {
    getNext: function() {
      var rval = ar.shift();
      debug('enabledServices '+ rval +'\n');
      return rval;
    },
    hasMoreElements: function() {
      return (ar.length>0);
    },
  }
})


flockSearchManager.prototype.__defineGetter__('searchElsewhereList', function () { 
  var ar = this.getSearchElsewhereList();
  return {
    getNext: function() {
      var rval = ar.shift();
      return rval;
    },
    hasMoreElements: function() {
      return (ar.length > 0);
    }
  };
})


flockSearchManager.prototype.getServicesInOrder = function () {
  var pref = this.mPrefService.getPrefType(LIVESEARCH_ORDERED_SVCS);
  if (pref) {
    var pref = this.mPrefService.getCharPref(LIVESEARCH_ORDERED_SVCS);
    var ar = pref.split(",");
    var rval = new Array();
    for(var i=0;i<ar.length;++i) {
      if(ar[i].length) rval.push(ar[i]); 
    }
    return rval;
  } else {
    return new Array();
  }
}

flockSearchManager.prototype.getEnabledServices = function() {
  try {
    var pref = this.mPrefService.getCharPref("flock.liveSearch.enabledServices");
    var ar = pref.split(",");
    var rval = new Array();
    for(var i=0;i<ar.length;++i) {
      if(ar[i].length) rval.push(ar[i]); 
    }
    return rval;
  } catch(e) {
    debug(e);
    return new Array();
  }
}


flockSearchManager.prototype.isServiceEnabled = function(aShortName) {
    try {
       var pref = this.mPrefService.getCharPref(LIVESEARCH_ENABLED_SVCS_PREF);
       var ar = pref.split(",");
       for(var i=0;i<ar.length; i++) {
           if (ar[i] == aShortName)
            return true;
       }
       return false;
    } catch (ex) {
        debug (ex);
        return false;
    }
}

flockSearchManager.prototype.search = function(aQuery, aNumResults, aListener, aDataSource) {
  var enum = this.enabledServices;
  var inst = this;
  /*
  var searchListener = {
      foundResults: function(aNumResults, aDataSource, aServiceNameSource) {
          //inst.notify(aNumResults, aDataSource, aServiceNameSource);
          aListener.foundResults(aNumResults, aDataSource, aServiceNameSource);
      }
  }
  */
  while(enum.hasMoreElements()) {
    var service = enum.getNext();
    service.search(aQuery, aNumResults, /* searchListener */ aListener, aDataSource);
    aListener.searchStarted(service.shortName)
    //this.notifySearchStarted(service.shortName);
  }
}

flockSearchManager.prototype.getSearchElsewhereList = function() {
  var pref = this.mPrefService.getCharPref(SEARCHELSEWHERE_EXCLUSION_PREF);
  var engines = this.mBrowserSearchService.getEngines({ });
  var retval = [];
  for (var i = 0; i < engines.length; i++) {
    if (pref.indexOf(engines[i].name) == -1) {
      retval.push(engines[i]);
    }
  }
  return retval;
},

flockSearchManager.prototype.notifySearchStarted = function (aServiceNameSource) {
  for(var i=0;i<this.mListeners.length;++i) {
    this.mListeners[i].searchStarted(aServiceNameSource);
  }
}

flockSearchManager.prototype.notify = function (aNumResults, aDataSource, aServiceNameSource) {
  for(var i=0;i<this.mListeners.length;++i) {
      this.mListeners[i].foundResults(aNumResults, aDataSource, aServiceNameSource);
    }
}

flockSearchManager.prototype.setEnabledServices = function(aArray) {
    var val = aArray.join(",");
    var pref = this.mPrefService.setCharPref("flock.search.flyout.enabledServices", val);
}

flockSearchManager.prototype.flags = nsIClassInfo.SINGLETON;
flockSearchManager.prototype.classDescription = "Flock Search Manager Service";
flockSearchManager.prototype.getInterfaces = function (count) {
    var interfaceList = [flockISearchManager, nsIClassInfo];
    count.value = interfaceList.length;
    return interfaceList;
}

flockSearchManager.prototype.getHelperForLanguage = function (count) {return null;}

flockSearchManager.prototype.QueryInterface =
function (iid) {
    if (!iid.equals(flockISearchManager) && 
        !iid.equals(nsIClassInfo) &&
        !iid.equals(nsISupports))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
}

// Module implementation
var FlockSearchModule = new Object();

FlockSearchModule.registerSelf =
function (compMgr, fileSpec, location, type)
{
    compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);

    compMgr.registerFactoryLocation(FLOCK_SEARCH_MANAGER_CID, 
                                    "Flock Search Manager JS Component",
                                    FLOCK_SEARCH_MANAGER_CONTRACTID, 
                                    fileSpec, 
                                    location,
                                    type);
}

FlockSearchModule.getClassObject =
function (compMgr, cid, iid) {
    if (!cid.equals(FLOCK_SEARCH_MANAGER_CID))
        throw Components.results.NS_ERROR_NO_INTERFACE;
    
    if (!iid.equals(Components.interfaces.nsIFactory))
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    
    return FlockSearchManagerFactory;
}

FlockSearchModule.canUnload =
function(compMgr)
{
    return true;
}
    
/* factory object */
var FlockSearchManagerFactory = new Object();

FlockSearchManagerFactory.createInstance =
function (outer, iid) {
    if (outer != null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;

    return (new flockSearchManager()).QueryInterface(iid);
}

/* entrypoint */
function NSGetModule(compMgr, fileSpec) {
    return FlockSearchModule;
}

