// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @constructor
 * @class FunctionSequence to invoke steps in sequence
 *
 * @param steps             array of functions to invoke in parallel
 * @param callback          callback to invoke on success
 * @param failureCallback   callback to invoke on failure
 */
function FunctionParallel(name, steps, logger, callback, failureCallback) {
  // Private variables hidden in closure
  this.currentStepIdx_ = -1;
  this.failed_ = false;
  this.steps_ = steps;
  this.callback_ = callback;
  this.failureCallback_ = failureCallback;
  this.logger = logger;
  this.name = name;

  this.remaining = this.steps_.length;

  this.nextStep = this.nextStep_.bind(this);
  this.onError = this.onError_.bind(this);
  this.apply = this.start.bind(this);
}


/**
 * Error handling function, which fires error callback.
 *
 * @param err error message
 */
FunctionParallel.prototype.onError_ = function(err) {
  if (!this.failed_) {
    this.failed_ = true;
    this.failureCallback_(err);
  }
};

/**
 * Advances to next step. This method should not be used externally. In external
 * cases should be used nextStep function, which is defined in closure and thus
 * has access to internal variables of functionsequence.
 */
FunctionParallel.prototype.nextStep_ = function() {
  if (--this.remaining == 0 && !this.failed_) {
    this.callback_();
  }
};

/**
 * This function should be called only once on start, so start all the children
 * at once
 */
FunctionParallel.prototype.start = function(var_args) {
  this.logger.vlog('Starting [' + this.steps_.length + '] parallel tasks with '
                    + arguments.length + ' argument(s)');
  if (this.logger.verbose) {
    for (var j = 0; j < arguments.length; j++) {
      this.logger.vlog(arguments[j]);
    }
  }
  for (var i=0; i < this.steps_.length; i++) {
    this.logger.vlog('Attempting to start step [' + this.steps_[i].name + ']');
    try {
      this.steps_[i].apply(this, arguments);
    } catch(e) {
      this.onError(e.toString());
    }
  }
};