/**
 * Converts an image element to a greyscale data URL.
 * @param imageElement
 * @returns {string}
 */
export function convertImageElementToGreyscaleDataUrl(imageElement) {
  if (!imageElement?.naturalWidth || !imageElement?.naturalHeight) {
    throw new Error("Image must be loaded before it can be converted to greyscale.");
  }

  const canvas = document.createElement("canvas");
  canvas.width = imageElement.naturalWidth;
  canvas.height = imageElement.naturalHeight;

  const context = canvas.getContext("2d");
  context.drawImage(imageElement, 0, 0);

  const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
  const pixels = imageData.data;

  for (let i = 0; i < pixels.length; i += 4) {
    const luminance = Math.round(pixels[i] * 0.299 + pixels[i + 1] * 0.587 + pixels[i + 2] * 0.114);
    pixels[i] = luminance;
    pixels[i + 1] = luminance;
    pixels[i + 2] = luminance;
  }

  context.putImageData(imageData, 0, 0);

  return canvas.toDataURL("image/jpeg", 1);
}

/**
 * Adds a greyscale button to the marker area controls if it doesn't already exist.
 * @param markerArea
 * @param onGreyscale
 * @param title
 */
export function addGreyscaleButtonToMarkerArea(markerArea, onGreyscale, title) {
  requestAnimationFrame(() => {
    const renderButton = markerArea?.coverDiv?.querySelector?.('[data-action="render"]');

    if (!renderButton || markerArea.coverDiv.querySelector('[data-action="greyscale"]')) return;

    const greyscaleButton = document.createElement("div");
    greyscaleButton.className = renderButton.className;
    greyscaleButton.innerHTML =
      '<svg viewBox="0 0 24 24"><path d="M12 2a10 10 0 1 0 0 20V2zm0 2.25v15.5a7.75 7.75 0 0 1 0-15.5z"/></svg>';
    greyscaleButton.setAttribute("role", "button");
    greyscaleButton.setAttribute("data-action", "greyscale");
    greyscaleButton.setAttribute("aria-label", title);
    greyscaleButton.title = title;
    greyscaleButton.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      onGreyscale();
    });

    renderButton.parentElement.insertBefore(greyscaleButton, renderButton);
  });
}

/**
 * Applies a greyscale filter to the image in the marker area and updates the image source.
 * @param markerArea
 * @param imageElement
 * @returns {string}
 */
export function applyGreyscaleToMarkerAreaImage(markerArea, imageElement) {
  const dataUrl = convertImageElementToGreyscaleDataUrl(imageElement);

  setMarkerAreaImageSource(markerArea, imageElement, dataUrl);

  return dataUrl;
}

/**
 * Sets the image source for the marker area and updates the editing target if it's an image element.
 * @param markerArea
 * @param imageElement
 * @param src
 */
export function setMarkerAreaImageSource(markerArea, imageElement, src) {
  imageElement.src = src;

  if (markerArea?.editingTarget instanceof HTMLImageElement) {
    markerArea.editingTarget.src = src;
  }
}

/**
 * Adds undo functionality for image edits to the marker area by tracking the state before and after undo actions.
 * @param markerArea
 * @param canUndoImage
 * @param undoImage
 */
export function addImageHistoryUndoToMarkerArea(markerArea, canUndoImage, undoImage) {
  requestAnimationFrame(() => {
    const undoButton = markerArea?.coverDiv?.querySelector?.('[data-action="undo"]');

    if (!undoButton || undoButton.dataset.imageHistoryUndo === "true") return;

    let markerStateBeforeUndo = null;

    undoButton.dataset.imageHistoryUndo = "true";
    undoButton.addEventListener(
      "click",
      () => {
        markerStateBeforeUndo = JSON.stringify(markerArea.getState(true));
      },
      true
    );
    undoButton.addEventListener("click", () => {
      const markerStateAfterUndo = JSON.stringify(markerArea.getState(true));

      if (markerStateBeforeUndo === markerStateAfterUndo && canUndoImage()) {
        undoImage();
      }

      markerStateBeforeUndo = null;
    });
  });
}
