'use strict';

/**
 * Util class to track usages of something
 */
export default class UsageCounter {
 
  private _usages: Record<string, Set<any>> = {};

  /**
   * Acquires a usage. If already acquired by specified usage, the count won't be incremented
   * @param key key to count
   * @param usage usage
   */
  acquire(key: string, usage: any) {
    this._usages[key] ||= new Set();
    this._usages[key].add(usage);
  }

  /**
   * Releases a usage. If already not acquired by specified usage, the counte won't be decremented
   * @param key key to count
   * @param usage usage
   */
  release(key: string, usage: any) {
    this._usages[key]?.delete(usage);
    if (!this._usages[key]?.size) {
      delete this._usages[key];
    }
  }

  /**
   * Releases all usages of a key
   * @param key key to release
   */
  releaseAll(key: string) {
    for (let usage of this.getUsages(key)) {
      this.release(key, usage);
    }
  }

  /**
   * Returns whether a key is currently in use
   * @param key key to check
   * @returns is in use
   */
  isInUse(key: string): boolean {
    return !!this._usages[key]?.size;
  }

  /**
   * Returns key usage IDs
   * @param key key to count
   * @returns usages
   */
  getUsages(key: any): Set<any> {
    return this._usages[key] || new Set();
  }

  /**
   * Returns if key is acquired by usage
   * @param key key to check
   * @param usage usage
   * @returns is acquired
   */
  isAcquiredBy(key: string, usage: any): boolean {
    return !!this._usages[key]?.has(usage);
  }

  /**
   * Returns keys which are acquired by specific usage
   * @param usage usage
   * @returns keys acquired by the specified usage
   */
  getAcquiredBy(usage: string): string[] {
    let result: string[] = [];
    for (let [key, usages] of Object.entries(this._usages)) {
      if (usages.has(usage)) {
        result.push(key);
      }
    }
    return result;
  }
}
