import LisioImgToBlob from "../../../misc/lisio-img-to-blob";
import { LisioReaderAdapter, ReaderObject } from "../lisio-reader-adapter";
import ExploreCalendarHelper from "./explore-calendar-helper";

class ExploreTableHelper {
  private _calendarHelper: ExploreCalendarHelper;
  private _tables: ReaderObject[] = [];
  private _tablesCache = new Map<number, ReaderObject>();

  constructor(calendarHelper: ExploreCalendarHelper) {
    this._calendarHelper = calendarHelper;
  }

  public get tables() {
    return this._tables;
  }

  public async exploreTable(
    readerAdapter: LisioReaderAdapter,
    tag: HTMLElement | SVGElement,
    mediaConverter: LisioImgToBlob,
  ) {
    if (
      tag.localName === "table" &&
      tag.parentElement?.closest<HTMLElement>("table") == undefined &&
      (tag instanceof HTMLElement || tag instanceof SVGElement)
    ) {
      const { opacity } = window.getComputedStyle(tag);
      const tableObject = {
        name: "table",
        childNodes: [],
        position: readerAdapter.all.indexOf(tag),
        isHidden:
          tag instanceof HTMLElement
            ? tag.offsetParent == undefined || parseInt(opacity) === 0
            : parseInt(opacity) === 0,
      };
      this._tablesCache.set(tableObject.position, tableObject);
      this._tables.push(tableObject);
      tag.dataset.lisioPosition = tableObject.position.toString();
      readerAdapter.toSend.push(tableObject);
    } else {
      let tableObject: ReaderObject | undefined;
      if (
        ["thead", "tr", "tbody", "table"].includes(tag.localName) ||
        (tag instanceof HTMLTableCellElement /*td or th */ &&
          Array.from(tag.childNodes).filter(
            (elt) => elt.nodeType === 3 && elt.textContent?.trim() != "",
          ).length) === 0
      ) {
        tableObject = {
          name: tag.localName,
          childNodes: [] as ReaderObject[],
          position: readerAdapter.all.indexOf(tag),
          maybeACalendar:
            tag.parentElement?.closest<HTMLElement>(
              "[data-lisio-maybe-a-calendar]",
            ) != undefined,
        };
        tag.dataset.lisioPosition = tableObject.position.toString();
        this._tablesCache.set(tableObject.position, tableObject);
        const childNodes = Array.from(tag.childNodes).filter(
          (child) => child.nodeType === 3 && child.textContent?.trim() != "",
        );
        if (childNodes.length > 0) {
          for (const childNode of childNodes) {
            tableObject.childNodes?.push({
              name: childNode.nodeName,
              content: childNode.textContent?.trim() || "",
              position: -1,
            });
          }
        }
        const nearestParent = tag.parentElement?.closest<HTMLElement>(
          "td, th, tr, tbody, thead, table",
        );
        if (
          nearestParent != undefined &&
          nearestParent.dataset.lisioPosition != undefined
        ) {
          tableObject.parent = parseInt(nearestParent.dataset.lisioPosition);
        }
      } else if (tag instanceof HTMLImageElement) {
        if (
          !tag.src?.includes("maps.googleapis.com") &&
          !tag.src?.includes("tile.openstreetmap.org")
        ) {
          const datas = readerAdapter.handleImage(tag);
          datas.name = "img";
          tableObject = datas;
        }
      } else if (tag instanceof SVGElement) {
        const hash = await mediaConverter.loadSVG(tag);
        const position = readerAdapter.all.indexOf(tag);
        const { visibility, opacity } = window.getComputedStyle(tag);
        if (
          tag.getAttribute("width") == undefined ||
          tag.getAttribute("height") == undefined
        ) {
          const { width, height } = tag.getBoundingClientRect();
          tag.setAttribute("width", width.toString());
          tag.setAttribute("height", height.toString());
        }
        if (
          tag.getAttribute("width") != "0" &&
          tag.getAttribute("height") != "0"
        ) {
          tableObject = {
            name: "img",
            src: hash,
            position,
            width: tag.getAttribute("width") as string | undefined,
            height: tag.getAttribute("height") as string | undefined,
            insert: position === 0 ? "before" : "after",
            isHidden: visibility === "hidden" || parseInt(opacity) === 0,
          };
        }
      } else if (tag instanceof HTMLElement) {
        const clonedTag = tag.cloneNode(true) as HTMLElement;
        const isHidden = readerAdapter.handleStyles(clonedTag, tag);
        const allTagChildren = tag.querySelectorAll<HTMLElement>("*");
        if (
          clonedTag.dataset.lisioClickable == undefined &&
          (clonedTag.tagName === "A" ||
            clonedTag.tagName === "BUTTON" ||
            (clonedTag.tagName === "INPUT" &&
              clonedTag.getAttribute("type") === "button") ||
            tag.onclick != undefined)
        ) {
          clonedTag.dataset.lisioClickable =
            readerAdapter.dataLisioClickable.toString();
          tag.dataset.lisioClickable =
            readerAdapter.dataLisioClickable.toString();
          readerAdapter.dataLisioClickable++;
        }
        for (const [index, child] of clonedTag
          .querySelectorAll<HTMLElement>("*")
          .entries()) {
          if (child.textContent?.trim() == "") {
            child.remove();
            continue;
          }
          readerAdapter.handleStyles(child, allTagChildren[index]);
          if (
            child.dataset.lisioClickable == undefined &&
            (child instanceof HTMLLinkElement ||
              child instanceof HTMLButtonElement ||
              (child instanceof HTMLInputElement &&
                child.getAttribute("type") === "button") ||
              child.onclick != undefined)
          ) {
            child.dataset.lisioClickable =
              readerAdapter.dataLisioClickable.toString();
            allTagChildren[index].dataset.lisioClickable =
              readerAdapter.dataLisioClickable.toString();
            readerAdapter.dataLisioClickable++;
          }
        }
        const position = readerAdapter.all.indexOf(tag);
        tableObject = {
          name: clonedTag.localName,
          content: clonedTag.textContent?.trim(),
          classes: clonedTag.className,
          position,
          isHidden,
          clickable:
            clonedTag.dataset.lisioClickable == undefined
              ? undefined
              : Number.parseInt(clonedTag.dataset.lisioClickable),
          maybeACalendar: false,
        };
        const closestA = tag.parentElement;
        if (closestA instanceof HTMLAnchorElement) {
          tableObject.name = "a";
          tableObject.blank = `${closestA.target === "_blank"}`;
          tableObject.href =
            closestA.href === "" ? "" : new URL(closestA.href).href;
          if (closestA.dataset.lisioClickable == undefined) {
            closestA.dataset.lisioClickable =
              readerAdapter.dataLisioClickable.toString();
          }
          tableObject.clickable = Number.parseInt(
            closestA.dataset.lisioClickable || "-1",
          );
          readerAdapter.dataLisioClickable++;
        } else if (clonedTag instanceof HTMLAnchorElement) {
          tableObject.blank = `${clonedTag.target === "_blank"}`;
          tableObject.href =
            clonedTag.href === "" ? "" : new URL(clonedTag.href).href;
          if (clonedTag.dataset.lisioClickable == undefined) {
            tag.dataset.lisioClickable =
              readerAdapter.dataLisioClickable.toString();
          }
          tableObject.clickable = Number.parseInt(
            tag.dataset.lisioClickable || "-1",
          );
          readerAdapter.dataLisioClickable++;
        }
        let textContent = "";
        if (
          clonedTag instanceof HTMLSelectElement &&
          clonedTag.selectedOptions.length > 0
        ) {
          textContent =
            clonedTag.selectedOptions[0].textContent
              ?.trim()
              .normalize("NFD")
              .replace(/[\u0300-\u036f]/gu, "") || "";
        } else if (
          clonedTag instanceof HTMLInputElement &&
          clonedTag.type != "hidden"
        ) {
          textContent = clonedTag.value
            .trim()
            .normalize("NFD")
            .replace(/[\u0300-\u036f]/gu, "");
        } else {
          textContent =
            clonedTag.textContent
              ?.trim()
              .normalize("NFD")
              .replace(/[\u0300-\u036f]/gu, "") || "";
        }
        this._calendarHelper.calendarTable(
          textContent,
          tag,
          tableObject,
          this._tablesCache,
        );
      }
      if (
        tableObject != undefined &&
        tableObject.position != undefined &&
        !tableObject.maybeACalendar
      ) {
        const lastParent = tag.closest<HTMLElement>(
          `[data-lisio-position]:not([data-lisio-position="${tableObject.position}"])`,
        );
        tag.dataset.lisioPosition = tableObject.position.toString();
        if (
          lastParent != undefined &&
          lastParent.dataset.lisioPosition != undefined
        ) {
          this._tablesCache
            .get(parseInt(lastParent.dataset.lisioPosition))
            ?.childNodes?.push(tableObject);
        }
      }
    }
  }
}

export default ExploreTableHelper;
