var Cookie = {
  version: '0.7',
  cookies: {},
  _each: function(iterator) {
    $H(this.cookies).each(iterator);
  },
  
  getAll: function() {
    this.cookies = {};
    $A(document.cookie.split('; ')).each(function(cookie) {
      var seperator = cookie.indexOf('=');
      this.cookies[cookie.substring(0, seperator)] = 
          unescape(cookie.substring(seperator + 1, cookie.length));
    }.bind(this));
    return this.cookies;
  },
  
  read: function() {
    var cookies = $A(arguments), results = [];
    this.getAll();
    cookies.each(function(name) {
      if (this.cookies[name]) results.push(this.cookies[name]);
      else results.push(null);
    }.bind(this));
    return results.length > 1 ? results : results[0];
  },
  
  write: function(cookies, options) {
    if (cookies.constructor == Object && cookies.name) cookies = [cookies];
    if (cookies.constructor == Array) {
      $A(cookies).each(function(cookie) {
        this._write(cookie.name, cookie.value, cookie.expires,
                    cookie.path, cookie.domain);
      }.bind(this));
    }else {
      options = options || {expires: false, path: '', domain: ''};
      for (name in cookies){
        this._write(name, cookies[name],
                    options.expires, options.path, options.domain);
      }
    }
  },
  
  _write: function(name, value, expires, path, domain) {
    if (name.indexOf('=') != -1) return;
    var cookieString = name + '=' + escape(value);
    if (expires) cookieString += '; expires=' + expires.toGMTString();
    if (path) cookieString += '; path=' + path;
    if (domain) cookieString += '; domain=' + domain;
    document.cookie = cookieString;
  },
  
  erase: function(cookies) {
    var cookiesToErase = {};
    $A(arguments).each(function(cookie) {
      cookiesToErase[cookie] = '';
    });
    this.write(cookiesToErase, {expires: (new Date((new Date()).getTime() - 1e11)), path: '/'});
    this.getAll();
  },
  
  eraseAll: function() {
    this.erase.apply(this, $H(this.getAll()).keys());
  }
};

Object.extend(Cookie, {
  get: Cookie.read,
  set: Cookie.write,
  
  add: Cookie.read,
  remove: Cookie.erase,
  removeAll: Cookie.eraseAll,
  
  wipe: Cookie.erase,
  wipeAll: Cookie.eraseAll,
  destroy: Cookie.erase,
  destroyAll: Cookie.eraseAll
});

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

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]);
  }
});

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 'a minute ago'; //return 'less than a minute ago';
    } else if(delta < 120) {
        return 'a minute ago'; //return 'about a minute ago';
    } else if(delta < (45*60)) {
        return (parseInt(delta / 60)).toString() + ' minutes ago';
    } else if(delta < (120*60)) {
        return 'an hour ago'; //return 'about an hour ago';
    } else if(delta < (24*60*60)) {
        return (parseInt(delta / 3600)).toString() + ' hours ago'; //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 > 5) {
        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
});