import PlaceablesLayer from "./base/placeables-layer.mjs";
import SceneControls from "../../applications/ui/scene-controls.mjs";
import {loadTexture} from "../loader.mjs";
import FilePicker from "@client/applications/apps/file-picker.mjs";
import ShapeLayerMixin from "./mixins/shapes.mjs";
import TilePalette from "../../applications/sheets/palette/tile-palette.mjs";

/**
 * @import Tile from "../placeables/tile.mjs";
 */

/**
 * A PlaceablesLayer designed for rendering the visual Scene for a specific vertical cross-section.
 * @category Canvas
 */
export default class TilesLayer extends ShapeLayerMixin(PlaceablesLayer) {

  /** @inheritdoc */
  static documentName = "Tile";

  /** @inheritdoc */
  static paletteClass = TilePalette;

  /* -------------------------------------------- */
  /*  Layer Attributes                            */
  /* -------------------------------------------- */

  /** @inheritdoc */
  static get layerOptions() {
    return foundry.utils.mergeObject(super.layerOptions, {
      name: "tiles",
      zIndex: 300,
      controllableObjects: true,
      rotatableObjects: true,
      confirmBeforeCreation: true
    });
  }

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

  /** @inheritdoc */
  get hookName() {
    return TilesLayer.name;
  }

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

  /** @inheritdoc */
  get hud() {
    return canvas.hud.tile;
  }

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

  /**
   * An array of Tile objects which are rendered within the objects container
   * @type {Tile[]}
   */
  get tiles() {
    return this.objects?.children || [];
  }

  /* -------------------------------------------- */
  /*  Layer Methods                               */
  /* -------------------------------------------- */

  /** @inheritDoc */
  async _tearDown(options) {
    for ( const tile of this.tiles ) {
      if ( tile.isVideo ) {
        game.video.stop(tile.sourceElement);
      }
    }
    return super._tearDown(options);
  }

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

  /** @override */
  static prepareSceneControls() {
    const sc = SceneControls;
    return {
      name: "tiles",
      order: 2,
      title: "CONTROLS.GroupTile",
      layer: "tiles",
      icon: "fa-solid fa-cubes",
      visible: game.user.isGM,
      onChange: (event, active) => {
        if ( active ) canvas.tiles.activate();
      },
      onToolChange: (_event, tool, active) => {
        canvas.tiles.setAllRenderFlags({refreshState: true});
        if ( active && !tool.control ) canvas.tiles.releaseAll();
      },
      tools: {
        select: {
          name: "select",
          order: 1,
          title: "CONTROLS.TileSelect",
          icon: "fa-solid fa-expand",
          interaction: true,
          control: true,
          toolclip: {
            heading: "CONTROLS.TileSelect",
            items: sc.buildToolclipItems(["selectAlt", "selectMultiple", "move", "rotate", "hud", "edit", "delete"])
          }
        },
        tile: {
          name: "tile",
          order: 2,
          title: "CONTROLS.TilePlace",
          icon: "fa-solid fa-cube",
          creation: true,
          toolclip: {
            heading: "CONTROLS.TilePlace",
            items: sc.buildToolclipItems(["create", "move", "rotate", "hud", "edit", "delete"])
          }
        },
        browse: {
          name: "browse",
          order: 3,
          title: "CONTROLS.TileBrowser",
          icon: "fa-solid fa-folder",
          button: true,
          onChange: () => new FilePicker.implementation({
            type: "texture",
            displayMode: "tiles",
            tileSize: true
          }).render({force: true}),
          toolclip: {
            heading: "CONTROLS.TileBrowser",
            items: sc.buildToolclipItems(["place", "move", "rotate", "hud", "edit", "delete"])
          }
        },
        snap: {
          name: "snap",
          order: 4,
          title: "CONTROLS.CommonForceSnap",
          icon: "fa-solid fa-plus",
          toggle: true,
          visible: !canvas.grid?.isGridless,
          active: canvas.forceSnapVertices,
          onChange: (event, toggled) => canvas.forceSnapVertices = toggled
        },
        togglePalette: { ...this.TOGGLE_PALETTE, order: 6 }
      },
      activeTool: "select"
    };
  }

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

  /** @override */
  _createDragPreviewData(event) {
    return foundry.utils.mergeObject({
      width: 1,
      height: 1,
      sort: Math.max(this.getMaxSort() + 1, 0)
    }, this.paletteCreateData);
  }

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

  /** @override */
  _createDragShapeData(event) {
    return {type: "rectangle", x: 0, y: 0, width: 0, height: 0, anchorX: 0.5, anchorY: 0.5};
  }

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

  /** @override */
  _updateDragPreview(event) {
    const interaction = event.interactionData;
    const {x, y, width, height, rotation} = interaction.shape;
    interaction.preview.document.updateSource({x, y, width, height, rotation});
    interaction.preview.renderFlags.set({refreshTransform: true});
  }

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

  /** @override */
  _updateMouseWheelPreview() {
    const {preview, shape} = this._mouseWheelContext;
    preview.document.updateSource({rotation: shape.rotation});
    preview.renderFlags.set({refreshRotation: true});
  }

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

  /**
   * Handle drop events for Tile data on the Tiles Layer
   * @param {DragEvent} event     The concluding drag event
   * @param {object} data         The extracted Tile data
   * @protected
   */
  async _onDropData(event, data) {
    if ( !data.texture?.src ) return;
    if ( !this.active ) this.activate();

    // Get the data for the tile to create
    const createData = await this._getDropData(event, data);

    // Validate that the drop position is in-bounds and snap to grid
    if ( !canvas.dimensions.rect.contains(createData.x, createData.y) ) return false;

    // Create the Tile Document
    const cls = foundry.utils.getDocumentClass(this.constructor.documentName);
    return cls.create(createData, {parent: canvas.scene});
  }

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

  /**
   * Prepare the data object when a new Tile is dropped onto the canvas
   * @param {DragEvent} event     The concluding drag event
   * @param {object} data         The extracted Tile data
   * @returns {object}            The prepared data to create
   * @protected
   */
  async _getDropData(event, data) {

    // Determine the tile size
    const tex = await loadTexture(data.texture.src);
    const ratio = canvas.dimensions.size / (data.tileSize || canvas.dimensions.size);
    data.width = tex.baseTexture.width * ratio;
    data.height = tex.baseTexture.height * ratio;

    // Determine the sort
    data.sort = Math.max(this.getMaxSort() + 1, 0);

    // Determine the final position and snap to grid unless SHIFT is pressed
    if ( !event.shiftKey ) {
      const {x, y} = this.getSnappedPoint(data);
      data.x = x;
      data.y = y;
    }

    // Create the tile as hidden if the ALT key is pressed
    if ( event.altKey ) data.hidden = true;

    // Merge palette defaults
    const paletteData = this.paletteCreateData;
    foundry.utils.mergeObject(data, paletteData);

    return data;
  }
}
