/**
 * This is a TypeScript code that defines an abstract class `BaseInteractor` with a generic type `T` that extends either `SVGElement` or `HTMLCanvasElement`.
 */
export class BaseInteractor {
  /**
   * This is a constructor function that takes a parameter of type T and sets it as a protected property called "context".
   *
   * @param {SVGElement | HTMLCanvasElement} context - The "context" parameter is a protected property of a class constructor that is
   * of type "T". It is used to store a reference to an object or data that is required by the class
   * methods to perform their tasks. The "protected" keyword means that the property can only be
   * accessed within the class
   */
  constructor(context) {
    this.context = context;
    this.interactingEntryForEventRegistry = new Map();
    this.interactingEntryForDrawerRegistry = new WeakMap();
  }
  /**
   * This function registers an interacting entry with a drawer, draw piece, and event type in an interactor registry.
   *
   * @param {BaseDrawer<unknown>} drawer - The drawer parameter is an instance of a class that extends the BaseDrawer class,
   * which is used for drawing on a canvas or other graphical surface.
   * @param {DrawPiece} drawPiece - DrawPiece is a type that represents a specific piece that can be
   * drawn on a canvas or other graphical surface. It could include information such as the shape,
   * color, size, and position of the piece.
   * @param {InteractingEntry} interactingEntry - InteractingEntry is a data structure that represents
   * an interaction between a user and a graphical element on the screen. It contains information such
   * as the type of event that triggered the interaction (e.g. mouse click, touch)
   */
  register(drawer, drawPiece, interactingEntry) {
    /* eslint-disable @typescript-eslint/no-non-null-assertion */
    const eventType = interactingEntry.eventType;
    if (!this.interactingEntryForEventRegistry.get(eventType)) {
      this.interactingEntryForEventRegistry.set(eventType, []);
    }
    this.interactingEntryForEventRegistry.get(eventType).push(interactingEntry);
    if (!this.interactingEntryForDrawerRegistry.get(drawer)) {
      this.interactingEntryForDrawerRegistry.set(drawer, new WeakMap());
    }
    if (!this.interactingEntryForDrawerRegistry.get(drawer).get(drawPiece)) {
      this.interactingEntryForDrawerRegistry.get(drawer).set(drawPiece, []);
    }
    this.interactingEntryForDrawerRegistry.get(drawer).get(drawPiece).push(interactingEntry);
    /* eslint-enable @typescript-eslint/no-non-null-assertion */
  }
  /**
   * This function unregisters a drawer from the interactor registry.
   *
   * @param {BaseDrawer<unknown>} drawer - The parameter `drawer` is of type `BaseDrawer<unknown>`. It is an object that
   * represents a drawer and is used to unregister the drawer from the `interactorRegistry`. The
   * `interactorRegistry` is a `WeakMap` that stores a mapping between an `Interactor` and
   */
  unregisterByDrawer(drawer) {
    /* eslint-disable @typescript-eslint/no-non-null-assertion */
    const drawPiece = drawer.drawPiece;
    if (drawPiece &&
      this.interactingEntryForDrawerRegistry.has(drawer) &&
      this.interactingEntryForDrawerRegistry.get(drawer) &&
      this.interactingEntryForDrawerRegistry.get(drawer).has(drawPiece) &&
      this.interactingEntryForDrawerRegistry.get(drawer).get(drawPiece)) {
      this.interactingEntryForEventRegistry.forEach((value, key) => {
        this.interactingEntryForEventRegistry.set(key, value.filter((interactingEntry) => {
          return (this.interactingEntryForDrawerRegistry
            .get(drawer)
            .get(drawPiece)
            .indexOf(interactingEntry) < 0);
        }));
      });
      /* eslint-enable @typescript-eslint/no-non-null-assertion */
    }
    this.interactingEntryForDrawerRegistry.delete(drawer);
  }
  /**
   * This function handles mouse events for registered interactor and drawers.
   *
   * @param {EventType} eventType - The type of event being registered in the registry, such as "click" or "mousemove".
   * @param {MouseEvent} event - The `event` parameter is of type `MouseEvent`, which represents a
   * mouse-related event (such as a click, double-click, or mouse movement) that occurred on the web
   * page. It contains information about the event, such as the type of event, the target element that
   * triggered the event
   */
  callRegistry(eventType, event) {
    const interactingEntryArray = this.interactingEntryForEventRegistry.get(eventType);
    if (interactingEntryArray) {
      interactingEntryArray.forEach((interactingEntry) => {
        if (interactingEntry.shouldHandle(event)) {
          interactingEntry.handle(event);
        }
      });
    }
  }
}
