Lingr.Timestamp = Class.create({
  initialize: function() {
    this.timer = null;
    
    this.parent = document.documentElement;
    
    Element.select(this.parent, '.timestamp').each(function(e) {
      this.updateTimestamp(e);
    }.bind(this));
    
    this.setTimer(this.parent);
  },
  
  updateTimestamp: function(e) {
    var iso8601Time = e.down('.iso8601').innerHTML;
    
    if (!iso8601Time.blank()) {
      var datetime = new Date()
      datetime.updateFromISO8601(iso8601Time);
      
      var diff = (new Date() - datetime) / 1000;
      
      e.down('.relative').update(this.humanizeDifference(diff));
      
      if (diff < 300) {
        e.addClassName('hot');
      }
      
      e.title = datetime.strftime('%i:%M%P on %B %D%Z');
    }
  },
  
  setTimer: function(parent) {
    if (!this.timer) {
      this.timer = function(parent) {
        this.timer = null;
        
        Element.select(parent, '.hottest').invoke('removeClassName','hottest');
        
        $(this.parent).getElementsBySelector('.timestamp').each(function(e) {
          this.updateTimestamp(e);
        }.bind(this));
        
        this.setTimer(parent);
      }.bind(this).delay(60, parent);
    }
  },
  
  humanizeDifference: function(diff) {
    if (diff < 60) {
      return 'now';
    }
    else if (diff < 3600) {
      return Lingr.Text.pluralize(Math.floor(diff / 60), 'minute') + ' ago';
    }
    else if (diff < 86400) {
      return Lingr.Text.pluralize(Math.floor(diff / 3600), 'hour') + ' ago';
    }
    else {
      return Lingr.Text.pluralize(Math.floor(diff / 86400), 'day') + ' ago';
    }
  },
  
  pluralize: function(count, singular, plural, space, zero_str) {
    return count == 0 ? (zero_str || '') : (count + (space || ' ') + (count > 1 ? (plural || (singular + 's')) : singular));
  }
});

Date.prototype.updateFromISO8601 = function (string) {
  	var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
      	"(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
      	"(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
  	var d = string.match(new RegExp(regexp));
    
  	var offset = 0;
  	var date = new Date(d[1], 0, 1);
    
  	if (d[3]) { date.setMonth(d[3] - 1); }
  	if (d[5]) { date.setDate(d[5]); }
  	if (d[7]) { date.setHours(d[7]); }
  	if (d[8]) { date.setMinutes(d[8]); }
  	if (d[10]) { date.setSeconds(d[10]); }
  	if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
  	if (d[14] && d[14] != 'Z') {
      	offset = (Number(d[16]) * 60) + Number(d[17]);
      	offset *= ((d[15] == '-') ? 1 : -1);
  	}
    
  	offset -= date.getTimezoneOffset();
  	time = (Number(date) + (offset * 60 * 1000));
  	this.setTime(Number(time));
};

Date.prototype.toISO8601 = function (format, offset) {
  	/* accepted values for the format [1-6]:
     	1 Year:
     	  YYYY (eg 1997)
      2 Year and month:
        YYYY-MM (eg 1997-07)
      3 Complete date:
        YYYY-MM-DD (eg 1997-07-16)
      4 Complete date plus hours and minutes:
        YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
      5 Complete date plus hours, minutes and seconds:
        YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
      6 Complete date plus hours, minutes, seconds and a decimal
        fraction of a second
        YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
  	*/
  	if (!format) { var format = 6; }
  	if (!offset) {
      	var offset = 'Z';
      	var date = this;
  	} else {
      	var d = offset.match(/([-+])([0-9]{2}):([0-9]{2})/);
      	var offsetnum = (Number(d[2]) * 60) + Number(d[3]);
      	offsetnum *= ((d[1] == '-') ? -1 : 1);
      	var date = new Date(Number(Number(this) + (offsetnum * 60000)));
  	}

  	var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; };

  	var str = "";
  	str += date.getUTCFullYear();
  	if (format > 1) { str += "-" + zeropad(date.getUTCMonth() + 1); }
  	if (format > 2) { str += "-" + zeropad(date.getUTCDate()); }
  	if (format > 3) {
      	str += "T" + zeropad(date.getUTCHours()) +
             	":" + zeropad(date.getUTCMinutes());
  	}
  	if (format > 5) {
      	var secs = Number(date.getUTCSeconds() + "." +
                 	((date.getUTCMilliseconds() < 100) ? '0' : '') +
                 	zeropad(date.getUTCMilliseconds()));
      	str += ":" + zeropad(secs);
  	} else if (format > 4) { str += ":" + zeropad(date.getUTCSeconds()); }

  	if (format > 3) { str += offset; }
  	return str;
};

/* other support functions -- thanks, ecmanaut! */
var strftime_funks = {
  zeropad: function( n ){ return n>9 ? n : '0'+n; },
  a: function(t) { return ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][t.getDay()]; },
  A: function(t) { return ['Sunday','Monday','Tuedsay','Wednesday','Thursday','Friday','Saturday'][t.getDay()]; },
  b: function(t) { return ['Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'][t.getMonth()]; },
  B: function(t) { return ['January','February','March','April','May','June', 'July','August',
      'September','October','November','December'][t.getMonth()]; },
  c: function(t) { return t.toString(); },
  C: function(t) { 
    var now = new Date();
    if (now.getFullYear() != t.getFullYear() ||
      now.getMonth() != t.getMonth() ||
      now.getDate() != t.getDate()) {
        return " (" + strftime_funks['B'](t) + " " + strftime_funks['D'](t) + strftime_funks['Z'](t) + ")";
      }
      else
        return "";
    },
  d: function(t) { return this.zeropad(t.getDate()); },
  D: function(t) { return t.getDate(); },
  H: function(t) { return this.zeropad(t.getHours()); },
  h: function(t) { return t.getHours(); },
  I: function(t) { return this.zeropad(((t.getHours() + 12) % 12) || 12); },
  i: function(t) { return (((t.getHours() + 12) % 12) || 12); },
  m: function(t) { return this.zeropad(t.getMonth()+1); }, // month-1
  M: function(t) { return this.zeropad(t.getMinutes()); },
  p: function(t) { return this.H(t) < 12 ? 'AM' : 'PM'; },
  P: function(t) { return this.H(t) < 12 ? 'am' : 'pm'; },
  S: function(t) { return this.zeropad(t.getSeconds()); },
  w: function(t) { return t.getDay(); }, // 0..6 == sun..sat
  y: function(t) { return this.zeropad(this.Y(t) % 100); },
  Y: function(t) { return t.getFullYear(); },
  Z: function(t) { 
    var now = new Date();
    if (now.getFullYear() != t.getFullYear()) {
      return " " + t.getFullYear();
    }
    else
      return "";
    },
  '%': function(t) { return '%'; }
};

Date.prototype.strftime = function (fmt) {
    var t = this;
    for (var s in strftime_funks) {
        if (s.length == 1 )
            fmt = fmt.replace('%' + s, strftime_funks[s](t));
    }
    return fmt;
};

if (typeof(TrimPath) != 'undefined') {
    TrimPath.parseTemplate_etc.modifierDef.strftime = function (t, fmt) {
        return new Date(t).strftime(fmt);
    };
}
