var Signal = require('signal')
var cadence = require('cadence')
var seedrandom = require('seedrandom')
var coalesce = require('extant')
function Demur (options) {
options || (options = {})
this._retries = coalesce(options.retires, Infinity)
this._factor = coalesce(options.factor, 2)
this._delay = coalesce(options.delay, 0)
this._minimum = coalesce(options.minimum, 1000)
this._maximum = coalesce(options.maximum, Infinity)
this._reset = coalesce(options.reset, Infinity)
this._Date = coalesce(options.Date, Date)
this._random = options.randomize
? seedrandom(options.seed || this._Date.now())
: function () { return 0.5 }
this._lastChecked = options.reset == null ? 0 : this._Date.now() - options.reset - 1
this._duration = this._minimum
this._attempt = 0
this._retrying = new Signal
}
Demur.prototype._retry = function (random) {
var now = this._Date.now()
if (this.cancelled || ++this._attempt > this._retries) {
this.cancelled = true
return 0
}
var since = now - this._lastChecked
this._lastChecked = now
if (since >= this._reset) {
this.reset()
return this._delay
}
var duration = this._duration
this._duration = Math.min(this._factor * this._duration, this._maximum)
return Math.round(duration * (1 + (random() - 0.5)))
}
Demur.prototype.retry = cadence(function (async) {
async(function () {
var duration = this._retry(this._random)
if (duration != 0) {
this._retrying.wait(async())
this._timeout = setTimeout(this._retrying.notify.bind(this._retrying), duration)
}
}, function () {
this._timeout = null
return ! this.cancelled
})
})
Demur.prototype.reset = function () {
this._duration = this._minimum
this._attempt = 0
}
Demur.prototype.cancel = function () {
this.cancelled = true
if (this._timeout != null) {
this._retrying.notify()
clearTimeout(this._timeout)
}
}
module.exports = Demur