import { LisioReaderAdapter, ReaderObject } from "../lisio-reader-adapter";

interface CalendarLangData {
  days: string[];
  longestDayLength: number;
  longestMonthLength: number;
  months: string[];
}

interface CalendarLangDatas {
  en: CalendarLangData;
  fr: CalendarLangData;
}

interface MaybePrevAndNext {
  maybeNext: HTMLElement | undefined;
  maybePrev: HTMLElement | undefined;
}

class ExploreCalendarHelper {
  private _calendarCaches: ReaderObject[] = [];
  private _calendarLang: string = ["fr", "en"].includes(
    document.documentElement.lang,
  )
    ? document.documentElement.lang.toLowerCase().split("-")[0]
    : "fr";
  private _calendarsLangDatas: CalendarLangDatas = {
    fr: {
      months: [
        "Janvier",
        "Fevrier",
        "Mars",
        "Avril",
        "Mai",
        "Juin",
        "Juillet",
        "Aout",
        "Septembre",
        "Octobre",
        "Novembre",
        "Decembre",
      ],
      days: [
        "Lundi",
        "Mardi",
        "Mercredi",
        "Jeudi",
        "Vendredi",
        "Samedi",
        "Dimanche",
      ],
      longestMonthLength: 9,
      longestDayLength: 8,
    },
    en: {
      months: [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
      ],
      days: [
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
        "Sunday",
      ],
      longestMonthLength: 9,
      longestDayLength: 9,
    },
  };
  private _currentDate: Date = new Date(Date.now());
  private _currentMonth: number = this._currentDate.getMonth();
  private _currentMonthReg;
  private _dateInTHead = false;
  private _dayRegs: RegExp[] = [];
  private _daysCache: ReaderObject[] = [];
  private _daysNumberCache: ReaderObject[] = [];
  private _enventsNameCache: string[] = [];
  private _eventsCache: ReaderObject[] = [];
  private _iCal = 0;
  private _iDayRegs = 0;
  private _monthRegs: string[] = [];
  private _monthsCache: ReaderObject[] = [];
  private _nextButton: HTMLElement | undefined;
  private _oldNext: number | undefined;
  private _oldPrev: number | undefined;
  private _prevButton: HTMLElement | undefined;
  private _readerAdapter: LisioReaderAdapter;
  private _selectorLeftButton =
    '[id*="left"]:not([id*="next"]), [class*="left"]:not([class*="next"]), [data-*="left"]:not([data-*="next"]), [aria-label*="left"]:not([aria-label*="next"])';
  private _selectorNextButton =
    '[id*="next"], [class*="next"], [data-*="next"], [aria-label*="next"]';
  private _selectorPrevButton =
    '[id*="prev"], [class*="prev"], [data-*="prev"], [aria-label*="prev"]';
  private _selectorRightButton =
    '[id*="right"]:not([id*="prev"]), [class*="right"]:not([class*="prev"]), [data-*="right"]:not([data-*="prev"]), [aria-label*="right"]:not([aria-label*="prev"])';
  private _shadowRootCalendar?: ShadowRoot;
  private _yearsCache: ReaderObject[] = [];

  constructor(readerAdapter: LisioReaderAdapter) {
    this._readerAdapter = readerAdapter;
    for (const [monthIndex, month] of this._calendarsLangDatas[
      this._calendarLang as keyof CalendarLangDatas
    ].months.entries()) {
      const monthFDigit = Math.floor((monthIndex + 1) / 10);
      const monthSDigit = (monthIndex + 1) % 10;
      const space = "[-\\\\s]";
      const threeFirstMonthLetters = month.slice(0, 3);
      const monthLetters = [...month.slice(3)];
      const firstP = [monthFDigit, monthSDigit, space];
      this._monthRegs.push(
        `^${firstP
          .map((elt) => this.createNonCapturingGroup(elt.toString()))
          .join("")}${threeFirstMonthLetters}${this.createNonCapturingGroup(
          "\\.",
          false,
        )}${monthLetters
          .map((elt) => this.createNonCapturingGroup(elt, true))
          .join("")}$`,
      );
    }

    this._currentMonthReg = new RegExp(
      this._monthRegs[this._currentMonth],
      "i",
    );

    for (const day of this._calendarsLangDatas[
      this._calendarLang as keyof CalendarLangDatas
    ].days) {
      const firstdayLetters = day.slice(0, 1);
      const dayLetters = [...day.slice(1)];
      this._dayRegs.push(
        new RegExp(
          `^${firstdayLetters}${this.createNonCapturingGroup(
            "\\.",
            false,
          )}${dayLetters
            .map((elt) => this.createNonCapturingGroup(elt, true))
            .join("")}$`,
          "i",
        ),
      );
    }
  }

  public get calendarLang() {
    return this._calendarLang;
  }

  public get shadowRootCalendar(): ShadowRoot | undefined {
    return this._shadowRootCalendar;
  }

  public set shadowRootCalendar(value: ShadowRoot) {
    this._shadowRootCalendar = value;
  }

  public calendarForm(
    textContent: string,
    tag: HTMLElement,
    formObject: ReaderObject,
    formsCache: Map<number, ReaderObject>,
  ) {
    let ret = true;
    const maybePrevOrNext: {
      maybePrev: HTMLElement | undefined;
      maybeNext: HTMLElement | undefined;
    } = {
      maybePrev: undefined,
      maybeNext: undefined,
    };
    this.findPrevOrNextButton(maybePrevOrNext, tag, textContent);
    const numberTextContent = Number.parseInt(textContent);
    if (
      tag instanceof HTMLInputElement ||
      tag instanceof HTMLSelectElement ||
      tag instanceof HTMLButtonElement ||
      tag instanceof HTMLTextAreaElement
    ) {
      const numberTextContent = Number.parseInt(textContent);
      if (
        textContent.length === 4 &&
        !isNaN(numberTextContent) &&
        /^\d+$/.test(textContent) &&
        this._daysNumberCache.length === 0 &&
        !formObject.isHidden
      ) {
        formObject.maybeACalendar = true;
        tag.dataset.lisioMaybeACalendar = "true";
        this._yearsCache.push(formObject);
        this._calendarCaches.push(formObject);
        formObject.content = textContent;
        // let parent = tag.parentElement;
        // let closest = parent.closest<HTMLElement>('td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])');
        // while(closest != undefined){
        //     closest.dataset.lisioMaybeACalendar = true;
        //     parent = parent.parentElement;
        //     closest = parent.closest<HTMLElement>('td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])');
        // }
      } else if (
        textContent.length <
          this._calendarsLangDatas[this._calendarLang as "fr" | "en"]
            .longestMonthLength +
            6 &&
        (new RegExp(
          `^\\w{1,${
            this._calendarsLangDatas[this._calendarLang as "fr" | "en"]
              .longestMonthLength
          }}(?:\\.)?(?:\\s)(\\d{4})?$`,
          "i",
        ).test(textContent)
          ? this._currentMonthReg.test(
              // eslint-disable-next-line no-useless-escape
              textContent.split(/(?:[\s\.\-])?\d{4}/i)[0],
            )
          : this._currentMonthReg.test(textContent)) &&
        this._daysNumberCache.length === 0 &&
        !formObject.isHidden
      ) {
        // if(isMonthAndYear){
        //     yearsCache.push(formObject);
        // }
        formObject.maybeACalendar = true;
        tag.dataset.lisioMaybeACalendar = "true";
        this._monthsCache.push(formObject);
        this._calendarCaches.push(formObject);
        formObject.content = textContent;
        // let parent = tag.parentElement;
        // let closest = parent.closest<HTMLElement>('td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])');
        // while(closest != undefined){
        //     closest.dataset.lisioMaybeACalendar = true;
        //     parent = parent.parentElement;
        //     closest = parent.closest<HTMLElement>('td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])');
        // }
      } else {
        formsCache.set(formObject.position, formObject);
      }
    } else if (
      textContent != undefined &&
      textContent.length < 3 &&
      !isNaN(numberTextContent) &&
      /^\d+$/.test(textContent) &&
      !formObject.isHidden
    ) {
      formObject.maybeACalendar =
        numberTextContent > 0 && numberTextContent < 32;
      tag.dataset.lisioMaybeACalendar = "true";
      this._daysNumberCache.push(formObject);
      this._calendarCaches.push(formObject);
      formObject.content = textContent;
      // let parent = tag.parentElement;
      // let closest = parent.closest<HTMLElement>('td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])');
      // while(closest != undefined){
      //     closest.dataset.lisioMaybeACalendar = true;
      //     parent = parent.parentElement;
      //     closest = parent.closest<HTMLElement>('td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])');
      // }
    } else if (
      textContent != undefined &&
      textContent.length <
        this._calendarsLangDatas[this._calendarLang as "fr" | "en"]
          .longestDayLength +
          1 &&
      this._dayRegs[
        textContent.charAt(0).toLowerCase() ===
          this._calendarsLangDatas[this._calendarLang as "fr" | "en"].days[0]
            .charAt(0)
            .toLowerCase() || this._iDayRegs > 0
          ? this._iDayRegs
          : 6
      ].test(textContent) &&
      this._daysNumberCache.length === 0 &&
      !formObject.isHidden
    ) {
      formObject.maybeACalendar = true;
      tag.dataset.lisioMaybeACalendar = "true";
      if (
        textContent.charAt(0).toLowerCase() !==
          this._calendarsLangDatas[this._calendarLang as "fr" | "en"].days[0]
            .charAt(0)
            .toLowerCase() &&
        this._iDayRegs === 0
      ) {
        this._iDayRegs = 6;
      }
      this._iDayRegs = ++this._iDayRegs % 7;
      this._daysCache.push(formObject);
      this._calendarCaches.push(formObject);
      formObject.content = textContent;
    } else {
      ret = false;
    }
    if (!formObject.maybeACalendar) {
      if (
        this._calendarCaches.length > 0 &&
        maybePrevOrNext.maybeNext == undefined &&
        maybePrevOrNext.maybePrev == undefined &&
        !formObject.isHidden
      ) {
        this._iDayRegs = 0;
        // tester si un élément est peut-être un calendrier
        if (this._calendarCaches.length < 30) {
          for (const calendarCache of this._calendarCaches) {
            calendarCache.maybeACalendar = false;
            const lastParent = this._readerAdapter.all[
              calendarCache.position
            ].closest<HTMLElement>(
              `[data-lisio-position]:not([data-lisio-position="${calendarCache.position}"])`,
            );
            this._readerAdapter.all[calendarCache.position].removeAttribute(
              "data-lisio-maybe-a-calendar",
            );
            this._readerAdapter.all[
              calendarCache.position
            ].dataset.lisioPosition = formObject.position.toString();
            try {
              if (
                lastParent != undefined &&
                lastParent.dataset.lisioPosition != undefined
              ) {
                formsCache
                  .get(parseInt(lastParent.dataset.lisioPosition))
                  ?.childNodes?.push(calendarCache);
              }
              // eslint-disable-next-line @typescript-eslint/no-unused-vars
            } catch (error) {
              formsCache.set(calendarCache.position, calendarCache);
            }
          }
        } else {
          let commonAncestor: HTMLElement | SVGElement | undefined;
          if (
            this._readerAdapter.all[
              this._calendarCaches[0].position
            ]?.className?.includes("lightpick")
          ) {
            commonAncestor = this._readerAdapter.all[
              this._calendarCaches[0].position
            ].closest<HTMLElement>(".lightpick") as HTMLElement | undefined;
          } else {
            const allAncestors = this.getAncestors(
              this._readerAdapter.all[this._calendarCaches[0].position],
            );
            for (let i = 1; i < allAncestors.length; i++) {
              if (this._calendarCaches[i] != undefined) {
                const ancestors = this.getAncestors(
                  this._readerAdapter.all[this._calendarCaches[i].position],
                );
                const commonAncestors = allAncestors.filter((elt) =>
                  ancestors.includes(elt),
                );
                if (commonAncestors.length > 0) {
                  commonAncestor = commonAncestors[0];
                }
              }
            }
          }
          if (commonAncestor) {
            commonAncestor.dataset.lisioCalendar = "true";
          }

          const calendarObserver = new MutationObserver((records) => {
            const newClickables = new Map<number, number>();
            const nMaybePrevOrNext: {
              maybePrev: HTMLElement | undefined;
              maybeNext: HTMLElement | undefined;
            } = {
              maybePrev: undefined,
              maybeNext: undefined,
            };
            const texts = [];
            for (const record of records) {
              for (const addedNode of record.addedNodes) {
                const textIterator = document.createNodeIterator(
                  addedNode,
                  NodeFilter.SHOW_TEXT,
                  {
                    acceptNode(node) {
                      return node.textContent?.trim() != ""
                        ? NodeFilter.FILTER_ACCEPT
                        : NodeFilter.FILTER_REJECT;
                    },
                  },
                );
                let textNode: Node | undefined = undefined;
                while (
                  (textNode = textIterator.nextNode() as Node | undefined)
                ) {
                  if (textNode.nodeType === Node.TEXT_NODE) {
                    const textContent = textNode.textContent || "";
                    const numberTextContent = Number.parseInt(textContent);
                    if (
                      textContent.length < 3 &&
                      !isNaN(numberTextContent) &&
                      numberTextContent === texts.length + 1 &&
                      /^\d+$/.test(textContent)
                    ) {
                      const element = textNode.parentNode;
                      if (
                        element instanceof HTMLElement &&
                        element.dataset.lisioClickable == undefined &&
                        !newClickables.has(numberTextContent)
                      ) {
                        //à mettre sur le boutton ou l'élément réellement clickable
                        element.dataset.lisioClickable =
                          this._readerAdapter.dataLisioClickable.toString();
                        newClickables.set(
                          numberTextContent,
                          this._readerAdapter.dataLisioClickable,
                        );
                        this._readerAdapter.dataLisioClickable++;
                      }
                    }
                  }
                }
                if (
                  addedNode.nodeType === 1 &&
                  addedNode instanceof HTMLElement &&
                  addedNode.textContent != undefined
                ) {
                  this.findPrevOrNextButton(
                    nMaybePrevOrNext,
                    addedNode,
                    addedNode.textContent,
                  );
                }
              }
            }
            if (commonAncestor?.dataset.iCalendar != undefined) {
              this._readerAdapter.broadcastChannel.sendRefreshCalendarMessage({
                iCalendar: Number.parseInt(commonAncestor.dataset.iCalendar),
                clickables: newClickables,
              });
            }
          });

          if (commonAncestor != undefined) {
            calendarObserver.observe(commonAncestor, {
              childList: true,
              subtree: true,
            });
            commonAncestor.dataset.iCalendar = this._iCal.toString();
          }

          this._oldPrev =
            this._prevButton?.dataset.lisioClickable == undefined
              ? undefined
              : Number.parseInt(this._prevButton.dataset.lisioClickable);
          this._oldNext =
            this._nextButton?.dataset.lisioClickable == undefined
              ? undefined
              : Number.parseInt(this._nextButton.dataset.lisioClickable);

          const cacheIndex = formsCache.keys().next().value;
          if (cacheIndex != undefined && commonAncestor != undefined) {
            formsCache.get(cacheIndex)?.childNodes?.push({
              name: "calendar",
              width: window.getComputedStyle(commonAncestor).width,
              iCalendar: this._iCal,
              isShadowRootCalendar: this._shadowRootCalendar != undefined,
              position:
                this._calendarCaches[this._calendarCaches.length - 1].position,
              month: [...this._monthsCache],
              year: [...this._yearsCache],
              days: [...this._daysCache],
              daysNumbers: [...this._daysNumberCache],
              events: {},
              prevButtonClickable: this._prevButton?.dataset.lisioClickable,
              nextButtonClickable: this._nextButton?.dataset.lisioClickable,
              childNodes: [...this._calendarCaches],
            });
          }
        }
        this._iCal++;
        this._calendarCaches.splice(0, this._calendarCaches.length);
        this._monthsCache.splice(0, this._monthsCache.length);
        this._yearsCache.splice(0, this._yearsCache.length);
        this._daysCache.splice(0, this._daysCache.length);
        this._daysNumberCache.splice(0, this._daysNumberCache.length);
        this._prevButton = undefined;
        this._nextButton = undefined;
      }

      if (
        tag.dataset.lisioClickable == undefined &&
        (tag.tagName === "A" ||
          tag.tagName === "BUTTON" ||
          tag.getAttribute("type") === "button" ||
          tag.getAttribute("type") === "submit" ||
          tag.onclick != undefined)
      ) {
        formObject.clickable = this._readerAdapter.dataLisioClickable;
        tag.dataset.lisioClickable =
          this._readerAdapter.dataLisioClickable.toString();
        this._readerAdapter.dataLisioClickable++;
      } else if (
        tag.dataset.lisioChange == undefined &&
        (tag.localName === "select" ||
          tag.localName === "textarea" ||
          tag.localName === "input")
      ) {
        formObject.change = this._readerAdapter.dataLisioChange;
        tag.dataset.lisioChange =
          this._readerAdapter.dataLisioChange.toString();
        this._readerAdapter.dataLisioChange++;
      }
      if (formObject.position != -1 && !formObject.maybeACalendar) {
        tag.dataset.lisioPosition = formObject.position.toString();
        let cached;
        let lastPosition;
        let lastParent: HTMLElement | undefined = tag;
        do {
          if (lastPosition == undefined) {
            lastPosition = formObject.position || -1;
            lastParent = tag.closest<HTMLElement>(
              `[data-lisio-position]:not([data-lisio-position="${lastPosition}"])`,
            ) as HTMLElement | undefined;
          } else {
            lastPosition = lastParent.dataset.lisioPosition;
            lastParent = lastParent.closest<HTMLElement>(
              `[data-lisio-position]:not([data-lisio-position="${lastPosition}"])`,
            ) as HTMLElement | undefined;
          }
          if (
            lastParent != undefined &&
            lastParent.dataset.lisioPosition != undefined
          ) {
            const cachedPosition = parseInt(lastParent.dataset.lisioPosition);
            cached = formsCache.get(cachedPosition);
          }
        } while (cached == undefined && lastParent != undefined);
        cached?.childNodes?.push(formObject);
      }
    }
    return ret;
  }

  public calendarOther(
    textContent: string,
    tag: HTMLElement,
    datas: ReaderObject,
  ) {
    const maybePrevOrNext: {
      maybePrev: HTMLElement | undefined;
      maybeNext: HTMLElement | undefined;
    } = {
      maybePrev: undefined,
      maybeNext: undefined,
    };
    this.findPrevOrNextButton(maybePrevOrNext, tag, textContent);
    const numberTextContent = Number.parseInt(textContent || "");
    if (
      textContent.length === 4 &&
      !isNaN(numberTextContent) &&
      /^\d+$/.test(textContent) &&
      this._daysNumberCache.length === 0
    ) {
      datas.maybeACalendar = true;
      tag.dataset.lisioMaybeACalendar = "true";
      this._yearsCache.push(datas);
      this._calendarCaches.push(datas);
    } else if (
      textContent.length < 3 &&
      !isNaN(numberTextContent) &&
      /^\d+$/.test(textContent)
    ) {
      datas.maybeACalendar = numberTextContent > 0 && numberTextContent < 32;
      tag.dataset.lisioMaybeACalendar = "true";
      this._daysNumberCache.push(datas);
      this._calendarCaches.push(datas);
    } else if (
      textContent.length <
        this._calendarsLangDatas[this._calendarLang as "fr" | "en"]
          .longestMonthLength +
          6 &&
      (new RegExp(
        `^\\w{1,${
          this._calendarsLangDatas[this._calendarLang as "fr" | "en"]
            .longestMonthLength
        }}(?:\\.)?(?:\\s)(\\d{4})?$`,
        "i",
      ).test(textContent)
        ? this._currentMonthReg.test(
            // eslint-disable-next-line no-useless-escape
            textContent.split(/(?:[\s\.\-])?\d{4}/i)[0],
          )
        : this._currentMonthReg.test(textContent)) &&
      this._daysNumberCache.length === 0
    ) {
      // if(isMonthAndYear){
      //     this._yearsCache.push(datas);
      // }
      datas.maybeACalendar = true;
      tag.dataset.lisioMaybeACalendar = "true";
      this._monthsCache.push(datas);
      this._calendarCaches.push(datas);
    } else if (
      textContent.length <
        this._calendarsLangDatas[this._calendarLang as "fr" | "en"]
          .longestDayLength +
          1 &&
      this._dayRegs[
        textContent.charAt(0).toLowerCase() ===
          this._calendarsLangDatas[this._calendarLang as "fr" | "en"].days[0]
            .charAt(0)
            .toLowerCase() || this._iDayRegs > 0
          ? this._iDayRegs
          : 6
      ].test(textContent) &&
      this._daysNumberCache.length === 0
    ) {
      datas.maybeACalendar = true;
      tag.dataset.lisioMaybeACalendar = "true";
      if (
        textContent.charAt(0).toLowerCase() !==
          this._calendarsLangDatas[this._calendarLang as "fr" | "en"].days[0]
            .charAt(0)
            .toLowerCase() &&
        this._iDayRegs === 0
      ) {
        this._iDayRegs = 6;
      }
      this._iDayRegs = ++this._iDayRegs % 7;
      this._daysCache.push(datas);
      this._calendarCaches.push(datas);
    } else if (
      this._calendarCaches.length > 0 &&
      maybePrevOrNext.maybeNext == undefined &&
      maybePrevOrNext.maybePrev == undefined &&
      !datas.isHidden
    ) {
      this._iDayRegs = 0;
      // tester si un élément est peut-être un calendrier
      if (
        this._calendarCaches.length < 30 &&
        this._daysNumberCache.length > 0
      ) {
        for (const calendarCache of this._calendarCaches) {
          calendarCache.maybeACalendar = false;
          this._readerAdapter.all[calendarCache.position].removeAttribute(
            "data-lisio-maybe-a-calendar",
          );
        }
        this._readerAdapter.toSend.push(...this._calendarCaches);
        this._readerAdapter.toSend.push(datas);
      } else if (this._daysNumberCache.length > 0) {
        let commonAncestor = this._readerAdapter.all[
          this._calendarCaches[0].position
        ].closest<HTMLElement | SVGElement>('[id*="calendar"]');
        if (commonAncestor == undefined) {
          const allAncestors = this.getAncestors(
            this._readerAdapter.all[this._calendarCaches[0].position],
          );
          for (let i = 1; i < allAncestors.length; i++) {
            if (this._calendarCaches[i] != undefined) {
              const ancestors = this.getAncestors(
                this._readerAdapter.all[this._calendarCaches[i].position],
              );
              const commonAncestors = allAncestors.filter((elt) =>
                ancestors.includes(elt),
              );
              if (commonAncestors.length > 0) {
                commonAncestor = commonAncestors[0];
              }
            }
          }
        }

        const allAncestorsDate = this.getAncestors(
          this._readerAdapter.all[this._daysNumberCache[0].position],
        );
        let commonAncestorDate;
        for (let i = 1; i < allAncestorsDate.length; i++) {
          if (this._daysNumberCache[i] != undefined) {
            const ancestors = this.getAncestors(
              this._readerAdapter.all[this._daysNumberCache[i].position],
            );
            const commonAncestors = allAncestorsDate.filter((elt) =>
              ancestors.includes(elt),
            );
            if (commonAncestors.length > 0) {
              commonAncestorDate = commonAncestors[0];
            }
          }
        }

        if (
          commonAncestor != undefined &&
          (commonAncestor.querySelector(this._selectorPrevButton) !=
            this._prevButton ||
            commonAncestor?.querySelector(this._selectorNextButton) !=
              this._nextButton)
        ) {
          this.findPrevOrNextButton(
            maybePrevOrNext,
            commonAncestor,
            commonAncestor.textContent || "",
          );
        }

        const calendarObserver = new MutationObserver((records) => {
          const newClickables = new Map();
          const maybePrevOrNext: {
            maybePrev: HTMLElement | undefined;
            maybeNext: HTMLElement | undefined;
          } = {
            maybePrev: undefined,
            maybeNext: undefined,
          };
          const texts = [];
          for (const record of records) {
            for (const addedNode of record.addedNodes) {
              const textIterator = document.createNodeIterator(
                addedNode,
                NodeFilter.SHOW_TEXT,
                {
                  acceptNode(node) {
                    return node.textContent?.trim() != ""
                      ? NodeFilter.FILTER_ACCEPT
                      : NodeFilter.FILTER_REJECT;
                  },
                },
              );
              let textNode: Node | undefined = undefined;
              while ((textNode = textIterator.nextNode() as Node | undefined)) {
                if (textNode.nodeType === Node.TEXT_NODE) {
                  const textContent = textNode.textContent || "";
                  const numberTextContent = Number.parseInt(textContent);
                  if (
                    textContent.length < 3 &&
                    !isNaN(numberTextContent) &&
                    numberTextContent === texts.length + 1 &&
                    /^\d+$/.test(textContent)
                  ) {
                    const element = textNode.parentNode;
                    if (
                      element instanceof HTMLElement &&
                      element.dataset.lisioClickable == undefined &&
                      !newClickables.has(numberTextContent)
                    ) {
                      //à mettre sur le boutton ou l'élément réellement clickable
                      element.dataset.lisioClickable =
                        this._readerAdapter.dataLisioClickable.toString();
                      newClickables.set(
                        numberTextContent,
                        this._readerAdapter.dataLisioClickable,
                      );
                      this._readerAdapter.dataLisioClickable++;
                    }
                  }
                }
              }
              if (
                addedNode instanceof HTMLElement ||
                addedNode instanceof SVGElement
              ) {
                this.findPrevOrNextButton(
                  maybePrevOrNext,
                  addedNode,
                  addedNode.textContent || "",
                );
              }
            }
          }
          if (commonAncestor?.dataset.iCalendar != undefined) {
            this._readerAdapter.broadcastChannel.sendRefreshCalendarMessage({
              iCalendar: Number.parseInt(commonAncestor.dataset.iCalendar),
              clickables: newClickables,
            });
          }
        });

        if (commonAncestor != undefined && commonAncestorDate != undefined) {
          calendarObserver.observe(
            this._shadowRootCalendar != undefined
              ? this._shadowRootCalendar
              : commonAncestor,
            { childList: true, subtree: true },
          );
          if (commonAncestor != undefined) {
            commonAncestor.dataset.iCalendar = this._iCal.toString();
          }

          this._oldPrev =
            this._prevButton?.dataset.lisioClickable == undefined
              ? undefined
              : Number.parseInt(this._prevButton?.dataset.lisioClickable);
          this._oldNext =
            this._nextButton?.dataset.lisioClickable == undefined
              ? undefined
              : Number.parseInt(this._nextButton?.dataset.lisioClickable);
          const { visibility, opacity } =
            window.getComputedStyle(commonAncestor);
          const calendar: ReaderObject = {
            name: "calendar",
            width: Number.parseInt(
              window.getComputedStyle(commonAncestorDate).width,
            ).toString(),
            iCalendar: this._iCal,
            isHidden:
              commonAncestor instanceof HTMLElement
                ? commonAncestor?.offsetParent == undefined ||
                  visibility === "hidden" ||
                  parseInt(opacity) === 0
                : visibility === "hidden" || parseInt(opacity) === 0,
            isShadowRootCalendar: this._shadowRootCalendar != undefined,
            position:
              this._calendarCaches[this._calendarCaches.length - 1].position,
            month: [...this._monthsCache],
            year: [...this._yearsCache],
            days: [...this._daysCache],
            daysNumbers: [...this._daysNumberCache],
            events: {},
            prevButtonClickable: this._prevButton?.dataset.lisioClickable,
            nextButtonClickable: this._nextButton?.dataset.lisioClickable,
            childNodes: [...this._calendarCaches],
          };
          for (const event of this._eventsCache) {
            if (
              calendar.events != undefined &&
              event.startingYear != undefined &&
              event.startingMonth != undefined &&
              event.startingDate != undefined &&
              event.duration != undefined
            ) {
              if (calendar.events[event.startingYear] == undefined) {
                calendar.events[event.startingYear] = {};
              }
              if (
                calendar.events[event.startingYear][event.startingMonth] ==
                undefined
              ) {
                calendar.events[event.startingYear][event.startingMonth] = {};
              }
              if (
                calendar.events[event.startingYear][event.startingMonth][
                  event.startingDate
                ] == undefined
              ) {
                calendar.events[event.startingYear][event.startingMonth][
                  event.startingDate
                ] = {};
              }
              if (event.duration != undefined && calendar.width != undefined) {
                const duration = Math.ceil(
                  (event.duration / Number.parseInt(calendar.width)) * 7,
                );
                event.duration = duration;
              }
              if (
                calendar.events[event.startingYear][event.startingMonth][
                  event.startingDate
                ][event.duration] == undefined
              ) {
                calendar.events[event.startingYear][event.startingMonth][
                  event.startingDate
                ][event.duration] = [];
              }
              if (
                calendar.events[event.startingYear][event.startingMonth][
                  event.startingDate
                ][event.duration] == undefined
              ) {
                calendar.events[event.startingYear][event.startingMonth][
                  event.startingDate
                ][event.duration].push(event);
              }
            }
          }
          this._readerAdapter.toSend.push(calendar);
        }
        this._iCal++;
      }
      this._calendarCaches.splice(0, this._calendarCaches.length);
      this._monthsCache.splice(0, this._monthsCache.length);
      this._yearsCache.splice(0, this._yearsCache.length);
      this._daysCache.splice(0, this._daysCache.length);
      this._daysNumberCache.splice(0, this._daysNumberCache.length);
      this._prevButton = undefined;
      this._nextButton = undefined;
    } else {
      return false;
    }
    return true;
  }

  public calendarTable(
    textContent: string,
    tag: HTMLElement,
    tableObject: ReaderObject,
    tablesCache: Map<number, ReaderObject>,
  ) {
    const maybePrevOrNext: {
      maybePrev: HTMLElement | undefined;
      maybeNext: HTMLElement | undefined;
    } = {
      maybePrev: undefined,
      maybeNext: undefined,
    };
    const numberTextContent = Number.parseInt(textContent);
    this.findPrevOrNextButton(maybePrevOrNext, tag, textContent);
    if (
      textContent.length === 4 &&
      !isNaN(numberTextContent) &&
      /^\d+$/.test(textContent) &&
      this._daysNumberCache.length === 0 &&
      !tableObject.isHidden
    ) {
      tableObject.maybeACalendar = true;
      tag.dataset.lisioMaybeACalendar = "true";
      this._yearsCache.push(tableObject);
      this._calendarCaches.push(tableObject);
      let parent: HTMLElement | undefined = tag.parentElement as
        | HTMLElement
        | undefined;
      let closest: HTMLElement | undefined = parent?.closest<HTMLElement>(
        "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
      ) as HTMLElement | undefined;
      while (closest != undefined) {
        if (closest instanceof HTMLElement) {
          closest.dataset.lisioMaybeACalendar = "true";
        }
        parent = parent?.parentElement as HTMLElement | undefined;
        closest = parent?.closest<HTMLElement>(
          "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
        ) as HTMLElement | undefined;
      }
    } else if (
      textContent.length < 3 &&
      !isNaN(numberTextContent) &&
      /^\d+$/.test(textContent)
    ) {
      tableObject.maybeACalendar =
        numberTextContent > 0 && numberTextContent < 32;
      tag.dataset.lisioMaybeACalendar = "true";
      this._daysNumberCache.push(tableObject);
      this._calendarCaches.push(tableObject);
      if (tag.closest<HTMLElement>("thead") != undefined) {
        this._dateInTHead = true;
      }
      let parent: HTMLElement | undefined = tag.parentElement as
        | HTMLElement
        | undefined;
      let closest: HTMLElement | undefined = parent?.closest<HTMLElement>(
        "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
      ) as HTMLElement | undefined;
      while (closest != undefined) {
        closest.dataset.lisioMaybeACalendar = "true";
        parent = parent?.parentElement as HTMLElement | undefined;
        closest = parent?.closest<HTMLElement>(
          "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
        ) as HTMLElement | undefined;
      }
    } else if (
      textContent.length <
        this._calendarsLangDatas[this._calendarLang as "fr" | "en"]
          .longestMonthLength +
          6 &&
      (new RegExp(
        `^\\w{1,${
          this._calendarsLangDatas[this._calendarLang as "fr" | "en"]
            .longestMonthLength
        }}(?:\\.)?(?:\\s)(\\d{4})?$`,
        "i",
      ).test(textContent)
        ? this._currentMonthReg.test(
            // eslint-disable-next-line no-useless-escape
            textContent.split(/(?:[\s\.\-])?\d{4}/i)[0],
          )
        : this._currentMonthReg.test(textContent)) &&
      this._daysNumberCache.length === 0 &&
      !tableObject.isHidden
    ) {
      // if(isMonthAndYear){
      //     this._yearsCache.push(tableObject);
      // }
      tableObject.maybeACalendar = true;
      tag.dataset.lisioMaybeACalendar = "true";
      this._monthsCache.push(tableObject);
      this._calendarCaches.push(tableObject);
      let parent: HTMLElement | undefined = tag.parentElement as
        | HTMLElement
        | undefined;
      let closest = parent?.closest<HTMLElement>(
        "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
      ) as HTMLElement | undefined;
      while (closest != undefined) {
        closest.dataset.lisioMaybeACalendar = "true";
        parent = parent?.parentElement as HTMLElement | undefined;
        closest = parent?.closest<HTMLElement>(
          "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
        ) as HTMLElement | undefined;
      }
    } else if (
      textContent.length <
        this._calendarsLangDatas[this._calendarLang as "fr" | "en"]
          .longestDayLength +
          1 &&
      this._dayRegs[
        textContent.charAt(0).toLowerCase() ===
          this._calendarsLangDatas[this._calendarLang as "fr" | "en"].days[0]
            .charAt(0)
            .toLowerCase() || this._iDayRegs > 0
          ? this._iDayRegs
          : 6
      ].test(textContent) &&
      this._daysNumberCache.length === 0 &&
      !tableObject.isHidden
    ) {
      tableObject.maybeACalendar = true;
      tag.dataset.lisioMaybeACalendar = "true";
      if (
        textContent.charAt(0).toLowerCase() !==
          this._calendarsLangDatas[this._calendarLang as "fr" | "en"].days[0]
            .charAt(0)
            .toLowerCase() &&
        this._iDayRegs === 0
      ) {
        this._iDayRegs = 6;
      }
      this._iDayRegs = ++this._iDayRegs % 7;
      this._daysCache.push(tableObject);
      this._calendarCaches.push(tableObject);
      let parent: HTMLElement | undefined = tag.parentElement as
        | HTMLElement
        | undefined;
      let closest: HTMLElement | undefined = parent?.closest<HTMLElement>(
        "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
      ) as HTMLElement | undefined;
      while (closest != undefined) {
        closest.dataset.lisioMaybeACalendar = "true";
        parent = parent?.parentElement as HTMLElement | undefined;
        closest = parent?.closest<HTMLElement>(
          "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
        ) as HTMLElement | undefined;
      }
    } else if (tag.closest<HTMLElement>("[data-lisio-maybe-a-calendar]")) {
      if (
        !tableObject.isHidden &&
        tag.parentElement?.closest<HTMLElement>("a") == undefined
      ) {
        tableObject.maybeACalendar = true;
        tableObject.maybeAnEvent = true;
        tableObject.startingYear = this._currentDate.getFullYear();
        tableObject.startingMonth = this._currentDate.getMonth();
        if (this._dateInTHead) {
          const closestTd = tag.closest<HTMLTableCellElement>("td");
          if (closestTd != undefined) {
            const closestTr = closestTd.closest<HTMLTableRowElement>("tr");
            if (closestTr != undefined) {
              const closestTBody =
                tag.closest<HTMLTableSectionElement>("tbody");
              if (closestTBody != undefined) {
                const closestTable = tag.closest<HTMLTableElement>("table");
                const iTr = Array.from(
                  closestTBody.querySelectorAll("tr"),
                ).indexOf(closestTr);
                const iTd = Array.from(
                  closestTr.querySelectorAll("td"),
                ).indexOf(closestTd);
                if (iTd >= 0 && iTr >= 0 && closestTable) {
                  const closestTHead = closestTable.querySelector("thead");
                  const dateTr = closestTHead?.querySelectorAll("tr")[iTr];
                  if (dateTr != undefined) {
                    const dateTd = dateTr.children[iTd];
                    tableObject.startingDate =
                      dateTd.textContent == undefined
                        ? undefined
                        : Number.parseInt(dateTd.textContent);
                  }
                }
              }
            }
          }
        } else {
          tableObject.startingDate =
            this._daysNumberCache.length > 0
              ? Number.parseInt(
                  this._daysNumberCache[this._daysNumberCache.length - 1]
                    .content || "",
                )
              : 1;
        }
        if (
          this._daysNumberCache.length < 7 &&
          tableObject.startingDate != undefined &&
          tableObject.startingDate > 7
        ) {
          if (tableObject.startingMonth === 0) {
            tableObject.startingYear--;
            tableObject.startingMonth = 11;
          } else {
            tableObject.startingMonth--;
          }
        } else if (
          this._daysNumberCache.length > 27 &&
          tableObject.startingDate != undefined &&
          tableObject.startingDate < 7
        ) {
          if (tableObject.startingMonth === 11) {
            tableObject.startingYear++;
            tableObject.startingMonth = 0;
          } else {
            tableObject.startingMonth++;
          }
        }
        const closestParent = tag.closest<HTMLElement>(
          `[class*="event"]:not([class="${tag.className}"])`,
        );
        const closestParentTextContent: string =
          closestParent != undefined && closestParent.textContent != undefined
            ? closestParent.textContent.trim()
            : "";
        const iOfName = this._enventsNameCache.indexOf(
          closestParentTextContent,
        );
        if (iOfName >= 0) {
          tableObject.numberEvent = iOfName;
        } else {
          this._enventsNameCache.push(closestParentTextContent);
          tableObject.numberEvent = this._enventsNameCache.length - 1;
        }
        if (closestParent != undefined) {
          tableObject.duration = Math.round(
            Number.parseInt(window.getComputedStyle(closestParent).width),
          );
        }
        tag.dataset.lisioMaybeACalendar = "true";
        this._calendarCaches.push(tableObject);
        this._eventsCache.push(tableObject);
        let parent: HTMLElement | undefined = tag.parentElement as
          | HTMLElement
          | undefined;
        let closest: HTMLElement | undefined = parent?.closest<HTMLElement>(
          "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
        ) as HTMLElement | undefined;
        while (closest != undefined) {
          closest.dataset.lisioMaybeACalendar = "true";
          parent = parent?.parentElement as HTMLElement | undefined;
          closest = parent?.closest<HTMLElement>(
            "td:not([data-lisio-maybe-a-calendar]), th:not([data-lisio-maybe-a-calendar]), tr:not([data-lisio-maybe-a-calendar]), tbody:not([data-lisio-maybe-a-calendar]), thead:not([data-lisio-maybe-a-calendar]), table:not([data-lisio-maybe-a-calendar])",
          ) as HTMLElement | undefined;
        }
      }
    } else if (
      this._calendarCaches.length > 0 &&
      maybePrevOrNext.maybeNext == undefined &&
      maybePrevOrNext.maybePrev == undefined &&
      !tableObject.isHidden
    ) {
      this._iDayRegs = 0;
      // tester si un élément est peut-être un calendrier
      if (this._calendarCaches.length < 30) {
        for (const calendarCache of this._calendarCaches) {
          calendarCache.maybeACalendar = false;
          const lastParent: HTMLElement | undefined = this._readerAdapter.all[
            calendarCache.position
          ].closest<HTMLElement>(
            `[data-lisio-position]:not([data-lisio-position="${calendarCache.position}"])`,
          ) as HTMLElement | undefined;
          this._readerAdapter.all[calendarCache.position].removeAttribute(
            "data-lisio-maybe-a-calendar",
          );
          this._readerAdapter.all[
            calendarCache.position
          ].dataset.lisioPosition = tableObject.position.toString();
          if (
            lastParent == undefined ||
            lastParent.dataset.lisioPosition == undefined
          ) {
            throw new Error("Last parent calendar is undefined");
          }
          tablesCache
            .get(parseInt(lastParent.dataset.lisioPosition))
            ?.childNodes?.push(calendarCache);
        }
      } else {
        let commonAncestor = this._readerAdapter.all[
          this._calendarCaches[0].position
        ].closest<HTMLElement>('[id*="calendar"]') as HTMLElement | undefined;
        if (commonAncestor == undefined) {
          const allAncestors = this.getAncestors(
            this._readerAdapter.all[this._calendarCaches[0].position],
          );
          for (let i = 1; i < allAncestors.length; i++) {
            if (this._calendarCaches[i] != undefined) {
              const ancestors = this.getAncestors(
                this._readerAdapter.all[this._calendarCaches[i].position],
              );
              const commonAncestors = allAncestors.filter((elt) =>
                ancestors.includes(elt),
              );
              if (commonAncestors.length > 0) {
                commonAncestor = commonAncestors[0] as HTMLElement | undefined;
              }
            }
          }
        }
        if (commonAncestor != undefined) {
          commonAncestor.dataset.lisioCalendar = "true";
        }

        if (this._prevButton == undefined) {
          const maybePrev =
            commonAncestor?.querySelector<HTMLElement>(
              this._selectorPrevButton,
            ) ||
            commonAncestor?.querySelector<HTMLElement>(
              this._selectorLeftButton,
            );
          if (maybePrev != undefined) {
            this._prevButton = maybePrev;
            if (this._prevButton.dataset.lisioClickable == undefined) {
              this._prevButton.dataset.lisioClickable =
                this._readerAdapter.dataLisioClickable.toString();
              this._readerAdapter.dataLisioClickable++;
            }
          }
        }
        if (this._nextButton == undefined) {
          const maybeNext =
            commonAncestor?.querySelector<HTMLElement>(
              this._selectorNextButton,
            ) ||
            commonAncestor?.querySelector<HTMLElement>(
              this._selectorRightButton,
            );
          if (maybeNext != undefined) {
            this._nextButton = maybeNext;
            if (this._nextButton.dataset.lisioClickable == undefined) {
              this._nextButton.dataset.lisioClickable =
                this._readerAdapter.dataLisioClickable.toString();
              this._readerAdapter.dataLisioClickable++;
            }
          }
        }

        const calendarObserver = new MutationObserver((records) => {
          const newClickables = new Map<number, number>();
          let nPrevButton: HTMLElement | undefined = undefined;
          let nNextButton: HTMLElement | undefined = undefined;
          const texts = [];
          for (const record of records) {
            for (const addedNode of record.addedNodes) {
              if (addedNode instanceof HTMLElement) {
                const textIterator = document.createNodeIterator(
                  addedNode,
                  NodeFilter.SHOW_TEXT,
                  {
                    acceptNode(node) {
                      return node.textContent?.trim() != ""
                        ? NodeFilter.FILTER_ACCEPT
                        : NodeFilter.FILTER_REJECT;
                    },
                  },
                );
                let textNode: Node | undefined = undefined;
                while (
                  (textNode = textIterator.nextNode() as Node | undefined)
                ) {
                  if (textNode.nodeType === Node.TEXT_NODE) {
                    const textContent = textNode.textContent;
                    if (textContent != undefined) {
                      const numberTextContent = Number.parseInt(textContent);
                      if (
                        textContent.length < 3 &&
                        !isNaN(numberTextContent) &&
                        numberTextContent === texts.length + 1 &&
                        /^\d+$/.test(textContent)
                      ) {
                        const element = textNode.parentNode as
                          | HTMLElement
                          | undefined;
                        if (element != undefined) {
                          if (
                            element.dataset.lisioClickable == undefined &&
                            !newClickables.has(numberTextContent)
                          ) {
                            element.dataset.lisioClickable =
                              this._readerAdapter.dataLisioClickable.toString();
                            newClickables.set(
                              numberTextContent,
                              this._readerAdapter.dataLisioClickable,
                            );
                            this._readerAdapter.dataLisioClickable++;
                          }
                        }
                      }
                    }
                  }
                }
                const textContent = addedNode.textContent;
                if (addedNode.nodeType === 1) {
                  const maybePrev =
                    addedNode.querySelector<HTMLElement>(
                      this._selectorPrevButton,
                    ) ||
                    addedNode.querySelector<HTMLElement>(
                      this._selectorLeftButton,
                    );
                  const maybeNext =
                    addedNode.querySelector<HTMLElement>(
                      this._selectorNextButton,
                    ) ||
                    addedNode.querySelector<HTMLElement>(
                      this._selectorRightButton,
                    );
                  if (nPrevButton == undefined) {
                    if (
                      maybePrev != undefined &&
                      (textContent === "" ||
                        (isNaN(Number.parseInt(textContent || "")) &&
                          maybePrev.querySelector<HTMLElement>(
                            this._selectorPrevButton,
                          ) == undefined))
                    ) {
                      nPrevButton = maybePrev;
                      if (
                        nPrevButton.dataset.lisioClickable == undefined &&
                        this._oldPrev != undefined
                      ) {
                        nPrevButton.dataset.lisioClickable =
                          this._oldPrev.toString();
                      }
                    }
                  }
                  if (nNextButton == undefined) {
                    if (
                      maybeNext != undefined &&
                      (textContent === "" ||
                        (isNaN(Number.parseInt(textContent || "")) &&
                          maybeNext.querySelector<HTMLElement>(
                            this._selectorNextButton,
                          ) == undefined))
                    ) {
                      nNextButton = maybeNext;
                      if (
                        nNextButton.dataset.lisioClickable == undefined &&
                        this._oldNext != undefined
                      ) {
                        nNextButton.dataset.lisioClickable =
                          this._oldNext.toString();
                      }
                    }
                  }
                }
              }
            }
          }
          if (commonAncestor?.dataset.iCalendar != undefined) {
            this._readerAdapter.broadcastChannel.sendRefreshCalendarMessage({
              iCalendar: Number.parseInt(commonAncestor.dataset.iCalendar),
              clickables: newClickables,
            });
          }
        });
        if (commonAncestor != undefined) {
          calendarObserver.observe(commonAncestor, {
            childList: true,
            subtree: true,
          });
          commonAncestor.dataset.iCalendar = this._iCal.toString();
        }

        this._oldPrev =
          this._prevButton?.dataset.lisioClickable == undefined
            ? undefined
            : Number.parseInt(this._prevButton?.dataset.lisioClickable);
        this._oldNext =
          this._nextButton?.dataset.lisioClickable == undefined
            ? undefined
            : Number.parseInt(this._nextButton?.dataset.lisioClickable);
      }
      this._iCal++;
      this._calendarCaches.splice(0, this._calendarCaches.length);
      this._monthsCache.splice(0, this._monthsCache.length);
      this._yearsCache.splice(0, this._yearsCache.length);
      this._daysCache.splice(0, this._daysCache.length);
      this._daysNumberCache.splice(0, this._daysNumberCache.length);
      this._prevButton = undefined;
      this._nextButton = undefined;
    }
  }

  private createNonCapturingGroup(char: string, extra?: boolean): string {
    return `(?:${char})?${extra ? "(?:\\.)?" : ""}`;
  }

  private findPrevOrNextButton(
    maybePrevAndNext: MaybePrevAndNext,
    tag: HTMLElement | SVGElement,
    textContent: string,
  ): void {
    if (this._prevButton == undefined) {
      maybePrevAndNext.maybePrev = (tag.closest<HTMLElement>(
        this._selectorPrevButton,
      ) || tag.closest<HTMLElement>(this._selectorLeftButton)) as
        | HTMLElement
        | undefined;
      if (
        maybePrevAndNext.maybePrev != undefined &&
        (textContent === "" ||
          (isNaN(Number.parseInt(textContent)) &&
            tag.querySelector(this._selectorPrevButton) == undefined))
      ) {
        this._prevButton = maybePrevAndNext.maybePrev;
        if (
          this._prevButton != undefined &&
          this._prevButton.dataset.lisioClickable == undefined
        ) {
          this._prevButton.dataset.lisioClickable =
            this._readerAdapter.dataLisioClickable.toString();
          this._readerAdapter.dataLisioClickable++;
        }
      }
    } else if (this._nextButton == undefined) {
      maybePrevAndNext.maybeNext = (tag.closest<HTMLElement>(
        this._selectorNextButton,
      ) || tag.closest<HTMLElement>(this._selectorRightButton)) as
        | HTMLElement
        | undefined;
      if (
        maybePrevAndNext.maybeNext != undefined &&
        (textContent === "" ||
          (isNaN(Number.parseInt(textContent)) &&
            tag.querySelector(this._selectorNextButton) == undefined))
      ) {
        this._nextButton = maybePrevAndNext.maybeNext;
        if (
          this._nextButton != undefined &&
          this._nextButton.dataset.lisioClickable == undefined
        ) {
          this._nextButton.dataset.lisioClickable =
            this._readerAdapter.dataLisioClickable.toString();
          this._readerAdapter.dataLisioClickable++;
        }
      }
    }
  }

  private getAncestors(
    elt: HTMLElement | SVGElement | undefined,
  ): (HTMLElement | SVGElement)[] {
    const ancestors: (HTMLElement | SVGElement)[] = [];
    let element = elt;
    while (element != undefined) {
      ancestors.push(element);
      element = element.parentElement as HTMLElement | SVGElement | undefined;
    }
    return ancestors;
  }
}

export default ExploreCalendarHelper;
