text: 'use strict';
  
  const EventEmitter = require('events');
  const RESTManager = require('../rest/RESTManager');
  const Options = require('../util/Options');
  const Util = require('../util/Util');
  
  /**
   * The base class for all clients.
   * @extends {EventEmitter}
   */
  class BaseClient extends EventEmitter {
    constructor(options = {}) {
      super();
  
      /**
       * Timeouts set by {@link BaseClient#setTimeout} that are still active
       * @type {Set<Timeout>}
       * @private
       */
      this._timeouts = new Set();
  
      /**
       * Intervals set by {@link BaseClient#setInterval} that are still active
       * @type {Set<Timeout>}
       * @private
       */
      this._intervals = new Set();
  
      /**
       * Intervals set by {@link BaseClient#setImmediate} that are still active
       * @type {Set<Immediate>}
       * @private
       */
      this._immediates = new Set();
  
      /**
       * The options the client was instantiated with
       * @type {ClientOptions}
       */
      this.options = Util.mergeDefault(Options.createDefault(), options);
  
      /**
       * The REST manager of the client
       * @type {RESTManager}
       * @private
       */
      this.rest = new RESTManager(this, options._tokenType);
    }
  
    /**
     * API shortcut
     * @type {Object}
     * @readonly
     * @private
     */
    get api() {
      return this.rest.api;
    }
  
    /**
     * Destroys all assets used by the base client.
     */
    destroy() {
      for (const t of this._timeouts) this.clearTimeout(t);
      for (const i of this._intervals) this.clearInterval(i);
      for (const i of this._immediates) this.clearImmediate(i);
      this._timeouts.clear();
      this._intervals.clear();
      this._immediates.clear();
    }
  
    /**
     * Sets a timeout that will be automatically cancelled if the client is destroyed.
     * @param {Function} fn Function to execute
     * @param {number} delay Time to wait before executing (in milliseconds)
     * @param {...*} args Arguments for the function
     * @returns {Timeout}
     */
    setTimeout(fn, delay, ...args) {
      const timeout = setTimeout(() => {
        fn(...args);
        this._timeouts.delete(timeout);
      }, delay);
      this._timeouts.add(timeout);
      return timeout;
    }
  
    /**
     * Clears a timeout.
     * @param {Timeout} timeout Timeout to cancel
     */
    clearTimeout(timeout) {
      clearTimeout(timeout);
      this._timeouts.delete(timeout);
    }
  
    /**
     * Sets an interval that will be automatically cancelled if the client is destroyed.
     * @param {Function} fn Function to execute
     * @param {number} delay Time to wait between executions (in milliseconds)
     * @param {...*} args Arguments for the function
     * @returns {Timeout}
     */
    setInterval(fn, delay, ...args) {
      const interval = setInterval(fn, delay, ...args);
      this._intervals.add(interval);
      return interval;
    }
  
    /**
     * Clears an interval.
     * @param {Timeout} interval Interval to cancel
     */
    clearInterval(interval) {
      clearInterval(interval);
      this._intervals.delete(interval);
    }
  
    /**
     * Sets an immediate that will be automatically cancelled if the client is destroyed.
     * @param {Function} fn Function to execute
     * @param {...*} args Arguments for the function
     * @returns {Immediate}
     */
    setImmediate(fn, ...args) {
      const immediate = setImmediate(fn, ...args);
      this._immediates.add(immediate);
      return immediate;
    }
  
    /**
     * Clears an immediate.
     * @param {Immediate} immediate Immediate to cancel
     */
    clearImmediate(immediate) {
      clearImmediate(immediate);
      this._immediates.delete(immediate);
    }
  
    /**
     * Increments max listeners by one, if they are not zero.
     * @private
     */
    incrementMaxListeners() {
      const maxListeners = this.getMaxListeners();
      if (maxListeners !== 0) {
        this.setMaxListeners(maxListeners + 1);
      }
    }
  
    /**
     * Decrements max listeners by one, if they are not zero.
     * @private
     */
    decrementMaxListeners() {
      const maxListeners = this.getMaxListeners();
      if (maxListeners !== 0) {
        this.setMaxListeners(maxListeners - 1);
      }
    }
  
    toJSON(...props) {
      return Util.flatten(this, { domain: false }, ...props);
    }
  }
  
  module.exports = BaseClient;
  