/**
 * @license
 * Copyright 2020 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * An item in the toolbox.
 *
 * @class
 */
// Former goog.module ID: Blockly.ToolboxItem

import type {ICollapsibleToolboxItem} from '../interfaces/i_collapsible_toolbox_item.js';
import type {IFocusableTree} from '../interfaces/i_focusable_tree.js';
import type {IToolbox} from '../interfaces/i_toolbox.js';
import type {IToolboxItem} from '../interfaces/i_toolbox_item.js';
import * as idGenerator from '../utils/idgenerator.js';
import type * as toolbox from '../utils/toolbox.js';
import type {WorkspaceSvg} from '../workspace_svg.js';

/**
 * Class for an item in the toolbox.
 */
export class ToolboxItem implements IToolboxItem {
  protected id_: string;
  protected parent_: ICollapsibleToolboxItem | null;
  protected level_: number;
  protected toolboxItemDef_: toolbox.ToolboxItemInfo | null;
  protected workspace_: WorkspaceSvg;
  /** The toolbox this category belongs to. */
  protected readonly parentToolbox_: IToolbox;

  /**
   * @param toolboxItemDef The JSON defining the toolbox item.
   * @param parentToolbox The toolbox that holds the toolbox item.
   * @param opt_parent The parent toolbox item or null if the category does not
   *     have a parent.
   */
  constructor(
    toolboxItemDef: toolbox.ToolboxItemInfo,
    parentToolbox: IToolbox,
    opt_parent?: ICollapsibleToolboxItem,
  ) {
    /** The ID for the category. */
    this.id_ =
      (toolboxItemDef as AnyDuringMigration)['toolboxitemid'] ||
      idGenerator.getNextUniqueId();

    /** The parent of the category. */
    this.parent_ = opt_parent || null;

    /** The level that the category is nested at. */
    this.level_ = this.parent_ ? this.parent_.getLevel() + 1 : 0;

    /** The JSON definition of the toolbox item. */
    this.toolboxItemDef_ = toolboxItemDef;

    this.parentToolbox_ = parentToolbox;

    /** The workspace of the parent toolbox. */
    this.workspace_ = this.parentToolbox_.getWorkspace();
  }

  /**
   * Initializes the toolbox item.
   * This includes creating the DOM and updating the state of any items based
   * on the info object.
   */
  init() {}
  // No-op by default.

  /**
   * Gets the div for the toolbox item.
   *
   * @returns The div for the toolbox item.
   */
  getDiv(): Element | null {
    return null;
  }

  /**
   * Gets the HTML element that is clickable.
   * The parent toolbox element receives clicks. The parent toolbox will add an
   * ID to this element so it can pass the onClick event to the correct
   * toolboxItem.
   *
   * @returns The HTML element that receives clicks, or null if this item should
   *     not receive clicks.
   */
  getClickTarget(): Element | null {
    return null;
  }

  /**
   * Gets a unique identifier for this toolbox item.
   *
   * @returns The ID for the toolbox item.
   */
  getId(): string {
    return this.id_;
  }

  /**
   * Gets the parent if the toolbox item is nested.
   *
   * @returns The parent toolbox item, or null if this toolbox item is not
   *     nested.
   */
  getParent(): ICollapsibleToolboxItem | null {
    return null;
  }

  /**
   * Gets the nested level of the category.
   *
   * @returns The nested level of the category.
   * @internal
   */
  getLevel(): number {
    return this.level_;
  }

  /**
   * Whether the toolbox item is selectable.
   *
   * @returns True if the toolbox item can be selected.
   */
  isSelectable(): boolean {
    return false;
  }

  /**
   * Whether the toolbox item is collapsible.
   *
   * @returns True if the toolbox item is collapsible.
   */
  isCollapsible(): boolean {
    return false;
  }

  /** Dispose of this toolbox item. No-op by default. */
  dispose() {}

  /**
   * Sets whether the category is visible or not.
   * For a category to be visible its parent category must also be expanded.
   *
   * @param _isVisible True if category should be visible.
   */
  setVisible_(_isVisible: boolean) {}

  /** See IFocusableNode.getFocusableElement. */
  getFocusableElement(): HTMLElement | SVGElement {
    const div = this.getDiv();
    if (!div) {
      throw Error('Trying to access toolbox item before DOM is initialized.');
    }
    if (!(div instanceof HTMLElement)) {
      throw Error('Toolbox item div is unexpectedly not an HTML element.');
    }
    return div as HTMLElement;
  }

  /** See IFocusableNode.getFocusableTree. */
  getFocusableTree(): IFocusableTree {
    return this.parentToolbox_;
  }

  /** See IFocusableNode.onNodeFocus. */
  onNodeFocus(): void {}

  /** See IFocusableNode.onNodeBlur. */
  onNodeBlur(): void {}

  /** See IFocusableNode.canBeFocused. */
  canBeFocused(): boolean {
    return true;
  }

  /**
   * Returns the toolbox this toolbox item belongs to.
   */
  getParentToolbox(): IToolbox {
    return this.parentToolbox_;
  }
}
// nop by default
