import { fabric } from "fabric";
import {
  clearObjects,
  sortObjects,
  transformHighlightedText,
  checkTextBoxHeight,
} from "../../components/canvas/utils";
import { PaddedHighlightingTextbox } from "../../components/canvas/textbox";

import topTearImageURL from "../../assets/template/newspaper_quote/newspaper_tear_top.png";
import bottomTearImageURL from "../../assets/template/newspaper_quote_middle/newspaper_tear_bottom.png";
import quoteImageURL from "../../assets/template/newspaper_quote/quote.svg";

let mainTextBox = null;
let mainTextBoxForeground = null;

let personNameText = null;
let personInfoSeparator = null;
let personPositionText = null;
let sourceText = null;

let contractedByTextbox = null;

let logoImage = null;
let quoteImage = null;
let topTearImage = null;
let bottomTearImage = null;
let sourceImage = null;

let leftQuote = null;
let rightQuote = null;

let topTear = null;
let bottomTear = null;
let topTearFill = null;
let bottomTearFill = null;

let background = null;
let previousLogoPosition = null;

const redraw = async (canvas, options) => {
  clearObjects(
    [
      background,
      mainTextBox,
      mainTextBoxForeground,
      personNameText,
      personInfoSeparator,
      personPositionText,
      contractedByTextbox,
      leftQuote,
      rightQuote,
      topTear,
      topTearFill,
      bottomTear,
      bottomTearFill,
    ],
    canvas,
  );

  canvas.preserveObjectStacking = true;

  const textMarginSides = Math.ceil(canvas.width * 0.11);
  let mainTextPaddingBottom = Math.ceil(canvas.height * 0);
  const mainTextMarginBottom = Math.ceil(canvas.height * 0.4);
  const mainTextSize = Math.ceil(canvas.height * 0.043);
  const mainTextLineHeight = 1.3;

  const nameTextMarginTop = Math.ceil(canvas.height * 0.025);
  const positionTextSideGap = Math.ceil(canvas.width * 0.01);
  const positionTextSeparatorWidth = Math.ceil(canvas.width * 0.0035);

  const bottomTextSize = Math.ceil(canvas.height * 0.03);
  const additionalContentExtraBottomPadding = Math.ceil(canvas.height * 0.03);

  const contractedByTextSize = Math.ceil(canvas.height * 0.02);
  const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9);
  const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03);

  const logoWidth = Math.ceil(canvas.width * 0.2);
  const logoSideMargin = Math.ceil(canvas.width * 0.07);

  const sourceImageHeight = Math.ceil(canvas.height * 0.07);
  const sourceImageMarginTop = Math.ceil(canvas.width * 0.07);
  const sourceImageMarginSide = Math.ceil(canvas.width * 0.14);

  const sourceTextMarginSide = Math.ceil(canvas.width * 0.005);

  const quoteHeight = Math.ceil(canvas.height * 0.042);

  const leftQuoteMarginSide = Math.ceil(canvas.width * 0.065);
  const leftQuoteMarginSideExtra = Math.ceil(canvas.width * -0.005);
  const leftQuoteMarginTop = 0;

  const rightQuoteMarginSide = Math.ceil(canvas.width * 0.0175);
  const rightQuoteMarginSideExtra = Math.ceil(canvas.width * 0.005);
  const rightQuoteMarginTop = 0;

  const topTearMarginBottom = Math.ceil(canvas.height * 0.225);
  const bottomTearMarginTop = Math.ceil(canvas.height * 0.125);

  /* BEGIN Background render */

  background = new fabric.Rect({
    width: canvas.width * 1.1,
    height: canvas.height * 1.1,
    top: -20, // FIXME: Why???? Fabric.js, what are you trying to tell me?!
    left: -20,
    fill: options.colors.background.value,
    selectable: false,
    zIndex: 0,
  });

  canvas.add(background);

  /* END Foreground render */

  if (options.mainText !== null) {
    /* BEGIN Name text render */

    if (options.personName !== null) {
      mainTextPaddingBottom += additionalContentExtraBottomPadding;

      let styles = {
        0: {},
      };
      let position = 0;

      for (let position = 0; position < options.personName.length; position++) {
        styles[0][position] = {
          fontWeight: "bold",
        };
      }

      personNameText = new fabric.Text(options.personName, {
        left: textMarginSides,
        top:
          canvas.height -
          mainTextPaddingBottom +
          nameTextMarginTop -
          mainTextMarginBottom,
        fontFamily: "Roboto Condensed",
        fontSize: bottomTextSize,
        styles: styles,
        fill: options.colors.baseText.value,
        selectable: false,
        zIndex: 10,
      });

      canvas.add(personNameText);

      if (options.personPosition !== null) {
        personInfoSeparator = new fabric.Rect({
          left:
            personNameText.left + personNameText.width + positionTextSideGap,
          top: personNameText.top,
          width: positionTextSeparatorWidth,
          fill: options.colors.baseText.value,
          selectable: false,
          zIndex: 10,
        });

        canvas.add(personInfoSeparator);

        personPositionText = new fabric.Text(options.personPosition, {
          left:
            personInfoSeparator.left +
            personInfoSeparator.width +
            positionTextSideGap,
          top: personNameText.top,
          fontFamily: "Roboto Condensed",
          fontSize: bottomTextSize,
          fill: options.colors.baseText.value,
          selectable: false,
          zIndex: 10,
        });

        checkTextBoxHeight(personPositionText, 2);

        canvas.add(personPositionText);
        personInfoSeparator.set({ height: personPositionText.height });
        canvas.renderAll();
      }
    }

    /* END Name text render */

    /* BEGIN Main text render */

    const mainTextWidth = canvas.width - textMarginSides * 2;

    const highlightedData = transformHighlightedText(
      options.mainText,
      mainTextSize,
      mainTextWidth,
      "Glegoo",
      options.colors.highlight.value,
      options.colors.highlightedText.value,
      { padWhenDiacritics: true },
    );

    mainTextBox = new PaddedHighlightingTextbox(highlightedData.text, {
      width: canvas.width,
      left: textMarginSides,
      textAlign: "left",
      fontFamily: "Glegoo",
      fontStyle: "bold",
      fontSize: mainTextSize,
      lineHeight: mainTextLineHeight,
      fill: options.colors.baseText.value,
      styles: highlightedData.styles,
      selectable: false,
      highlightPadding: canvas.height * 0.008,
      zIndex: 10,
    });

    checkTextBoxHeight(mainTextBox, 4);

    canvas.add(mainTextBox);

    const mainTextBoxTop =
      canvas.height -
      mainTextBox.height -
      mainTextPaddingBottom -
      mainTextMarginBottom;

    mainTextBox.top = mainTextBoxTop - highlightedData.paddingBottom;

    canvas.renderAll();

    /* END Main text render */

    /* BEGIN Left quote render */

    // Load on first render
    if (quoteImage === null) {
      quoteImage = new Image();

      const quoteImageLoadPromise = new Promise((resolve) => {
        quoteImage.onload = () => {
          resolve();
        };
      });

      quoteImage.src = quoteImageURL;

      await quoteImageLoadPromise;
    }

    leftQuote = new fabric.Image(quoteImage, {
      selectable: false,
      zIndex: 10,
    });
    leftQuote.scaleToHeight(quoteHeight);
    leftQuote.set({
      left:
        leftQuoteMarginSide +
        (options.mainText[0] === "*" ? leftQuoteMarginSideExtra : 0),
      top: mainTextBoxTop + leftQuoteMarginTop,
    });

    canvas.add(leftQuote);

    /* END Left quote render */

    /* BEGIN Right quote render */

    rightQuote = new fabric.Image(quoteImage, {
      flipX: true,
      selectable: false,
      zIndex: 10,
    });
    rightQuote.scaleToHeight(quoteHeight);
    rightQuote.set({
      left:
        mainTextBox.left +
        mainTextBox.__lineWidths[mainTextBox.__lineWidths.length - 1] +
        rightQuoteMarginSide +
        (options.mainText[options.mainText.length - 1] === "*"
          ? rightQuoteMarginSideExtra
          : 0),
      top:
        mainTextBoxTop +
        mainTextBox.__lineHeights
          .slice(0, -1)
          .reduce((partialSum, a) => partialSum + a, 0) +
        rightQuoteMarginTop,
    });

    canvas.add(rightQuote);

    /* END Right quote render */

    /* BEGIN Top tear render */

    // Load on first render
    if (topTearImage === null) {
      topTearImage = new Image();

      const tearImageLoadPromise = new Promise((resolve) => {
        topTearImage.onload = () => {
          resolve();
        };
      });

      topTearImage.src = topTearImageURL;

      await tearImageLoadPromise;
    }

    topTear = new fabric.Image(topTearImage, {
      selectable: false,
      zIndex: 11,
    });
    topTear.scaleToWidth(canvas.width);
    topTear.set({
      top: mainTextBoxTop - topTearMarginBottom,
      left: 0,
    });

    canvas.add(topTear);

    /* END Top tear render */

    /* BEGIN Top tear fill render */

    topTearFill = new fabric.Polygon(
      [
        { x: -canvas.width * 0.01, y: topTear.top + canvas.height * 0.05 },
        // Hacky seam fix
        { x: canvas.width * 0.1, y: topTear.top + canvas.height * 0.025 },
        { x: canvas.width * 0.15, y: topTear.top + canvas.height * 0.02 },
        { x: canvas.width * 0.2, y: topTear.top + canvas.height * 0.033 },
        { x: canvas.width * 0.3, y: topTear.top + canvas.height * 0.065 },
        { x: canvas.width * 0.4, y: topTear.top + canvas.height * 0.06 },
        { x: canvas.width * 0.5, y: topTear.top + canvas.height * 0.07 },
        { x: canvas.width * 0.53, y: topTear.top + canvas.height * 0.065 },
        { x: canvas.width * 0.6, y: topTear.top + canvas.height * 0.08 },
        { x: canvas.width * 0.75, y: topTear.top + canvas.height * 0.05 },
        { x: canvas.width * 0.85, y: topTear.top + canvas.height * 0.08 },
        { x: canvas.width, y: topTear.top + canvas.height * 0.12 },
        { x: canvas.width, y: topTear.top },
        { x: canvas.width, y: mainTextBoxTop },
        { x: -canvas.width * 0.01, y: mainTextBoxTop },
        // Hacky seam fix
      ],
      {
        top: topTear.top + canvas.height * 0.005, // Hacky seam fix
        left: -(canvas.width * 0.005), // Hacky seam fix
        fill: options.colors.foreground.value,
        selectable: false,
        zIndex: 9,
      },
    );

    canvas.add(topTearFill);

    /* END Top tear fill render */

    /* BEGIN Main text foreground render */

    const foregroundHeight =
      canvas.height - mainTextBoxTop - mainTextMarginBottom;

    mainTextBoxForeground = new fabric.Rect({
      width: canvas.width,
      height: foregroundHeight,
      left: 0,
      top: mainTextBoxTop,
      fill: options.colors.foreground.value,
      selectable: false,
      zIndex: 9,
    });

    canvas.add(mainTextBoxForeground);

    /* END Main text foreground render */

    /* BEGIN Bottom tear render */

    const mainTextBoxForegroundBottomY =
      mainTextBoxForeground.top + mainTextBoxForeground.height;

    // Load on first render
    if (bottomTearImage === null) {
      bottomTearImage = new Image();

      const tearImageLoadPromise = new Promise((resolve) => {
        bottomTearImage.onload = () => {
          resolve();
        };
      });

      bottomTearImage.src = bottomTearImageURL;

      await tearImageLoadPromise;
    }

    bottomTear = new fabric.Image(bottomTearImage, {
      selectable: false,
      zIndex: 11,
    });
    bottomTear.scaleToWidth(canvas.width);
    bottomTear.set({
      top: mainTextBoxForegroundBottomY + bottomTearMarginTop,
      left: 0,
    });

    canvas.add(bottomTear);

    /* END Bottom tear render */

    /* BEGIN Bottom tear fill render */

    const bottomTearBottom = bottomTear.top + bottomTear.getScaledHeight();

    bottomTearFill = new fabric.Polygon(
      [
        { x: -canvas.width * 0.01, y: mainTextBoxForegroundBottomY },
        // Hacky seam fix
        { x: canvas.width, y: mainTextBoxForegroundBottomY },
        { x: canvas.width, y: bottomTearBottom - canvas.height * 0.063 },
        { x: canvas.width * 0.95, y: bottomTearBottom - canvas.height * 0.06 },
        { x: canvas.width * 0.92, y: bottomTearBottom - canvas.height * 0.045 },
        { x: canvas.width * 0.85, y: bottomTearBottom - canvas.height * 0.07 },
        { x: canvas.width * 0.77, y: bottomTearBottom - canvas.height * 0.06 },
        { x: canvas.width * 0.7, y: bottomTearBottom - canvas.height * 0.08 },
        { x: canvas.width * 0.5, y: bottomTearBottom - canvas.height * 0.005 },
        { x: canvas.width * 0.4, y: bottomTearBottom - canvas.height * 0.045 },
        { x: canvas.width * 0.25, y: bottomTearBottom - canvas.height * 0.03 },
        { x: canvas.width * 0.1, y: bottomTearBottom - canvas.height * 0.085 },
        { x: canvas.width * 0.05, y: bottomTearBottom - canvas.height * 0.055 },
        { x: -canvas.width * 0.01, y: bottomTearBottom - canvas.height * 0.07 },
        // Hacky seam fix
      ],
      {
        top: mainTextBoxForegroundBottomY - canvas.height * 0.005, // Hacky seam fix
        left: -(canvas.width * 0.005), // Hacky seam fix
        fill: options.colors.foreground.value,
        selectable: false,
        zIndex: 9,
      },
    );

    canvas.add(bottomTearFill);

    /* END Bottom tear fill render */
  }

  /* BEGIN Logo render */

  // A logo is provided, and it either hasn't been rendered yet or is a new one.
  const createNewLogo =
    (options.logoImage !== null &&
      (logoImage === null ||
        (options.logoImage !== null &&
          options.logoImage !== logoImage._element))) ||
    previousLogoPosition != options.logoPosition.id;

  previousLogoPosition = options.logoPosition.id;

  if (createNewLogo) {
    canvas.remove(logoImage);

    logoImage = new fabric.Image(options.logoImage, { selectable: false });
    logoImage.scaleToWidth(logoWidth);

    if (options.logoPosition.id == "top-right") {
      logoImage.set({
        left: canvas.width - logoWidth - logoSideMargin,
        top: logoSideMargin,
        zIndex: 11,
      });
    } else if (options.logoPosition.id == "top-left") {
      logoImage.set({
        left: logoSideMargin,
        top: logoSideMargin,
        zIndex: 11,
      });
    } else {
      logoImage.set({
        left: logoSideMargin,
        top: canvas.height - logoSideMargin - logoImage.getScaledHeight(),
        zIndex: 11,
      });
    }

    canvas.add(logoImage);
  }

  /* END Logo render */

  /* BEGIN Contracted by render */

  if (options.contractedBy !== null) {
    contractedByTextbox = new fabric.Textbox(options.contractedBy, {
      left:
        canvas.width - contractedByTextMaxWidth - contractedByTextSidesMargin,
      top: canvas.height - contractedByTextSidesMargin - contractedByTextSize,
      width: contractedByTextMaxWidth,
      fontFamily: "Roboto Condensed",
      fontSize: contractedByTextSize,
      textAlign: "right",
      fill: options.colors.contractedByText.value,
      selectable: false,
      zIndex: 10,
    });

    checkTextBoxHeight(contractedByTextbox, 1);

    canvas.add(contractedByTextbox);
  }

  /* END Contracted by render */

  /* BEGIN Source image render */

  const createNewSourceImage =
    options.sourceImage !== null &&
    (sourceImage === null ||
      (options.sourceImage !== null &&
        options.sourceImage !== sourceImage._element));

  if (options.sourceImage !== null && options.personName === null) {
    mainTextPaddingBottom += additionalContentExtraBottomPadding;
  }

  if (createNewSourceImage) {
    canvas.remove(sourceImage);
    canvas.remove(sourceText);

    sourceImage = new fabric.Image(options.sourceImage, {});
    sourceImage.scaleToHeight(sourceImageHeight);
    sourceImage.set({
      left: sourceImageMarginSide,
      top: sourceImageMarginTop,
      selectable: false,
      zIndex: 10,
    });

    canvas.add(sourceImage);

    sourceText = new fabric.Text("Zdroj: ", {
      fontSize: bottomTextSize,
      fill: options.colors.sourceText.value,
      fontFamily: "Roboto Condensed",
      left: sourceImage.left - sourceTextMarginSide,
      top: sourceImage.top,
      selectable: false,
      zIndex: 10,
    });

    sourceText.set({
      left: sourceText.left - sourceText.width,
    });

    canvas.add(sourceText);
  } else if (options.sourceImage === null) {
    canvas.remove(sourceImage);
    canvas.remove(sourceText);
  }

  /* END Source image render */

  sortObjects(canvas);
};

export default redraw;
