var util = require('util');

/**
 * @class Recorder
 * @param {{retention: <Number>}} [options]
 */
var Recorder = function(options) {
  this._records = [];
  this._options = _.defaults(options || {}, {
    retention: 300,
    format: '[{date} {level}] {message}'
  });
};

Recorder.prototype = {
  /**
   * @returns {String}
   */
  getFormattedRecords: function() {
    return _.map(this.getRecords(), function(record) {
      return this._recordFormatter(record);
    }, this).join('\n');
  },

  /**
   * @returns {{date: {Date}, messages: *[], context: {Object}}[]}
   */
  getRecords: function() {
    return this._records;
  },

  /**
   * @param {*[]} messages
   * @param {Object} context
   */
  addRecord: function(messages, context) {
    var record = {
      date: this._getDate(),
      messages: messages,
      context: context
    };
    this._records.push(record);
    this._cleanupRecords();
  },

  /**
   * @private
   */
  _cleanupRecords: function() {
    if (this._options.retention) {
      var retentionTime = this._getDate() - (this._options.retention * 1000);
      this._records = _.filter(this._records, function(record) {
        return record.date > retentionTime;
      });
    }
  },

  /**
   * @param {{date: {Date}, messages: *[], context: {Object}}} record
   * @returns {String}
   * @private
   */
  _recordFormatter: function(record) {
    var log = this._options.format;
    _.each({
      date: record.date.toISOString(),
      level: record.context.level.name,
      message: this._messageFormatter(record.messages)
    }, function(value, key) {
      var pattern = new RegExp('{' + key + '}', 'g');
      log = log.replace(pattern, value);
    });
    return log;
  },

  /**
   * @param {*[]} messages
   * @returns {String}
   * @private
   */
  _messageFormatter: function(messages) {
    var clone = _.toArray(messages);
    if (clone.length > 0 && _.isString(clone[0])) {
      clone[0] = clone[0].replace(/%o/g, '%j');
    }
    return util.format.apply(util.format, clone);
  },

  /**
   * @returns {Date}
   * @private
   */
  _getDate: function() {
    return new Date();
  }
};


module.exports = Recorder;

@@ -216,16 +216,37 @@
 n: 300,%0A
+    jsonMaxSize: 50,%0A
     form
@@ -278,16 +278,16 @@
 ssage%7D'%0A
-
   %7D);%0A%7D;
@@ -923,32 +923,91 @@
 ecords();%0A  %7D,%0A%0A
+  flushRecords: function() %7B%0A    this._records = %5B%5D;%0A  %7D,%0A%0A
   /**%0A   * @priv
@@ -1951,102 +1951,1526 @@
     
-if (clone.length %3E 0 && _.isString(clone%5B0%5D)) %7B%0A      clone%5B0%5D = clone%5B0%5D.replace(/%25o/g, '%25j')
+var index, value, encoded;%0A    for (index = 0; index %3C clone.length; index++) %7B%0A      encoded = value = clone%5Bindex%5D;%0A%0A      if (_.isString(value) && 0 === index) %7B%0A        // about console.log and util.format substitution,%0A        // see https://developers.google.com/web/tools/chrome-devtools/debug/console/console-write#string-substitution-and-formatting%0A        // and https://nodejs.org/api/util.html#util_util_format_format%0A        value = value.replace(/%25%5BidfoO%5D/g, '%25s');%0A      %7D else if (value instanceof RegExp) %7B%0A        value = value.toString();%0A      %7D else if (value instanceof Date) %7B%0A        value = value.toISOString();%0A      %7D else if (_.isObject(value) && value._class) %7B%0A        value = '%5B' + value._class + (value._id && value._id.id ? ':' + value._id.id : '') + '%5D';%0A      %7D else if (_.isObject(value) && /%5E%5C%5Bobject ((?!Object).)+%5C%5D$/.test(value.toString())) %7B%0A        value = value.toString();%0A      %7D%0A%0A      try %7B%0A        if (_.isString(value) %7C%7C _.isNumber(value)) %7B%0A          encoded = value;%0A        %7D else %7B%0A          encoded = JSON.stringify(value);%0A          if (encoded.length %3E this._options.jsonMaxSize) %7B%0A            encoded = encoded.slice(0, this._options.jsonMaxSize - 4) + '%E2%80%A6' + encoded%5Bencoded.length - 1%5D;%0A          %7D%0A        %7D%0A      %7D catch (e) %7B%0A        if (_.isUndefined(value)) %7B%0A          encoded = 'undefined';%0A        %7D else if (_.isNull(value)) %7B%0A          encoded = 'null';%0A        %7D else %7B%0A          encoded = '%5Bunknown%5D'%0A        %7D%0A      %7D%0A      clone%5Bindex%5D = encoded
 ;%0A  

Improve recorder message formatting