text: 'use strict';
  
  /** @typedef {{ execute: function (), label: string, lag: number= }} */
  var CommandExecutable;
  
  /**
   * Keeps track of the queue off commands to execute for a player
   */
  class CommandQueue {
    constructor() {
      this.commands = [];
      this.lag = 0;
      this.lastRun = 0;
    }
  
    /**
     * Safely add lag to the current queue. This method will not let you add a
     * negative amount as a safety measure. If you want to subtract lag you can
     * directly manipulate the `lag` property.
     * @param {number} amount milliseconds of lag
     */
    addLag(amount) {
      this.lag += Math.max(0, amount);
    }
  
    /**
     * @param {CommandExecutable} executable Thing to run with an execute and a queue label
     * @param {number} lag Amount of lag to apply to the queue after the command is run
     */
    enqueue(executable, lag) {
      let newIndex = this.commands.push(Object.assign(executable, { lag })) - 1;
      return newIndex;
    }
  
    get hasPending() {
      return this.commands.length > 0;
    }
  
    /**
     * Execute the currently pending command if it's ready
     * @return {boolean} whether the command was executed
     */
    execute() {
      if (!this.commands.length || this.msTilNextRun > 0) {
        return false;
      }
  
      const command = this.commands.shift();
  
      this.lastRun = Date.now();
      this.lag = command.lag;
      command.execute();
      return true;
    }
  
    /**
     * @type {Array<Object>}
     */
    get queue() {
      return this.commands;
    }
  
    /**
     * Flush all pending commands. Does _not_ reset lastRun/lag. Meaning that if
     * the queue is flushed after a command was just run its lag will still have
     * to expire before another command can be run. To fully reset the queue use
     * the reset() method.
     */
    flush() {
      this.commands = [];
    }
  
    /**
     * Completely reset the queue and any lag. This is fairly dangerous as if the
     * player could reliably reset the queue they could negate any command lag. To
     * clear commands without altering lag use flush()
     */
    reset() {
      this.flush();
      this.lastRun = 0;
      this.lag = 0;
    }
  
    /**
     * Seconds until the next command can be executed
     * @type {number}
     */
    get lagRemaining() {
      return this.msTilNextRun / 1000;
    }
  
    /**
     * Milliseconds til the next command can be executed
     * @type {number}
     */
    get msTilNextRun() {
      return Math.max(0, (this.lastRun + this.lag) - Date.now());
    }
  
    /**
     * For a given command index find how many seconds until it will run
     * @param {number} commandIndex
     * @param {boolean} milliseconds
     * @return {number}
     */
    getTimeTilRun(commandIndex) {
      return this.getMsTilRun(commandIndex) / 1000;
    }
  
    /**
     * Milliseconds until the command at the given index can be run
     * @param {number} commandIndex
     * @return {number}
     */
    getMsTilRun(commandIndex) {
      if (!this.commands[commandIndex]) {
        throw new RangeError("Invalid command index");
      }
  
      let lagTotal = this.msTilNextRun;
      for (let i = 0; i < this.commands.length; i++) {
        if (i === commandIndex) {
          return lagTotal;
        }
  
        lagTotal += this.commands[i].lag;
      }
    }
  }
  
  module.exports = CommandQueue;
  