import PlaceableTab from "./placeable-tab.mjs";

/**
 * The Token-specific placeables tab.
 * @extends {PlaceableTab}
 */
export default class TokenTab extends PlaceableTab {
  /** @override */
  static DEFAULT_OPTIONS = {
    actions: {
      selectGroup: TokenTab.#onSelectGroup
    }
  };

  /** @override */
  static DIRECTORY_PARTIAL = "templates/sidebar/tabs/placeable/tokens.hbs";

  /** @override */
  static ENTRY_PARTIAL = "templates/sidebar/tabs/placeable/token.hbs";

  /* -------------------------------------------- */
  /*  Rendering                                   */
  /* -------------------------------------------- */

  /** @inheritDoc */
  async _prepareDirectoryContext(context, options) {
    context = await super._prepareDirectoryContext(context, options);
    if ( (canvas.scene?.levels.size ?? 0) <= 1 ) return context;
    const noLevel = [];
    const groups = {};
    for ( const entry of context.entries ) {
      const level = canvas.scene.levels.get(entry.levelId);
      if ( level ) {
        const group = groups[level.id] ??= { id: level.id, label: level.name, entries: [] };
        group.entries.push(entry);
      }
      else noLevel.push(entry);
    }
    context.groups = canvas.scene.levels.reverseSorted.reduce((arr, l) => {
      const group = groups[l.id];
      if ( group?.entries.length ) arr.push(group);
      return arr;
    }, []);
    if ( noLevel.length ) context.groups.push({
      label: _loc("SIDEBAR.PLACEABLES.Categories.NoLevel"), entries: noLevel
    });
    return context;
  }

  /* -------------------------------------------- */

  /** @inheritDoc */
  async _prepareEntry(entry, context) {
    context = await super._prepareEntry(entry, context);
    context.levelId = entry.level;
    return context;
  }

  /* -------------------------------------------- */
  /*  Event Listeners & Handlers                  */
  /* -------------------------------------------- */

  /** @inheritDoc */
  _getEntryContextOptions() {
    const options = super._getEntryContextOptions();
    const deleteIndex = options.findIndex(o => o.label === "SIDEBAR.Delete");
    options.splice(deleteIndex, 0, {
      label: "SIDEBAR.PLACEABLES.ACTIONS.ChangeLevel",
      icon: "fa-solid fa-layer-group",
      visible: li => {
        if ( !game.user.isGM ) return false;
        const token = this._getPlaceableFromElement(li);
        return !token.locked && !!canvas.level && (token._source.level !== canvas.level.id);
      },
      onClick: (event, li) => this.#onChangeLevel(li)
    });
    return options;
  }

  /* -------------------------------------------- */

  /**
   * Handle moving the token to the viewed level.
   * @param {HTMLElement} li  The context menu target.
   * @returns {Promise<void>}
   */
  async #onChangeLevel(li) {
    if ( !canvas.level ) return;
    const token = this._getPlaceableFromElement(li);
    if ( !token ) return;
    const destination = {level: canvas.level.id};
    const options = {pan: {duration: 0}, animate: false};
    if ( canvas.ready && token.rendered && token.object.controlled ) {
      const instructions = this.layer.controlled.reduce((instructions, token) => {
        instructions[token.id] = {destination};
        return instructions;
      }, {});
      await canvas.scene.moveTokens(instructions, options);
    }
    else await token.move(destination, options);
  }

  /* -------------------------------------------- */

  /**
   * Handle selecting a group of tokens.
   * @this {TokenTab}
   * @param {PointerEvent} event  The triggering event.
   * @param {HTMLElement} target  The action target.
   */
  static #onSelectGroup(event, target) {
    if ( !ui.controls.tool?.control ) ui.controls.activate({ tool: "select" });
    const { levelId } = target.closest("[data-level-id]").dataset;
    const [release, control] = this.layer.placeables.partition(t => t.document._source.level === levelId);
    if ( !event.shiftKey ) release.forEach(t => t.release({ renderSidebar: false }));
    control.forEach(t => t.control({ renderSidebar: false, releaseOthers: false }));
    ui.placeables.render();
    if ( game.activeTool === "select" ) ui.placeablesPalette?.render();
  }
}
