/**
 * The Application responsible for configuring a single Wall document within a parent Scene.
 * @param {Wall} object                       The Wall object for which settings are being configured
 * @param {FormApplicationOptions} [options]  Additional options which configure the rendering of the configuration
 *                                            sheet.
 */
class WallConfig extends DocumentSheet {

  /** @inheritdoc */
  static get defaultOptions() {
    const options = super.defaultOptions;
    options.classes.push("wall-config");
    options.template = "templates/scene/wall-config.html";
    options.width = 400;
    options.height = "auto";
    return options;
  }

  /**
   * An array of Wall ids that should all be edited when changes to this config form are submitted
   * @type {string[]}
   */
  editTargets = [];

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

  /** @inheritdoc */
  get title() {
    if ( this.editTargets.length > 1 ) return game.i18n.localize("WALLS.TitleMany");
    return super.title;
  }

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

  /** @inheritdoc */
  render(force, options) {
    if ( options?.walls instanceof Array ) {
      this.editTargets = options.walls.map(w => w.id);
    }
    return super.render(force, options);
  }

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

  /** @inheritdoc */
  getData(options={}) {
    const context = super.getData(options);
    context.source = this.document.toObject();
    context.p0 = {x: this.object.c[0], y: this.object.c[1]};
    context.p1 = {x: this.object.c[2], y: this.object.c[3]};
    context.gridUnits = this.document.parent.grid.units || game.i18n.localize("GridUnits");
    context.moveTypes = Object.keys(CONST.WALL_MOVEMENT_TYPES).reduce((obj, key) => {
      let k = CONST.WALL_MOVEMENT_TYPES[key];
      obj[k] = game.i18n.localize(`WALLS.SenseTypes.${key}`);
      return obj;
    }, {});
    context.senseTypes = Object.keys(CONST.WALL_SENSE_TYPES).reduce((obj, key) => {
      let k = CONST.WALL_SENSE_TYPES[key];
      obj[k] = game.i18n.localize(`WALLS.SenseTypes.${key}`);
      return obj;
    }, {});
    context.dirTypes = Object.keys(CONST.WALL_DIRECTIONS).reduce((obj, key) => {
      let k = CONST.WALL_DIRECTIONS[key];
      obj[k] = game.i18n.localize(`WALLS.Directions.${key}`);
      return obj;
    }, {});
    context.doorTypes = Object.keys(CONST.WALL_DOOR_TYPES).reduce((obj, key) => {
      let k = CONST.WALL_DOOR_TYPES[key];
      obj[k] = game.i18n.localize(`WALLS.DoorTypes.${key}`);
      return obj;
    }, {});
    context.doorStates = Object.keys(CONST.WALL_DOOR_STATES).reduce((obj, key) => {
      let k = CONST.WALL_DOOR_STATES[key];
      obj[k] = game.i18n.localize(`WALLS.DoorStates.${key}`);
      return obj;
    }, {});
    context.doorSounds = CONFIG.Wall.doorSounds;
    context.isDoor = this.object.isDoor;
    return context;
  }

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

  /** @inheritDoc */
  activateListeners(html) {
    html.find(".audio-preview").click(this.#onAudioPreview.bind(this));
    this.#enableDoorOptions(this.document.door > CONST.WALL_DOOR_TYPES.NONE);
    this.#toggleThresholdInputVisibility();
    return super.activateListeners(html);
  }

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

  #audioPreviewState = 0;

  /**
   * Handle previewing a sound file for a Wall setting
   * @param {Event} event   The initial button click event
   */
  #onAudioPreview(event) {
    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)];
    game.audio.play(src, {context: game.audio.interface});
  }

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

  /** @inheritDoc */
  async _onChangeInput(event) {
    if ( event.currentTarget.name === "door" ) {
      this.#enableDoorOptions(Number(event.currentTarget.value) > CONST.WALL_DOOR_TYPES.NONE);
    }
    else if ( event.currentTarget.name === "doorSound" ) {
      this.#audioPreviewState = 0;
    }
    else if ( ["light", "sight", "sound"].includes(event.currentTarget.name) ) {
      this.#toggleThresholdInputVisibility();
    }
    return super._onChangeInput(event);
  }

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

  /**
   * Toggle the disabled attribute of the door state select.
   * @param {boolean} isDoor
   */
  #enableDoorOptions(isDoor) {
    const doorOptions = this.form.querySelector(".door-options");
    doorOptions.disabled = !isDoor;
    doorOptions.classList.toggle("hidden", !isDoor);
    this.setPosition({height: "auto"});
  }

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

  /**
   * Toggle visibility of proximity input fields.
   */
  #toggleThresholdInputVisibility() {
    const form = this.form;
    const showTypes = [CONST.WALL_SENSE_TYPES.PROXIMITY, CONST.WALL_SENSE_TYPES.DISTANCE];
    for ( const sense of ["light", "sight", "sound"] ) {
      const select = form[sense];
      const input = select.parentElement.querySelector(".proximity");
      input.classList.toggle("hidden", !showTypes.includes(Number(select.value)));
    }
  }

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

  /** @inheritdoc */
  _getSubmitData(updateData={}) {
    const thresholdTypes = [CONST.WALL_SENSE_TYPES.PROXIMITY, CONST.WALL_SENSE_TYPES.DISTANCE];
    const formData = super._getSubmitData(updateData);
    for ( const sense of ["light", "sight", "sound"] ) {
      if ( !thresholdTypes.includes(formData[sense]) ) formData[`threshold.${sense}`] = null;
    }
    return formData;
  }

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

  /** @inheritdoc */
  async _updateObject(event, formData) {

    // Update multiple walls
    if ( this.editTargets.length > 1 ) {
      const updateData = canvas.scene.walls.reduce((arr, w) => {
        if ( this.editTargets.includes(w.id) ) {
          arr.push(foundry.utils.mergeObject(w.toJSON(), formData));
        }
        return arr;
      }, []);
      return canvas.scene.updateEmbeddedDocuments("Wall", updateData, {sound: false});
    }

    // Update single wall
    if ( !this.object.id ) return;
    return this.object.update(formData, {sound: false});
  }
}
