import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import {
  BaseBoxShapeUtil,
  HTMLContainer,
  Vec2d,
  deepCopy,
  imageShapeMigrations,
  imageShapeProps,
  toDomPrecision,
  useIsCropping,
  useValue
} from "@cmpd/editor";
import { useEffect, useState } from "react";
import { HyperlinkButton } from "../shared/HyperlinkButton.mjs";
import { usePrefersReducedMotion } from "../shared/usePrefersReducedMotion.mjs";
const loadImage = async (url) => {
  return new Promise((resolve, reject) => {
    const image = new Image();
    image.onload = () => resolve(image);
    image.onerror = () => reject(new Error("Failed to load image"));
    image.crossOrigin = "anonymous";
    image.src = url;
  });
};
const getStateFrame = async (url) => {
  const image = await loadImage(url);
  const canvas = document.createElement("canvas");
  canvas.width = image.width;
  canvas.height = image.height;
  const ctx = canvas.getContext("2d");
  if (!ctx)
    return;
  ctx.drawImage(image, 0, 0);
  return canvas.toDataURL();
};
async function getDataURIFromURL(url) {
  const response = await fetch(url);
  const blob = await response.blob();
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}
class ImageShapeUtil extends BaseBoxShapeUtil {
  static type = "image";
  static props = imageShapeProps;
  static migrations = imageShapeMigrations;
  isAspectRatioLocked = () => true;
  canCrop = () => true;
  getDefaultProps() {
    return {
      w: 100,
      h: 100,
      assetId: null,
      playing: true,
      url: "",
      crop: null
    };
  }
  component(shape) {
    const containerStyle = getContainerStyle(shape);
    const isCropping = useIsCropping(shape.id);
    const prefersReducedMotion = usePrefersReducedMotion();
    const [staticFrameSrc, setStaticFrameSrc] = useState("");
    const asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : void 0;
    if (asset?.type === "bookmark") {
      throw Error("Bookmark assets can't be rendered as images");
    }
    const isSelected = useValue(
      "onlySelectedShape",
      () => shape.id === this.editor.getOnlySelectedShape()?.id,
      [this.editor]
    );
    const showCropPreview = isSelected && isCropping && this.editor.isInAny("select.crop", "select.cropping", "select.pointing_crop_handle");
    const reduceMotion = prefersReducedMotion && (asset?.props.mimeType?.includes("video") || asset?.props.mimeType?.includes("gif"));
    useEffect(() => {
      if (asset?.props.src && "mimeType" in asset.props && asset?.props.mimeType === "image/gif") {
        let cancelled = false;
        const run = async () => {
          const newStaticFrame = await getStateFrame(asset.props.src);
          if (cancelled)
            return;
          if (newStaticFrame) {
            setStaticFrameSrc(newStaticFrame);
          }
        };
        run();
        return () => {
          cancelled = true;
        };
      }
    }, [prefersReducedMotion, asset?.props]);
    return /* @__PURE__ */ jsxs(Fragment, { children: [
      asset?.props.src && showCropPreview && /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(
        "div",
        {
          className: "tl-image",
          style: {
            opacity: 0.1,
            backgroundImage: `url(${!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src})`
          },
          draggable: false
        }
      ) }),
      /* @__PURE__ */ jsxs(
        HTMLContainer,
        {
          id: shape.id,
          style: { overflow: "hidden", width: shape.props.w, height: shape.props.h },
          children: [
            /* @__PURE__ */ jsxs("div", { className: "tl-image-container", style: containerStyle, children: [
              asset?.props.src ? /* @__PURE__ */ jsx(
                "div",
                {
                  className: "tl-image",
                  style: {
                    backgroundImage: `url(${!shape.props.playing || reduceMotion ? staticFrameSrc : asset.props.src})`
                  },
                  draggable: false
                }
              ) : null,
              asset?.props.isAnimated && !shape.props.playing && /* @__PURE__ */ jsx("div", { className: "tl-image__tg", children: "GIF" })
            ] }),
            "url" in shape.props && shape.props.url && /* @__PURE__ */ jsx(HyperlinkButton, { url: shape.props.url, zoomLevel: this.editor.getZoomLevel() })
          ]
        }
      )
    ] });
  }
  indicator(shape) {
    const isCropping = useIsCropping(shape.id);
    if (isCropping) {
      return null;
    }
    return /* @__PURE__ */ jsx("rect", { width: toDomPrecision(shape.props.w), height: toDomPrecision(shape.props.h) });
  }
  async toSvg(shape) {
    const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
    const asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : null;
    let src = asset?.props.src || "";
    if (src && src.startsWith("http")) {
      src = (await getDataURIFromURL(src)) || "";
    }
    const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
    image.setAttributeNS("http://www.w3.org/1999/xlink", "href", src);
    const containerStyle = getContainerStyle(shape);
    const crop = shape.props.crop;
    if (containerStyle.transform && crop) {
      const { transform, width, height } = containerStyle;
      const croppedWidth = (crop.bottomRight.x - crop.topLeft.x) * width;
      const croppedHeight = (crop.bottomRight.y - crop.topLeft.y) * height;
      const points = [
        new Vec2d(0, 0),
        new Vec2d(croppedWidth, 0),
        new Vec2d(croppedWidth, croppedHeight),
        new Vec2d(0, croppedHeight)
      ];
      const polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
      polygon.setAttribute("points", points.map((p) => `${p.x},${p.y}`).join(" "));
      const clipPath = document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
      clipPath.setAttribute("id", "cropClipPath");
      clipPath.appendChild(polygon);
      const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
      defs.appendChild(clipPath);
      g.appendChild(defs);
      const innerElement = document.createElementNS("http://www.w3.org/2000/svg", "g");
      innerElement.setAttribute("clip-path", "url(#cropClipPath)");
      image.setAttribute("width", width.toString());
      image.setAttribute("height", height.toString());
      image.style.transform = transform;
      innerElement.appendChild(image);
      g.appendChild(innerElement);
    } else {
      image.setAttribute("width", shape.props.w.toString());
      image.setAttribute("height", shape.props.h.toString());
      g.appendChild(image);
    }
    return g;
  }
  onDoubleClick = (shape) => {
    const asset = shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : void 0;
    if (!asset)
      return;
    const canPlay = asset.props.src && "mimeType" in asset.props && asset.props.mimeType === "image/gif";
    if (!canPlay)
      return;
    this.editor.updateShapes([
      {
        type: "image",
        id: shape.id,
        props: {
          playing: !shape.props.playing
        }
      }
    ]);
  };
  onDoubleClickEdge = (shape) => {
    const props = shape.props;
    if (!props)
      return;
    if (this.editor.getCroppingShapeId() !== shape.id) {
      return;
    }
    const crop = deepCopy(props.crop) || {
      topLeft: { x: 0, y: 0 },
      bottomRight: { x: 1, y: 1 }
    };
    const w = 1 / (crop.bottomRight.x - crop.topLeft.x) * shape.props.w;
    const h = 1 / (crop.bottomRight.y - crop.topLeft.y) * shape.props.h;
    const pointDelta = new Vec2d(crop.topLeft.x * w, crop.topLeft.y * h).rot(shape.rotation);
    const partial = {
      id: shape.id,
      type: shape.type,
      x: shape.x - pointDelta.x,
      y: shape.y - pointDelta.y,
      props: {
        crop: {
          topLeft: { x: 0, y: 0 },
          bottomRight: { x: 1, y: 1 }
        },
        w,
        h
      }
    };
    this.editor.updateShapes([partial]);
  };
}
function getContainerStyle(shape) {
  const crop = shape.props.crop;
  const topLeft = crop?.topLeft;
  if (!topLeft) {
    return {
      width: shape.props.w,
      height: shape.props.h
    };
  }
  const w = 1 / (crop.bottomRight.x - crop.topLeft.x) * shape.props.w;
  const h = 1 / (crop.bottomRight.y - crop.topLeft.y) * shape.props.h;
  const offsetX = -topLeft.x * w;
  const offsetY = -topLeft.y * h;
  return {
    transform: `translate(${offsetX}px, ${offsetY}px)`,
    width: w,
    height: h
  };
}
export {
  ImageShapeUtil
};
//# sourceMappingURL=ImageShapeUtil.mjs.map
