// vim: tabstop=2 softtabstop=2 shiftwidth=2 expandtab
//
// 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 HK_CONTRACTID = '@flock.com/housekeeper;1';
const HK_CLASSID    = Components.ID('{e8415493-ab17-463e-837d-8516fce89364}');
const HK_CLASSNAME  = 'Flock Housekeeper';


const CATEGORY_HOUSEKEEPING = 'flockHousekeeping';

const PREF_HK_INTERVAL      = 'flock.housekeeping.interval';
const DEFAULT_HK_INTERVAL   = 28800;


const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;


function getObserverService() {
  return Cc['@mozilla.org/observer-service;1']
    .getService(Ci.nsIObserverService);
}

function getIntPref(prefName, defaultValue) {
  try {
    var prefs = Cc['@mozilla.org/preferences-service;1']
      .getService(Ci.nsIPrefBranch);
    return prefs.getIntPref(prefName);
  }
  catch (e) {
    return defaultValue;
  }
}


function Housekeeper() {
  var obs = getObserverService();

  obs.addObserver(this, 'flock-data-ready', false);
  obs.addObserver(this, 'xpcom-shutdown', false);
}

Housekeeper.prototype = {
  _start: function HK__start() {
    this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
    this._logger.init('housekeeper');
    this._logger.info('starting up...');

    var tm = Cc['@mozilla.org/updates/timer-manager;1']
      .getService(Ci.nsIUpdateTimerManager);
    var interval = getIntPref(PREF_HK_INTERVAL, DEFAULT_HK_INTERVAL);
    tm.registerTimer('background-housekeeping-timer', this, interval);
  },

  _shutdown: function HK__shutdown() {
  },

  observe: function HK_observe(subject, topic, state) {
    var obs = getObserverService();

    switch (topic) {
      case 'flock-data-ready':
        obs.removeObserver(this, 'flock-data-ready');
        this._start();
        break;

      case 'xpcom-shutdown':
        obs.removeObserver(this, 'xpcom-shutdown');
        this._shutdown();
        break;
    }
  },

  notify: function HK_notify(timer) {
    this.doHousekeeping();
  },

  doHousekeeping: function HK_doHousekeeping() {
    var catman = Cc['@mozilla.org/categorymanager;1']
      .getService(Ci.nsICategoryManager);

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

        this._logger.info('Running housekeeping: ' + contractID);
        var service = Cc[contractID].getService(Ci.flockIHousekeeping);
        service.runHousekeeping();
      }
      catch (e) {
        this._logger.error('Error running housekeeping: ' + e);
      }
    }
  },

  getInterfaces: function HK_getInterfaces(countRef) {
    var interfaces = [Ci.flockIHousekeeper, Ci.nsITimerCallback,
                      Ci.nsIObserver, Ci.nsIClassInfo, Ci.nsISupports];
    countRef.value = interfaces.length;
    return interfaces;
  },
  getHelperForLanguage: function HK_getHelperForLanguage(language) {
    return null;
  },
  contractID: HK_CONTRACTID,
  classDescription: HK_CLASSNAME,
  classID: HK_CLASSID,
  implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  flags: Ci.nsIClassInfo.SINGLETON,

  QueryInterface: function HK_QueryInterface(iid) {
    if (iid.equals(Ci.flockIHousekeeper) ||
        iid.equals(Ci.nsITimerCallback) ||
        iid.equals(Ci.nsIObserver) ||
        iid.equals(Ci.nsIClassInfo) ||
        iid.equals(Ci.nsISupports))
      return this;
    throw Cr.NS_ERROR_NO_INTERFACE;
  }
}


function GenericComponentFactory(ctor) {
  this._ctor = ctor;
}

GenericComponentFactory.prototype = {

  _ctor: null,

  // nsIFactory
  createInstance: function(outer, iid) {
    if (outer != null)
      throw Cr.NS_ERROR_NO_AGGREGATION;
    return (new this._ctor()).QueryInterface(iid);
  },

  // nsISupports
  QueryInterface: function(iid) {
    if (iid.equals(Ci.nsIFactory) ||
        iid.equals(Ci.nsISupports))
      return this;
    throw Cr.NS_ERROR_NO_INTERFACE;
  },
};

var Module = {
  QueryInterface: function(iid) {
    if (iid.equals(Ci.nsIModule) ||
        iid.equals(Ci.nsISupports))
      return this;

    throw Cr.NS_ERROR_NO_INTERFACE;
  },

  getClassObject: function(cm, cid, iid) {
    if (!iid.equals(Ci.nsIFactory))
      throw Cr.NS_ERROR_NOT_IMPLEMENTED;

    if (cid.equals(HK_CLASSID))
      return new GenericComponentFactory(Housekeeper)

    throw Cr.NS_ERROR_NO_INTERFACE;
  },

  registerSelf: function(cm, file, location, type) {
    var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
    cr.registerFactoryLocation(HK_CLASSID, HK_CLASSNAME, HK_CONTRACTID,
                               file, location, type);

    var catman = Cc['@mozilla.org/categorymanager;1']
      .getService(Ci.nsICategoryManager);
    catman.addCategoryEntry('flock-startup', HK_CLASSNAME,
                            'service,' + HK_CONTRACTID,
                            true, true);
  },

  unregisterSelf: function(cm, location, type) {
    var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
    cr.unregisterFactoryLocation(HK_CLASSID, location);
  },

  canUnload: function(cm) {
    return true;
  },
};

function NSGetModule(compMgr, fileSpec)
{
  return Module;
}
