text: 'use strict';
  
  const util = require('util');
  const Base = require('../structures/Base');
  
  /**
  * Handle ratelimiting something
  * @prop {Number} interval How long (in ms) to wait between clearing used tokens
  * @prop {Number} lastReset Timestamp of last token clearing
  * @prop {Number} lastSend Timestamp of last token consumption
  * @prop {Number} tokenLimit The max number tokens the bucket can consume per interval
  * @prop {Number} tokens How many tokens the bucket has consumed in this interval
  */
  class Bucket {
    /**
      * Construct a Bucket
      * @arg {Number} tokenLimit The max number of tokens the bucket can consume per interval
      * @arg {Number} interval How long (in ms) to wait between clearing used tokens
      * @arg {Object} [options] Optional parameters
      * @arg {Object} options.latencyRef A latency reference object
      * @arg {Number} options.latencyRef.latency Interval between consuming tokens
      * @arg {Number} options.reservedTokens How many tokens to reserve for priority operations
      */
    constructor(tokenLimit, interval, options = {}) {
      this.tokenLimit = tokenLimit;
      this.interval = interval;
      this.latencyRef = options.latencyRef || { latency: 0 };
      this.lastReset = this.tokens = this.lastSend = 0;
      this.reservedTokens = options.reservedTokens || 0;
      this._queue = [];
    }
  
    check() {
      if (this.timeout || this._queue.length === 0) {
        return;
      }
      if (this.lastReset + this.interval + this.tokenLimit * this.latencyRef.latency < Date.now()) {
        this.lastReset = Date.now();
        this.tokens = Math.max(0, this.tokens - this.tokenLimit);
      }
  
      let val;
      let tokensAvailable = this.tokens < this.tokenLimit;
      let unreservedTokensAvailable = this.tokens < (this.tokenLimit - this.reservedTokens);
      while (this._queue.length > 0 && (unreservedTokensAvailable || (tokensAvailable && this._queue[0].priority))) {
        this.tokens++;
        tokensAvailable = this.tokens < this.tokenLimit;
        unreservedTokensAvailable = this.tokens < (this.tokenLimit - this.reservedTokens);
        const item = this._queue.shift();
        val = this.latencyRef.latency - Date.now() + this.lastSend;
        if (this.latencyRef.latency === 0 || val <= 0) {
          item.func();
          this.lastSend = Date.now();
        } else {
          setTimeout(() => {
            item.func();
          }, val);
          this.lastSend = Date.now() + val;
        }
      }
  
      if (this._queue.length > 0 && !this.timeout) {
        this.timeout = setTimeout(() => {
          this.timeout = null;
          this.check();
        }, this.tokens < this.tokenLimit ? this.latencyRef.latency : Math.max(0, this.lastReset + this.interval + this.tokenLimit * this.latencyRef.latency - Date.now()));
      }
    }
  
    /**
      * Queue something in the Bucket
      * @arg {Function} func A callback to call when a token can be consumed
      * @arg {Boolean} [priority=false] Whether or not the callback should use reserved tokens
      */
    queue(func, priority = false) {
      if (priority) {
        this._queue.unshift({ func, priority });
      } else {
        this._queue.push({ func, priority });
      }
      this.check();
    }
  
    [util.inspect.custom]() {
      return Base.prototype[util.inspect.custom].call(this);
    }
  }
  
  module.exports = Bucket;
  