import DrawingDocument from "@client/documents/drawing.mjs";
import SceneControls from "../../applications/ui/scene-controls.mjs";
import Drawing from "../placeables/drawing.mjs";
import PlaceablesLayer from "./base/placeables-layer.mjs";
import ShapeLayerMixin from "./mixins/shapes.mjs";
import DrawingPalette from "../../applications/sheets/palette/drawing-palette.mjs";

/**
 * @import Collection from "@common/utils/collection.mjs";
 * @import {Point} from "@common/_types.mjs";
 */

/**
 * The DrawingsLayer subclass of PlaceablesLayer.
 * This layer implements a container for drawings.
 * @category Canvas
 */
export default class DrawingsLayer extends ShapeLayerMixin(PlaceablesLayer) {

  /** @inheritdoc */
  static get layerOptions() {
    return foundry.utils.mergeObject(super.layerOptions, {
      name: "drawings",
      controllableObjects: true,
      rotatableObjects: true,
      zIndex: 500,
      allowedEmptyShapes: ["polygon"],
      discardClosingPoint: false
    });
  }

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

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

  /**
   * The collection of drawing objects which are rendered in the interface.
   * @type {Collection<string, Drawing>}
   */
  graphics = new foundry.utils.Collection();

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

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

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

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

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

  /** @inheritDoc */
  _getCopyableObjects(options) {
    if ( !game.user.can("DRAWING_CREATE") ) return [];
    return super._getCopyableObjects(options);
  }

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

  /** @inheritDoc */
  _deactivate() {
    super._deactivate();
    this.objects.visible = true;
  }

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

  /** @inheritdoc */
  async _draw(options) {
    await super._draw(options);
    this.objects.visible = true;
  }

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

  /**
   * Get initial data for a new drawing.
   * Start with some global defaults, apply user default config, then apply mandatory overrides per tool.
   * @param {Point} origin      The initial coordinate
   * @returns {object}          The new drawing data
   * @protected
   */
  _getNewDrawingData(origin) {
    const tool = game.activeTool;

    // Get saved palette defaults
    const data = foundry.utils.deepClone(this.paletteCreateData);

    // Mandatory additions
    delete data._id;
    data.x = origin.x;
    data.y = origin.y;
    data.sort = Math.max(this.getMaxSort() + 1, 0);
    data.author = game.user.id;
    data.shape = {};

    // Tool-based settings
    const strokeWidth = data.strokeWidth ?? 8;
    switch ( tool ) {
      case "rect":
        data.shape.type = Drawing.SHAPE_TYPES.RECTANGLE;
        data.shape.width = strokeWidth + 1;
        data.shape.height = strokeWidth + 1;
        data.bezierFactor = 0;
        break;
      case "ellipse":
        data.shape.type = Drawing.SHAPE_TYPES.ELLIPSE;
        data.shape.width = strokeWidth + 1;
        data.shape.height = strokeWidth + 1;
        data.bezierFactor = 0;
        break;
      case "polygon":
        data.shape.type = Drawing.SHAPE_TYPES.POLYGON;
        data.shape.points = [0, 0, 1, 0];
        data.bezierFactor = 0;
        break;
      case "freehand":
        data.shape.type = Drawing.SHAPE_TYPES.POLYGON;
        data.shape.points = [0, 0, 1, 0];
        break;
      case "text":
        data.shape.type = Drawing.SHAPE_TYPES.RECTANGLE;
        data.shape.width = strokeWidth + 1;
        data.shape.height = strokeWidth + 1;
        data.bezierFactor = 0;
        data.text ||= "";
        break;
    }

    // Return the cleaned data
    return DrawingDocument.cleanData(data);
  }

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

  /** @override */
  static prepareSceneControls() {
    const sc = SceneControls;
    return {
      name: "drawings",
      order: 3,
      title: "CONTROLS.GroupDrawing",
      layer: "drawings",
      icon: "fa-solid fa-pencil",
      visible: game.user.can("DRAWING_CREATE"),
      onChange: (event, active) => {
        if ( active ) canvas.drawings.activate();
      },
      onToolChange: (_event, tool, active) => {
        canvas.drawings.setAllRenderFlags({refreshState: true});
        if ( active && !tool.control ) canvas.drawings.releaseAll();
      },
      tools: {
        select: {
          name: "select",
          order: 1,
          title: "CONTROLS.DrawingSelect",
          icon: "fa-solid fa-expand",
          interaction: true,
          control: true,
          toolclip: {
            heading: "CONTROLS.DrawingSelect",
            items: sc.buildToolclipItems(["selectAlt", "selectMultiple", "move", "hud", "edit", "delete", "rotate"])
          }
        },
        rect: {
          name: "rect",
          order: 2,
          title: "CONTROLS.DrawingRect",
          icon: "fa-solid icon-fa-rectangle",
          creation: true,
          toolclip: {
            heading: "CONTROLS.DrawingRect",
            items: sc.buildToolclipItems(["draw", "drawProportionally"])
          }
        },
        ellipse: {
          name: "ellipse",
          order: 3,
          title: "CONTROLS.DrawingEllipse",
          icon: "fa-solid icon-fa-ellipse",
          creation: true,
          toolclip: {
            heading: "CONTROLS.DrawingEllipse",
            items: sc.buildToolclipItems(["draw", "drawProportionally"])
          }
        },
        polygon: {
          name: "polygon",
          order: 4,
          title: "CONTROLS.DrawingPoly",
          icon: "fa-solid fa-draw-polygon",
          creation: true,
          toolclip: {
            heading: "CONTROLS.DrawingPoly",
            items: sc.buildToolclipItems([{heading: "CONTROLS.CommonDraw", content: "CONTROLS.DrawingPolyP"}])
          }
        },
        freehand: {
          name: "freehand",
          order: 5,
          title: "CONTROLS.DrawingFree",
          icon: "fa-solid fa-signature",
          creation: true,
          toolclip: {
            src: "toolclips/tools/drawing-free.webm",
            heading: "CONTROLS.DrawingFree",
            items: sc.buildToolclipItems(["draw"])
          }
        },
        text: {
          name: "text",
          order: 6,
          title: "CONTROLS.DrawingText",
          icon: "fa-solid fa-font",
          interaction: true,
          control: true,
          creation: true,
          onChange: () => {
            const controlled = canvas.drawings.controlled;
            if ( controlled.length === 1 ) controlled[0].enableTextEditing();
          },
          toolclip: {
            heading: "CONTROLS.DrawingText",
            items: sc.buildToolclipItems(["draw", "drawProportionally", "move", "hud", "edit", "delete", "rotate"])
          }
        },
        snap: {
          name: "snap",
          order: 7,
          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: 8 },
        clear: {
          name: "clear",
          order: 8,
          title: "CONTROLS.DrawingClear",
          icon: "fa-solid fa-trash",
          onChange: () => canvas.drawings.deleteAll(),
          button: true
        }
      },
      activeTool: "select"
    };
  }

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

  /** @inheritDoc */
  _onDragLeftStart(event) {
    if ( game.activeTool === "freehand" ) {
      event.interactionData.freehand = true;
      event.interactionData.freehandSampleRate = this.constructor.placeableClass.FREEHAND_SAMPLE_RATE;
    }
    super._onDragLeftStart(event);
  }

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

  /** @override */
  _createDragPreviewData(event) {
    return this._getNewDrawingData(event.interactionData.origin);
  }

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

  /** @override */
  _createDragShapeData(event) {
    switch ( game.activeTool ) {
      case "rect":
      case "text": return {type: "rectangle", x: 0, y: 0, width: 0, height: 0, anchorX: 0.5, anchorY: 0.5};
      case "ellipse": return {type: "ellipse", x: 0, y: 0, radiusX: 0, radiusY: 0};
      case "polygon":
      case "freehand": return {type: "polygon", points: [0, 0, 0, 0]};
      default: throw new Error("Invalid tool");
    }
  }

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

  /** @override */
  _updateDragPreview(event) {
    const {preview, shape} = event.interactionData;
    switch ( shape.type ) {
      case "rectangle": {
        const {x, y, width, height, rotation} = shape;
        const {strokeWidth} = preview.document;
        preview.document.updateSource({
          x: x - (width / 2),
          y: y - (height / 2),
          shape: {
            width: Math.max(width, 2 * strokeWidth),
            height: Math.max(height, 2 * strokeWidth)
          },
          rotation
        });
        break;
      }
      case "ellipse": {
        const {x, y, radiusX, radiusY, rotation} = shape;
        const {strokeWidth} = preview.document;
        preview.document.updateSource({
          x: x - radiusX,
          y: y - radiusY,
          shape: {
            width: 2 * Math.max(radiusX, strokeWidth),
            height: 2 * Math.max(radiusY, strokeWidth)
          },
          rotation
        });
        break;
      }
      case "polygon": {
        let unrotatedShape;
        if ( preview.document.rotation === 0 ) unrotatedShape = shape;
        else {
          unrotatedShape = shape.clone();
          unrotatedShape.rotate(-preview.document.rotation);
        }
        let {x, y, width, height} = unrotatedShape.bounds;
        if ( (width === 0) && (height === 0) ) width = height = 1;
        const points = [];
        for ( let i = 0; i < unrotatedShape.points.length; i += 2 ) {
          points.push(unrotatedShape.points[i] - x, unrotatedShape.points[i + 1] - y);
        }
        preview.document.updateSource({x, y, shape: {width, height, points}});
        break;
      }
    }
    preview.renderFlags.set({refreshTransform: true});
  }

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

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