text: var log = require('./logger').create('launcher')
  var Promise = require('bluebird')
  
  var baseDecorator = require('./launchers/base').decoratorFactory
  var captureTimeoutDecorator = require('./launchers/capture_timeout').decoratorFactory
  var retryDecorator = require('./launchers/retry').decoratorFactory
  var processDecorator = require('./launchers/process').decoratorFactory
  
  // TODO(vojta): remove once nobody uses it
  var baseBrowserDecoratorFactory = function (baseLauncherDecorator, captureTimeoutLauncherDecorator,
    retryLauncherDecorator, processLauncherDecorator) {
    return function (launcher) {
      baseLauncherDecorator(launcher)
      captureTimeoutLauncherDecorator(launcher)
      retryLauncherDecorator(launcher)
      processLauncherDecorator(launcher)
    }
  }
  
  var Launcher = function (emitter, injector) {
    var browsers = []
    var lastStartTime
  
    var getBrowserById = function (id) {
      for (var i = 0; i < browsers.length; i++) {
        if (browsers[i].id === id) {
          return browsers[i]
        }
      }
  
      return null
    }
  
    this.launch = function (names, protocol, hostname, port, urlRoot) {
      var browser
      var url = protocol + '//' + hostname + ':' + port + urlRoot
  
      lastStartTime = Date.now()
  
      names.forEach(function (name) {
        var locals = {
          id: ['value', Launcher.generateId()],
          name: ['value', name],
          baseLauncherDecorator: ['factory', baseDecorator],
          captureTimeoutLauncherDecorator: ['factory', captureTimeoutDecorator],
          retryLauncherDecorator: ['factory', retryDecorator],
          processLauncherDecorator: ['factory', processDecorator],
          baseBrowserDecorator: ['factory', baseBrowserDecoratorFactory]
        }
  
        // TODO(vojta): determine script from name
        if (name.indexOf('/') !== -1) {
          name = 'Script'
        }
  
        try {
          browser = injector.createChild([locals], ['launcher:' + name]).get('launcher:' + name)
        } catch (e) {
          if (e.message.indexOf('No provider for "launcher:' + name + '"') !== -1) {
            log.warn('Can not load "%s", it is not registered!\n  ' +
              'Perhaps you are missing some plugin?', name)
          } else {
            log.warn('Can not load "%s"!\n  ' + e.stack, name)
          }
  
          return
        }
  
        // TODO(vojta): remove in v1.0 (BC for old launchers)
        if (!browser.forceKill) {
          browser.forceKill = function () {
            var self = this
  
            return new Promise(function (resolve) {
              self.kill(resolve)
            })
          }
  
          browser.restart = function () {
            var self = this
            this.kill(function () {
              self.start(url)
            })
          }
        }
  
        log.info('Starting browser %s', browser.name)
        browser.start(url)
        browsers.push(browser)
      })
  
      return browsers
    }
  
    this.launch.$inject = ['config.browsers', 'config.protocol', 'config.hostname', 'config.port', 'config.urlRoot']
  
    this.kill = function (id, callback) {
      var browser = getBrowserById(id)
      callback = callback || function () {}
  
      if (!browser) {
        process.nextTick(callback)
        return false
      }
  
      browser.forceKill().then(callback)
      return true
    }
  
    this.restart = function (id) {
      var browser = getBrowserById(id)
  
      if (!browser) {
        return false
      }
  
      browser.restart()
      return true
    }
  
    this.killAll = function (callback) {
      log.debug('Disconnecting all browsers')
  
      var remaining = 0
      var finish = function () {
        remaining--
        if (!remaining && callback) {
          callback()
        }
      }
  
      if (!browsers.length) {
        return process.nextTick(callback)
      }
  
      browsers.forEach(function (browser) {
        remaining++
        browser.forceKill().then(finish)
      })
    }
  
    this.areAllCaptured = function () {
      return !browsers.some(function (browser) {
          return !browser.isCaptured()
        })
    }
  
    this.markCaptured = function (id) {
      browsers.forEach(function (browser) {
        if (browser.id === id) {
          browser.markCaptured()
          log.debug('%s (id %s) captured in %d secs', browser.name, browser.id,
            (Date.now() - lastStartTime) / 1000)
        }
      })
    }
  
    // register events
    emitter.on('exit', this.killAll)
  }
  
  Launcher.$inject = ['emitter', 'injector']
  
  Launcher.generateId = function () {
    return '' + Math.floor(Math.random() * 100000000)
  }
  
  // PUBLISH
  exports.Launcher = Launcher
  