// 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 SHK_CONTRACTID = '@flock.com/stream-housekeeping;1';
const SHK_CLASSID    = Components.ID('{c4a5d459-3644-4557-9cd6-a3a4cb09c59a}');
const SHK_CLASSNAME  = 'Flock Stream Housekeeping';


const CAP_RUN_INTERVAL     = 500;
const CAP_SLEEP_INTERVAL   = 2000;

const DEFAULT_ITEM_CAP     = 100;


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


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 StreamHousekeeping() {
  this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  this._logger.init('streamhousekeeping');
  this._logger.info('starting up...');

  this._faves = Cc['@mozilla.org/rdf/datasource;1?name=flock-favorites']
    .getService(Ci.nsIRDFDataSource);
  this._coop = Cc["@flock.com/singleton;1"]
               .getService(Ci.flockISingleton)
               .getSingleton("chrome://flock/content/common/load-faves-coop.js")
               .wrappedJSObject;
}

StreamHousekeeping.prototype = {
  runHousekeeping: function SHK_runHousekeeping() {
    this._logger.info('running housekeeping...');
    this._capItemsForAllObjects();
  },

  capItemsForObject: function SHK_capItemsForObject(urn) {
    var obj = this._coop.get(urn);
    if (!obj || !obj.capItems)
      return;

    var maxItems = obj.maxItems;
    if (maxItems <= 0)
      maxItems = DEFAULT_ITEM_CAP;

    var removeCount = obj.count - maxItems;
    if (removeCount <= 0)
      return;

    this._logger.info('Removing ' + removeCount + ' items from ' + urn);

    var itemsToRemove = [];

    var items = obj.children.enumerate();
    while (items && items.hasMoreElements()) {
      var item = items.getNext();
      if (item && !item.flagged) {
        itemsToRemove.push(item);
        if (itemsToRemove.length >= removeCount)
          break;
      }
    }

    for each (var item in itemsToRemove) {
      this._logger.info('Removing ' + item.id());
      obj.children.remove(item);
      item.destroy();
    }
  },

  _capItemsForAllObjects: function SHK__capItemsForAllObjects() {
    var RDFS = Cc['@mozilla.org/rdf/rdf-service;1']
      .getService(Ci.nsIRDFService);
    var capItems = RDFS.GetResource('http://flock.com/rdf#capItems');

    var objs = this._faves.GetSources(capItems, RDFS.GetLiteral('true'), true);

    var gc = this;
 
    var cap = {
      notify: function(timer) {
        var start = Date.now();

        while (objs.hasMoreElements()) {
          var obj = objs.getNext().QueryInterface(Ci.nsIRDFResource);
          gc.capItemsForObject(obj.Value);

          if (Date.now() > start + CAP_RUN_INTERVAL)
            return;
        }

        timer.cancel();
      }
    };
 
    var timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
    timer.initWithCallback(cap, CAP_SLEEP_INTERVAL,
                           Ci.nsITimer.TYPE_REPEATING_SLACK);
  },

  getInterfaces: function SHK_getInterfaces(countRef) {
    var interfaces = [Ci.flockIStreamHousekeeping, Ci.flockIHousekeeping,
                      Ci.nsIClassInfo, Ci.nsISupports];
    countRef.value = interfaces.length;
    return interfaces;
  },
  getHelperForLanguage: function SHK_getHelperForLanguage(language) {
    return null;
  },
  contractID: SHK_CONTRACTID,
  classDescription: SHK_CLASSNAME,
  classID: SHK_CLASSID,
  implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  flags: Ci.nsIClassInfo.SINGLETON,

  QueryInterface: function SHK_QueryInterface(iid) {
    if (iid.equals(Ci.flockIStreamHousekeeping) ||
        iid.equals(Ci.flockIHousekeeping) ||
        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(SHK_CLASSID))
      return new GenericComponentFactory(StreamHousekeeping)

    throw Cr.NS_ERROR_NO_INTERFACE;
  },

  registerSelf: function(cm, file, location, type) {
    var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
    cr.registerFactoryLocation(SHK_CLASSID, SHK_CLASSNAME, SHK_CONTRACTID,
                               file, location, type);

    var catman = Cc['@mozilla.org/categorymanager;1']
      .getService(Ci.nsICategoryManager);
    catman.addCategoryEntry('flockHousekeeping', SHK_CLASSNAME, SHK_CONTRACTID,
                            true, true);
  },

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

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

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