import HandlebarsApplicationMixin from "../../api/handlebars-application.mjs";
import ApplicationV2 from "../../api/application.mjs";

/**
 * @import {ApplicationConfiguration} from "../../_types.mjs";
 * @import {HandlebarsRenderOptions} from "../../api/handlebars-applications.mjs";
 * @import PlaceableTab from "../tabs/placeable-tab.mjs";
 */

/**
 * A dialog application for configuring advanced placeable filters.
 * @extends {ApplicationV2<ApplicationConfiguration, HandlebarsRenderOptions>}
 * @mixes HandlebarsApplication
 */
export default class PlaceableFilter extends HandlebarsApplicationMixin(ApplicationV2) {
  /**
   * @param {PlaceableTab} tab                             The tab this dialog belongs to.
   * @param {Partial<ApplicationConfiguration>} [options]  Application options.
   */
  constructor(tab, options={}) {
    super(options);
    this.#tab = tab;
    this.#applyFilters = foundry.utils.debounce(() => this.#tab._applyFilters(), 200);
  }

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

  /** @override */
  static DEFAULT_OPTIONS = {
    tag: "form",
    classes: ["placeable-filter", "standard-form"],
    window: {
      title: "SIDEBAR.PLACEABLES.Filter.Title",
      icon: "fa-solid fa-filter",
      minimizable: false,
      resizable: false
    },
    position: {
      scale: .8,
      width: 375
    },
    actions: {
      clear: PlaceableFilter.#onClear
    }
  };

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

  /** @override */
  static PARTS = {
    body: {
      root: true,
      classes: ["standard-form"],
      template: "templates/sidebar/filters/body.hbs"
    }
  };

  /* -------------------------------------------- */
  /*  Properties                                  */
  /* -------------------------------------------- */

  /**
   * A debounced function to apply filters on the parent tab.
   * @type {Function}
   */
  #applyFilters;

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

  /**
   * The tab this dialog belongs to.
   * @type {PlaceableTab}
   */
  get tab() {
    return this.#tab;
  }

  #tab;

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

  /** @override */
  _canDetach() {
    return false;
  }

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

  /** @inheritDoc */
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    const { NumberField } = foundry.data.fields;
    const { elevation } = this.#tab._filterState;
    context.elevation = {
      bottom: {
        field: new NumberField({
          label: "SIDEBAR.PLACEABLES.Filter.Bottom", integer: true, placeholder: "\uf068\u221E"
        }, { name: "elevation.bottom" }),
        value: Number.isFinite(elevation.bottom) ? elevation.bottom : null
      },
      top: {
        field: new NumberField({
          label: "SIDEBAR.PLACEABLES.Filter.Top", integer: true, placeholder: "\u002b\u221E"
        }, { name: "elevation.top" }),
        value: Number.isFinite(elevation.top) ? elevation.top : null
      }
    };
    return context;
  }

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

  /** @inheritDoc */
  _attachFrameListeners() {
    super._attachFrameListeners();
    this.element.addEventListener("input", this.#onFilterInput.bind(this), { passive: true });
  }

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

  /**
   * Handle clearing all filters.
   * @this {PlaceableFilter}
   */
  static #onClear() {
    this.#tab._clearFilters();
    this.#tab._applyFilters();
    return this.render();
  }

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

  /**
   * Handle filtering.
   * @param {Event} event  The triggering event.
   */
  #onFilterInput(event) {
    const { target } = event;
    switch ( target.name ) {
      case "elevation.bottom":
        this.#tab._filterState.elevation.bottom = Number.isNaN(target.valueAsNumber) ? -Infinity : target.valueAsNumber;
        break;
      case "elevation.top":
        this.#tab._filterState.elevation.top = Number.isNaN(target.valueAsNumber) ? Infinity : target.valueAsNumber;
        break;
      default: return;
    }
    this.#applyFilters();
  }
}
