import PlaceableConfig from "../placeable-config.mjs";
import TokenApplicationMixin from "./mixin.mjs";
import BaseToken from "@common/documents/token.mjs";
import TokenDocument from "@client/documents/token.mjs";

/**
 * The Application responsible for configuring a single token document within a parent Scene
 * @extends PlaceableConfig
 * @mixes TokenApplication
 */
export default class TokenConfig extends TokenApplicationMixin(PlaceableConfig) {

  /** @override */
  isPrototype = false;

  /** @override */
  get token() {
    return this._preview ?? this.document;
  }

  /** @override */
  get actor() {
    return this.document.actor;
  }

  /** @override */
  get _fields() {
    return BaseToken.schema.fields;
  }

  /** @inheritDoc */
  get isVisible() {
    return super.isVisible && game.user.can("TOKEN_CONFIGURE");
  }

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

  /** @inheritDoc */
  async _prepareAppearanceTab(options) {
    const context = await super._prepareAppearanceTab(options);
    const gridType = this.token.parent?.grid.type;
    const S = CONST.TOKEN_SHAPES;
    if ( gridType === CONST.GRID_TYPES.SQUARE ) {
      if ( this.token.shape === CONST.TOKEN_SHAPES.RECTANGLE_1 ) {
        context.shapes = {
          [CONST.TOKEN_SHAPES.RECTANGLE_1]: _loc("TOKEN.SHAPES.RECTANGLE.label")
        };
      }
    }
    else if ( gridType === CONST.GRID_TYPES.GRIDLESS ) {
      if ( [S.ELLIPSE_1, S.RECTANGLE_1].includes(this.token.shape) ) {
        context.shapes = {
          [S.ELLIPSE_1]: _loc("TOKEN.SHAPES.ELLIPSE.label"),
          [S.RECTANGLE_1]: _loc("TOKEN.SHAPES.RECTANGLE.label")
        };
      }
    }
    return context;
  }

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

  /** @inheritDoc */
  _previewChanges(changes) {
    const priorTextureSrc = this.document.texture.src;
    const priorRingEnabled = this.document.ring.enabled;
    const priorRingColors = this.document.ring.colors;
    const priorRingEffects = this.document.ring.effects;
    changes = {...changes};
    delete changes.actorId;
    delete changes.actorLink;
    super._previewChanges(changes);
    if ( !this._preview.rendered ) return;
    const redraw = (this._preview.texture.src !== priorTextureSrc)
      || (this._preview.ring.enabled !== priorRingEnabled) || (priorRingEnabled
        && ((this._preview.ring.colors !== priorRingColors)
        || !foundry.utils.equals(this._preview.ring.effects, priorRingEffects)));
    this._preview.object.renderFlags.set({redraw: redraw});
    this._preview.object.initializeSources();
  }

  /* -------------------------------------------- */
  /*  Event Listeners and Handlers                */
  /* -------------------------------------------- */

  /** @inheritDoc */
  _onRender(context, options) {
    this.#toggleTurnMarkerDisabledFields();
    return super._onRender(context, options);
  }

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

  /** @inheritDoc */
  _onChangeForm(formConfig, event) {
    if ( ["bar1", "bar2"].includes(event.target.name) ) this._onChangeBar(event);

    // Turn Marker Disabled/Enabled
    if ( event.target.name === "turnMarker.mode" ) this.#toggleTurnMarkerDisabledFields();

    super._onChangeForm(formConfig, event);

    // Special handling for darkness state change
    if ( event.target.name === "light.negative" ) this.render({parts: ["light"]});
  }

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

  /**
   * Toggle the disabled state for certain turn marker fields depending on the mode.
   */
  #toggleTurnMarkerDisabledFields() {
    const notCustom = this.form["turnMarker.mode"].value !== "2";
    for ( const field of Object.values(this.document.schema.fields.turnMarker.fields) ) {
      if ( field.name === "mode" ) continue;
      const input = this.form[field.fieldPath];
      if ( input ) input.disabled = notCustom;
    }
  }

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

  /**
   * Handle changing the attribute bar in the drop-down selector to update the default current and max value
   * @param {Event} event  The select input change event
   * @protected
   */
  _onChangeBar(event) {
    const form = this.form;
    const attr = this.token.getBarAttribute("", {alternative: event.target.value});
    const barName = event.target.name;
    form.querySelector(`input[data-${barName}-value]`).value = attr !== null ? attr.value : "";
    form.querySelector(`input[data-${barName}-max]`).value = ((attr !== null) && (attr.type === "bar")) ? attr.max : "";
  }

  /* -------------------------------------------- */
  /*  Form Submission                             */
  /* -------------------------------------------- */

  /** @inheritDoc */
  async _processSubmitData(event, form, submitData, options) {
    let waypoint = {};
    for ( const k of TokenDocument.MOVEMENT_FIELDS ) {
      if ( k in submitData ) {
        if ( submitData[k] !== this.document._source[k] ) waypoint[k] = submitData[k];
        delete submitData[k];
      }
    }
    const result = await super._processSubmitData(event, form, submitData, options);
    if ( foundry.utils.isEmpty(waypoint) ) return result;
    if ( this.document.rendered ) waypoint = this.document.object._getConfigMovementPosition(waypoint);
    waypoint.action = "displace";
    waypoint.snapped = false;
    waypoint.explicit = true;
    waypoint.checkpoint = true;
    await this.document.move(waypoint, {method: "config", autoRotate: false, showRuler: false,
      constrainOptions: {ignoreWalls: true, ignoreCost: true}});
    return result;
  }
}
