import PlaceableConfig from "./placeable-config.mjs";
import {EDGE_SENSE_TYPES} from "../../../common/constants.mjs";

/**
 * The Application responsible for configuring a single Wall document within a parent Scene.
 */
export default class WallConfig extends PlaceableConfig {
  /** @inheritDoc */
  static DEFAULT_OPTIONS = {
    classes: ["wall-config"],
    position: {width: 480},
    window: {
      contentClasses: ["standard-form"],
      icon: "fa-solid fa-block-brick"
    },
    form: {closeOnSubmit: true},
    actions: {previewSound: WallConfig.#onPreviewSound}
  };

  /** @override */
  static PARTS = {
    body: {template: "templates/scene/wall-config.hbs"},
    footer: {template: "templates/generic/form-footer.hbs"}
  };

  /** Wall sense types affected by proximity threshold attenuation */
  static #PROXIMITY_SENSE_TYPES = [EDGE_SENSE_TYPES.PROXIMITY, EDGE_SENSE_TYPES.DISTANCE];

  /**
   * A tracking value corresponding with the index of an entry in CONFIG.Wall.doorSounds: each click of the preview
   * button plays the next sound in an entry.
   * @type {number}
   */
  #audioPreviewState = 0;

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

  /** @inheritDoc */
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    const {document, source, fields} = context;
    const c = source.c;
    const coordinates = _loc("WALL.CoordinateRange", {p0X: c[0], p0Y: c[1], p1X: c[2], p1Y: c[3]});
    const thresholdFields = ["light", "sight", "sound"].map(k => ({
      name: k,
      label: fields[k].label,
      choices: fields[k].choices,
      disabled: !WallConfig.#PROXIMITY_SENSE_TYPES.includes(source[k])
    }));
    const animationDirections = [
      {value: -1, label: _loc("WALL.ANIMATION_DIRECTIONS.REVERSE")},
      {value: 1, label: _loc("WALL.ANIMATION_DIRECTIONS.DEFAULT")}
    ];
    return Object.assign(context, {
      coordinates,
      thresholdFields,
      animation: source.animation || fields.animation.clean({}),
      animationDirections,
      animationTypes: CONFIG.Wall.animationTypes,
      animationFieldsetClass: (document.door > 0) && document.animation?.type ? "" : "hidden",
      gridUnits: document.parent.grid.units || _loc("MEASUREMENT.GridUnits"),
      doorSounds: CONFIG.Wall.doorSounds,
      buttons: [{type: "submit", icon: "fa-solid fa-floppy-disk", label: "WALL.Submit"}]
    });
  }

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

  /** @inheritDoc */
  _onChangeForm(formConfig, event) {
    switch ( event.target.name ) {
      case "door":
        this.#toggleDoorOptions(Number(event.target.value) > CONST.WALL_DOOR_TYPES.NONE);
        this.#toggleAnimationOptions();
        break;
      case "doorSound":
        // Reset the audio preview state
        this.#audioPreviewState = 0;
        break;
      case "light":
      case "sight":
      case "sound":
        this.#toggleThresholdInputVisibility();
        break;
      case "animation.type":
        this.#toggleAnimationOptions();
    }
    return super._onChangeForm(formConfig, event);
  }

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

  /** @inheritDoc */
  async _onRender(context, options) {
    await super._onRender(context, options);
    this.#toggleThresholdInputVisibility();
  }

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

  /**
   * Handle previewing a sound file for a Wall setting
   * @this {WallConfig}
   * @returns {Promise<void>}
   */
  static async #onPreviewSound() {
    const doorSoundName = this.form["doorSound"].value;
    const doorSound = CONFIG.Wall.doorSounds[doorSoundName];
    if ( !doorSound ) return;
    const interactions = CONST.WALL_DOOR_INTERACTIONS;
    const interaction = interactions[this.#audioPreviewState++ % interactions.length];
    let sounds = doorSound[interaction];
    if ( !sounds ) return;
    if ( !Array.isArray(sounds) ) sounds = [sounds];
    const src = sounds[Math.floor(Math.random() * sounds.length)];
    await game.audio.play(src, {context: game.audio.interface});
  }

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

  /**
   * Toggle the disabled attributes of the door options and show/hide their containing fieldset.
   * @param {boolean} isDoor
   * @returns {void}
   */
  #toggleDoorOptions(isDoor) {
    for ( const name of ["ds", "doorSound", "animation.type"] ) {
      const select = this.form[name];
      select.disabled = !isDoor;
      select.closest(".form-group").hidden = !isDoor;
    }
    this.setPosition(); // Form height changed
  }

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

  /**
   * Toggle the display of the advanced Door Animation section.
   */
  #toggleAnimationOptions() {
    const showOptions = (Number(this.form.door.value) > 0) && !!this.form["animation.type"].value;
    const fieldset = this.element.querySelector(".door-animation");
    fieldset.classList.toggle("hidden", !showOptions);
    this.setPosition(); // Form height changed
  }

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

  /**
   * Toggle the disabled and hidden attributes of proximity input fields.
   * @returns {void}
   */
  #toggleThresholdInputVisibility() {
    let anyProximityType = false;
    for ( const sense of ["light", "sight", "sound"] ) {
      const type = Number(this.form[sense].value);
      const input = this.form[`threshold.${sense}`];
      const isProximityType = WallConfig.#PROXIMITY_SENSE_TYPES.includes(type);
      anyProximityType ||= isProximityType;
      input.disabled = input.hidden = !isProximityType;
    }
    this.form.elements["threshold.attenuation"].closest(".form-group").hidden = !anyProximityType;
  }

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

  /** @inheritDoc */
  _prepareSubmitData(event, form, formData, updateData) {
    const submitData = super._prepareSubmitData(event, form, formData, updateData);
    for ( const sense of ["light", "sight", "sound"] ) {
      if ( (sense in submitData) && !WallConfig.#PROXIMITY_SENSE_TYPES.includes(submitData[sense]) ) {
        (submitData.threshold ??= {})[sense] = null;
      }
    }
    if ( submitData.door === CONST.WALL_DOOR_TYPES.NONE ) submitData.animation = null; // Purge animation data
    return submitData;
  }

}
