/**
 * Format any UTC timestamp on the page to the users local timezone.
 */
var DateFormat = {
	interval: null,
  autoParse: function(){
    $$('span.timestamp').each(function(span) {
      var utc = Date.parseUTC(span.title);
      var rel = span.getAttribute('rel');
      span.update(rel == 'words' ? utc.timeAgoInWords() : utc.strftime(rel))
    });
  },
	setInterval: function(milliseconds) {
		DateFormat.interval = window.setInterval(DateFormat.autoParse, milliseconds);
	}
}

Object.extend(Date.prototype, {
  /**
   * Given a formatted string, replace the necessary items and return.
   * Example: Time.now().strftime("%B %d, %Y") => February 11, 2008
   * @param {String} format The formatted string used to format the results
   */
  strftime: function(format) {
    var day = this.getDay(), month = this.getMonth();
    var hours = this.getHours(), minutes = this.getMinutes();
    function pad(num) { return num.toPaddedString(2); };
    return format.gsub(/\%([aAbBcdHImMpSwyY])/, function(part) {
      switch(part[1]) {
        case 'a': return $w("Sun Mon Tue Wed Thu Fri Sat")[day]; break;
        case 'A': return $w("Sunday Monday Tuesday Wednesday Thursday Friday Saturday")[day]; break;
        case 'b': return $w("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")[month]; break;
        case 'B': return $w("January February March April May June July August September October November December")[month]; break;
        case 'c': return this.toString(); break;
        case 'd': return pad(this.getDate()); break;
        case 'H': return pad(hours); break;
        case 'I': return pad((hours + 12) % 12); break;
        case 'm': return pad(month + 1); break;
        case 'M': return pad(minutes); break;
        case 'p': return hours > 12 ? 'PM' : 'AM'; break;
        case 'S': return pad(this.getSeconds()); break;
        case 'w': return day; break;
        case 'y': return pad(this.getFullYear() % 100); break;
        case 'Y': return this.getFullYear().toString(); break;
      }
    }.bind(this));
  },
  
  timeAgoInWords: function() {
    var relative_to = (arguments.length > 0) ? arguments[1] : new Date();
    return Date.distanceOfTimeInWords(this, relative_to, arguments[2], true);
  }
});

Object.extend(Date, {
  /**
   * Common formats passed to strftime
   */
  strftimeFormats: {
    time: "%I:%M %p",
  	day:  "%B %d",
  	short: '%b %d',
  	dayName: "%A"
  },
  
  /**
   * Get an array back with hours, minutes and seconds from now to a future date.
   * @param {Date} to The future time used to get equate the difference
   * @return {Array} [hours, minutes, seconds]
   */
  differenceFromNow: function(to) {
    var seconds = Math.ceil((new Date().getTime() - to.getTime()) / 1000);
    var hours   = Math.floor(seconds / 3600).toPaddedString(2);
    seconds     = Math.floor(seconds % 3600);
    var minutes = Math.floor(seconds / 60).toPaddedString(2);
    seconds = (seconds % 60).toPaddedString(2);
    return [hours, minutes, seconds];
  },
  
  /**
   * Parse a string date and return a UTC date
   * @param {String} value Formatted date string
   * @return Date
   */
  parseUTC: function(value) {
    var localDate = new Date(value);
    var utcSeconds = Date.UTC(localDate.getFullYear(), localDate.getMonth(), localDate.getDate(), localDate.getHours(), localDate.getMinutes(), localDate.getSeconds())
    return new Date(utcSeconds);
  },
  
  /**
   * Return the distance of time in words between two Dates
   * Example: '5 days ago', 'about an hour ago'
   * @param {Date} fromTime The start date to use in the calculation
   * @param {Date} toTime The end date to use in the calculation
   * @param {Boolean} Include the time in the output
   */
  distanceOfTimeInWords: function(fromTime, toTime, includeTime) {
    var delta = parseInt((toTime.getTime() - fromTime.getTime()) / 1000);
    if(delta < 60) {
        return 'less than a minute ago';
    } else if(delta < 120) {
        return 'about a minute ago';
    } else if(delta < (45*60)) {
        return (parseInt(delta / 60)).toString() + ' minutes ago';
    } else if(delta < (120*60)) {
        return 'about an hour ago';
    } else if(delta < (24*60*60)) {
        return 'about ' + (parseInt(delta / 3600)).toString() + ' hours ago';
    } else if(delta < (48*60*60)) {
        return '1 day ago';
    } else {
      var days = (parseInt(delta / 86400)).toString();
      if(days > 10) {
        var fmt  = '%B %d'
        if(toTime.getYear() != fromTime.getYear()) { fmt += ', %Y' }
        if(includeTime) fmt += ' %I:%M %p'
        return fromTime.strftime(fmt);
      } else {
        return days + " days ago"
      }
    }
  }
});

// Time helpers by MARTIN STRÖM
Object.extend(Number.prototype, {
  seconds: function() {
    return this * 1000;
  },
  
  minutes: function() {
    return this * (60).seconds();
  },
  
  hours: function() {
    return this * (60).minutes();
  },
  
  days: function() {
    return this * (24).hours();
  },
  
  weeks: function(args) {
    return this * (7).days();
  },
  
  fortnights: function() {
    return this * (2).weeks();
  },
  
  months: function() {
    return this * (30).days();
  },
  
  years: function() {
    return parseInt(this * (365.25).days())
  },
  
  since: function(time) {
    time = time || new Date();
    if (time instanceof Date) time = time.getTime();
    return this + time;
  },
  
  ago: function(time) {
    time = time || new Date();
    if (time instanceof Date) time = time.getTime();
    return time - this;
  },
  
  toDate: function() {
    var date = new Date();
    date.setTime(this);
    return date;
  }
});

Object.extend(Number.prototype, {
  second:    Number.prototype.seconds,
  minute:    Number.prototype.minutes,
  hour:      Number.prototype.hours,
  day:       Number.prototype.days,
  week:      Number.prototype.weeks,
  fortnight: Number.prototype.fortnights,
  month:     Number.prototype.months,
  year:      Number.prototype.years,
  from_now:  Number.prototype.since,
  fromNow:   Number.prototype.since,
  until:     Number.prototype.ago
});