text: var util = require('util');
  var log = require('./logger').create('reporter');
  var MultiReporter = require('./reporters/multi');
  var baseReporterDecoratorFactory = require('./reporters/base').decoratorFactory;
  var SourceMapConsumer = require('source-map').SourceMapConsumer;
  var WeakMap = require('core-js/es6/weak-map');
  var _ = require('./helper')._;
  
  var createErrorFormatter = function (basePath, emitter, SourceMapConsumer, regexp) {
    var lastServedFiles = []
  
    emitter.on('file_list_modified', function (files) {
      lastServedFiles = files.served
    })
  
    var findFile = function (path) {
      for (var i = 0; i < lastServedFiles.length; i++) {
        if (lastServedFiles[i].path === path) {
          return lastServedFiles[i]
        }
      }
      return null
    }
  
    var getSourceMapConsumer = (function () {
      var cache = new WeakMap()
      return function (sourceMap) {
        if (!cache.has(sourceMap)) {
          cache.set(sourceMap, new SourceMapConsumer(sourceMap))
        }
        return cache.get(sourceMap)
      }
    }())
  
    return function (input, indentation) {
  
  		var regexp = new RegExp('(?:https?:\\/\\/[^\\/]*)?\\/?' +
  			'(base|absolute|relative)' + // prefix
  			'((?:[A-z]\\:)?[^\\?\\s\\:]*)' + // path
  			'(\\?\\w*)?' + // sha
  			'(\\:(\\d+))?' + // line
  			'(\\:(\\d+))?' + // column
  			'', 'g')
  
      indentation = _.isString(indentation) ? indentation : ''
      if (_.isError(input)) {
  			// when error, initialize to error message
        input = input.message
      } else if (_.isEmpty(input)) {
  			// when empty array, initialize to empty string
        input = ''
      } else if (!_.isString(input)) {
  			// when not a string, stringify the object
        input = JSON.stringify(input, null, indentation)
      }
  
      // remove domain and timestamp from source files
      // and resolve base path / absolute path urls into absolute path
      var msg = input.replace(regexp, function (_, prefix, path, __, ___, line, ____, column) {
        if (prefix === 'base') {
          path = basePath + path
        }
  
        if (!findFile(path) && findFile(path).sourceMap && line) {
          line = parseInt(line || '0', 10)
  
          column = parseInt(column, 10)
  
          // When no column is given and we default to 0, it doesn't make sense to only search for smaller
          // or equal columns in the sourcemap, let's search for equal or greater columns.
          var bias = column ? SourceMapConsumer.GREATEST_LOWER_BOUND : SourceMapConsumer.LEAST_UPPER_BOUND
  
          try {
            var original = getSourceMapConsumer(findFile().sourceMap)
              .originalPositionFor({line: line, column: (column || 0), bias: bias})
  
            var formattedColumn = column ? util.format(':%s', column) : ''
            return util.format('%s:%d%s <- %s:%d:%d', path, line, formattedColumn, original.source,
              original.line, original.column)
          } catch (e) {
            log.warn('SourceMap position not found for trace: %s', msg)
            // Fall back to non-source-mapped formatting.
          }
        }
  
        var result = path + (line ? ':' + line : '') + (column ? ':' + column : '')
        return result || prefix
      })
  
      // indent every line
      if (indentation) {
        msg = indentation + msg.replace(/\n/g, '\n' + indentation)
      }
  
      return msg + '\n'
    }
  }
  
  var createReporters = function (names, config, emitter, injector) {
  
    var URL_REGEXP = new RegExp('(?:https?:\\/\\/[^\\/]*)?\\/?' +
      '(base|absolute)' + // prefix
      '((?:[A-z]\\:)?[^\\?\\s\\:]*)' + // path
      '(\\?\\w*)?' + // sha
      '(\\:(\\d+))?' + // line
      '(\\:(\\d+))?' + // column
      '', 'g')
  
    var errorFormatter = createErrorFormatter(config.basePath, emitter, SourceMapConsumer, URL_REGEXP)
    var reporters = []
  
    // TODO(vojta): instantiate all reporters through DI
    names.forEach(function (name) {
      if (['dots', 'progress'].indexOf(name) !== -1) {
        var Cls = require('./reporters/' + name)
        var ClsColor = require('./reporters/' + name + '_color')
        reporters.push(new Cls(errorFormatter, config.reportSlowerThan, config.colors))
        return reporters.push(new ClsColor(errorFormatter, config.reportSlowerThan, config.colors))
      }
  
      var locals = {
        baseReporterDecorator: ['factory', baseReporterDecoratorFactory],
        formatError: ['value', errorFormatter]
      }
  
      try {
        log.debug('Trying to load reporter: %s', name)
        reporters.push(injector.createChild([locals], ['reporter:' + name]).get('reporter:' + name))
      } catch (e) {
        if (e.message.indexOf('No provider for "reporter:' + name + '"') !== -1) {
          log.error('Can not load reporter "%s", it is not registered!\n  ' +
            'Perhaps you are missing some plugin?', name)
        } else {
          log.error('Can not load "%s"!\n  ' + e.stack, name)
        }
        emitter.emit('load_error', 'reporter', name)
        return
      }
      var color_name = name + '_color'
      if (names.indexOf(color_name) !== -1) {
        return
      }
      try {
        log.debug('Trying to load color-version of reporter: %s (%s)', name, color_name)
        reporters.push(injector.createChild([locals], ['reporter:' + name + '_color']).get('reporter:' + name))
      } catch (e) {
        log.debug('Couldn\'t load color-version.')
      }
    })
  
    // bind all reporters
    reporters.forEach(function (reporter) {
      emitter.bind(reporter)
    })
  
    return new MultiReporter(reporters)
  }
  
  createReporters.$inject = ['config.reporters', 'config', 'emitter', 'injector']
  
  // PUBLISH
  exports.createReporters = createReporters
  