//
// used by properLockFile
// Source: https://github.com/tim-kos/node-retry
//

var RetryOperation = require('./retry_operation');

exports.operation = function (options) {
  var timeouts = exports.timeouts(options);
  return new RetryOperation(timeouts, {
    forever: options && options.forever,
    unref: options && options.unref,
    maxRetryTime: options && options.maxRetryTime
  });
};

exports.timeouts = function (options) {
  if (options instanceof Array) {
    return [].concat(options);
  }

  var opts = {
    retries: 10,
    factor: 2,
    minTimeout: 1 * 1000,
    maxTimeout: Infinity,
    randomize: false
  };
  for (var key in options) {
    opts[key] = options[key];
  }

  if (opts.minTimeout > opts.maxTimeout) {
    throw new Error('minTimeout is greater than maxTimeout');
  }

  var timeouts = [];
  for (var i = 0; i < opts.retries; i++) {
    timeouts.push(this.createTimeout(i, opts));
  }

  if (options && options.forever && !timeouts.length) {
    timeouts.push(this.createTimeout(i, opts));
  }

  // sort the array numerically ascending
  timeouts.sort(function (a, b) {
    return a - b;
  });

  return timeouts;
};

exports.createTimeout = function (attempt, opts) {
  var random = (opts.randomize)
    ? (Math.random() + 1)
    : 1;

  var timeout = Math.round(random * opts.minTimeout * Math.pow(opts.factor, attempt));
  timeout = Math.min(timeout, opts.maxTimeout);

  return timeout;
};

exports.wrap = function (obj, options, methods) {
  if (options instanceof Array) {
    methods = options;
    options = null;
  }

  if (!methods) {
    methods = [];
    for (var key in obj) {
      if (typeof obj[key] === 'function') {
        methods.push(key);
      }
    }
  }

  for (var i = 0; i < methods.length; i++) {
    var method = methods[i];
    var original = obj[method];

    obj[method] = function retryWrapper(original) {
      var op = exports.operation(options);
      var args = Array.prototype.slice.call(arguments, 1);
      var callback = args.pop();

      args.push(function (err) {
        if (op.retry(err)) {
          return;
        }
        if (err) {
          arguments[0] = op.mainError();
        }
        callback.apply(this, arguments);
      });

      op.attempt(function () {
        original.apply(obj, args);
      });
    }.bind(obj, original);
    obj[method].options = options;
  }
};