// 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 DF_CONTRACTID = '@flock.com/date-formatter;1';
const DF_CLASSID    = Components.ID('3c4c0b89-82e0-4d08-938c-6d5ede31a784');
const DF_CLASSNAME  = 'Flock Date Formatter';

const URI_DF_PROPERTIES = 'chrome://flock/locale/common/flockDateFormatter.properties';


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


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


const MSECS_PER_DAY = 24 * 60 * 60 * 1000;

function DateFormatter() {
  var obs = getObserverService();
  obs.addObserver(this, 'profile-after-change', false);
}

DateFormatter.prototype = {
  _start: function DF__start() {
    var sbs = Cc['@mozilla.org/intl/stringbundle;1']
      .getService(Ci.nsIStringBundleService);
    var bundle = sbs.createBundle(URI_DF_PROPERTIES);

    this._pureDateTimeFormat = bundle.GetStringFromName("time");
    this._reallyShortFormat = bundle.GetStringFromName("time.veryshort");
    this._mediumShortFormat = bundle.GetStringFromName("time.medshort");
    this._daysAgoFormat = bundle.GetStringFromName("daysAgo");
    this._hoursAgoFormat = bundle.GetStringFromName("hoursAgo");
    this._momentAgoFormat = bundle.GetStringFromName("momentAgo");
    this._minutesAgoFormat = bundle.GetStringFromName("minutesAgo");
    this._weeksAgoFormat = bundle.GetStringFromName("weeksAgo");
    this._yearsAgoFormat = bundle.GetStringFromName("yearsAgo");

    for each (var t in ['tomorrow', 'today', 'yesterday']) {
      var tstr = bundle.GetStringFromName(t);

      this['_' + t + 'String'] = tstr;

      this['_' + t + 'DateTimeFormat'] =
        bundle.formatStringFromName('timeShort', [tstr], 1);
    }
  },

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

    switch (topic) {
      case 'profile-after-change':
        obs.removeObserver(this, 'profile-after-change');
        this._start();
        break;
    }
  },

  getTimeString: function DF_getTimeString(time) {
    var date = new Date(time);
    return date.toLocaleFormat('%I:%M %p').replace(/^0/, '');
  },
  getDateString: function DF_getDateString(time) {
    var date = new Date(time);

    var now = new Date();
    var now_date = new Date(now.toDateString());

    if (date.getTime() >= (now_date.getTime() + 2 * MSECS_PER_DAY))
      return date.toLocaleFormat('%A, %B %d, %Y').replace(' 0', ' ');
    else if (date.getTime() >= (now_date.getTime() + MSECS_PER_DAY))
      return this._tomorrowString;
    else if (date.getTime() >= now_date.getTime())
      return this._todayString;
    else if (date.getTime() >= (now_date.getTime() - MSECS_PER_DAY))
      return this._yesterdayString;
    else
      return date.toLocaleFormat('%A, %B %d, %Y').replace(' 0', ' ');
  },

  getDateTimeString: function DF_getDateTimeString(time) {
    var date = new Date(time);
    var now = new Date();

    return this._getDateTimeStringHelper(date, now, this._pureDateTimeFormat);
  },

  getShortDateTimeString: function DF_getShortDateTimeString(time) {
    var date = new Date(time);
    var now = new Date();

    var format;
    if (date.getYear() == now.getYear())
      format = this._reallyShortFormat;
    else
      format = this._mediumShortFormat;

    return this._getDateTimeStringHelper(date, now, format);
  },

  getFriendlyLastDate: function(lastUpdated) {
    var now = new Date();
    var updatedDate = Date.now() - lastUpdated;

    var secondsSince = updatedDate/1000;
    var minutesSince = secondsSince / 60;
    var hoursSince = minutesSince / 60;
    var daysSince = hoursSince / 24;
    var weeksSince = daysSince / 7;
    var yearsSince = daysSince / 365;

    if (yearsSince >= 1) {
      return yearsSince.toFixed(0) + this._yearsAgoFormat;
    } else if (daysSince > 13) {
      return weeksSince.toFixed(0) + this._weeksAgoFormat;
    } else if (daysSince >= 1) {
      return daysSince.toFixed(0) + this._daysAgoFormat;
    } else if (hoursSince >= 1) {
      return hoursSince.toFixed(0) + this._hoursAgoFormat;
    } else if (minutesSince >= 1) {
      return minutesSince.toFixed(0) + this._minutesAgoFormat;
    }

    // Invalid value check 
    if (minutesSince < 0) { 
      // Under/Overflow 
      return ""; 
    } 

    return this._momentAgoFormat;
  },

  _getDateTimeStringHelper: function DF_getDateTimeStringHelper(date, now,
                                                                format) {
    var datetime;
    var now_date = new Date(now.toDateString());

    if (date.getTime() >= (now_date.getTime() + 2 * MSECS_PER_DAY))
      datetime = date.toLocaleFormat(format);
    else if (date.getTime() >= (now_date.getTime() + MSECS_PER_DAY))
      datetime = date.toLocaleFormat(this._tomorrowDateTimeFormat);
    else if (date.getTime() >= now_date.getTime())
      datetime = date.toLocaleFormat(this._todayDateTimeFormat);
    else if (date.getTime() >= (now_date.getTime() - MSECS_PER_DAY))
      datetime = date.toLocaleFormat(this._yesterdayDateTimeFormat);
    else
      datetime = date.toLocaleFormat(format);

    return datetime.replace(/ 0/g, ' ');
  },

  getInterfaces: function DF_getInterfaces(countRef) {
    var interfaces =
        [Ci.flockIDateFormatter, Ci.nsIObserver,
         Ci.nsIClassInfo, Ci.nsISupports];
    countRef.value = interfaces.length;
    return interfaces;
  },
  getHelperForLanguage: function DF_getHelperForLanguage(language) {
    return null;
  },
  contractID: DF_CONTRACTID,
  classDescription: DF_CLASSNAME,
  classID: DF_CLASSID,
  implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  flags: Ci.nsIClassInfo.SINGLETON,

  QueryInterface: function DF_QueryInterface(iid) {
    if (iid.equals(Ci.flockIDateFormatter) ||
        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(DF_CLASSID))
      return new GenericComponentFactory(DateFormatter);

    throw Cr.NS_ERROR_NO_INTERFACE;
  },

  registerSelf: function(cm, file, location, type) {
    var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
    cr.registerFactoryLocation(DF_CLASSID,
                               DF_CLASSNAME,
                               DF_CONTRACTID,
                               file, location, type);

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

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

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

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