import {
  LisioNumericParameterNames,
  LisioStringParameterNames,
} from "@lisio/lisio-profils";

import helperStyles from "../assets/css/shadowroot/lisio-synth-help-info.css?raw";
import { LisioMutationObserverController } from "../controllers/lisio-mutation-observer-controller";
import { LisioParamsController } from "../controllers/lisio-params-controller";
import LisioShadowRootController from "../controllers/lisio-shadow-root-controller";
import LisioStyleSheetController from "../controllers/lisio-stylesheet-controller";
import LisioTranslationController from "../controllers/lisio-translation-controller";
import LisioCSSProperties from "../enums/lisio-css-properties";
import {
  decodeHTMLEntity,
  escapeRegexSpecialChars,
  isInViewport,
  isTouchDeviceLisio,
} from "../utils";
import LisioTextTreeWalker from "../walkers/lisio-text-tree-walker";
import LisioAdapter from "./lisio-adapter";

class LisioSpeechSynthesisAdapter extends LisioAdapter<boolean> {
  private _autoEnabled = true;
  private _autoTimeout = -1;
  private _currentIndexSynthVocal = -1;
  private _cursorSize = "default";
  private _iText = 0;
  private _isFromKeyboard = false;
  private _lastIText = 0;
  private _lastTarget?: HTMLElement;
  private _lisioExplainClick?: HTMLElement;
  private _mutationObserver: LisioMutationObserverController;
  private _offsetTexts: number[] = [];
  private _params: LisioParamsController;
  private _pauseButton = `<path d="M0 11.7733C0 5.27106 6.00341 0 13.4091 0H43.591C50.9965 0 57 5.27106 57 11.7733V12.2268C57 18.7289 50.9965 24 43.591 24H13.4091C6.00341 24 0 18.7289 0 12.2268V11.7733Z" fill="#AD1A00"></path><path d="M50 12C50 16.9706 45.9706 21 41 21C36.0294 21 32 16.9706 32 12C32 7.02946 36.0294 3 41 3C45.9706 3 50 7.02946 50 12Z" fill="white"></path><path d="M19.37 13.6039C18.4421 14.2567 17.3285 14.6079 16.1862 14.6079C15.0439 14.6079 13.9303 14.2567 13.0024 13.6039C11.9242 13.6921 10.9177 14.1694 10.1782 14.9431C9.43863 15.717 9.01883 16.7322 9 17.7921V19.4852C9 20.5545 9.54579 21 10.5464 21H21.826C22.7356 21 23.3723 20.5545 23.3723 19.4852V17.7921C23.3723 15.6535 21.644 13.8713 19.37 13.6039ZM16.1862 5.31682C15.6367 5.31682 15.0926 5.42293 14.585 5.62887C14.0772 5.83482 13.6159 6.13674 13.2274 6.51738C12.4427 7.28614 12.0019 8.32868 12.0019 9.41583C12.0136 9.95421 12.1336 10.485 12.3549 10.9779C12.5761 11.4707 12.8944 11.9161 13.2915 12.2884C13.6885 12.6608 14.1565 12.953 14.6688 13.1481C15.1811 13.3432 15.7277 13.4376 16.2772 13.4257C17.3555 13.403 18.3832 12.9732 19.146 12.2262C19.9086 11.479 20.3473 10.4722 20.3705 9.41583C20.3705 8.32868 19.9297 7.28614 19.145 6.51738C18.3602 5.74869 17.296 5.31682 16.1862 5.31682ZM25.5555 14.9406C27.1663 13.3543 28.0707 11.2078 28.0707 8.97033C28.0707 6.73277 27.1663 4.58633 25.5555 3L24.1911 4.33665C24.8182 4.94186 25.3161 5.6633 25.6559 6.45865C25.9958 7.25421 26.1708 8.10795 26.1708 8.97033C26.1708 9.83265 25.9958 10.6864 25.6559 11.4819C25.3161 12.2774 24.8182 12.9987 24.1911 13.6039L25.5555 14.9406Z" fill="white"></path><path d="M23.589 12.8049C24.0442 12.3265 24.4053 11.7578 24.6518 11.1314C24.8983 10.505 25.0252 9.83327 25.0252 9.15492C25.0252 8.4765 24.8983 7.80479 24.6518 7.17838C24.4053 6.55197 24.0442 5.98326 23.589 5.50488L22.3494 6.81518C22.6438 7.12014 22.8778 7.48416 23.0376 7.88595C23.1974 8.28768 23.2795 8.71904 23.2795 9.15492C23.2795 9.59072 23.1974 10.0221 23.0376 10.4239C22.8778 10.8256 22.6438 11.1897 22.3494 11.4947L23.589 12.8049Z" fill="white"></path><path d="M38.2 6H37.8C36.8059 6 36 6.71108 36 7.58824V16.4118C36 17.2889 36.8059 18 37.8 18H38.2C39.1941 18 40 17.2889 40 16.4118V7.58824C40 6.71108 39.1941 6 38.2 6Z" fill="#AD1A00"></path><path d="M44.2 6H43.8C42.8059 6 42 6.71108 42 7.58824V16.4118C42 17.2889 42.8059 18 43.8 18H44.2C45.1941 18 46 17.2889 46 16.4118V7.58824C46 6.71108 45.1941 6 44.2 6Z" fill="#AD1A00"></path>`;
  private _playButton = `<path d="M0 11.7733C0 5.27106 6.00341 0 13.4091 0H43.591C50.9965 0 57 5.27106 57 11.7733V12.2268C57 18.7289 50.9965 24 43.591 24H13.4091C6.00341 24 0 18.7289 0 12.2268V11.7733Z" fill="#005282"></path><path d="M50 12C50 16.9706 45.9706 21 41 21C36.0294 21 32 16.9706 32 12C32 7.02946 36.0294 3 41 3C45.9706 3 50 7.02946 50 12Z" fill="white"></path><path d="M19.37 13.6039C18.4421 14.2567 17.3285 14.6079 16.1862 14.6079C15.0439 14.6079 13.9303 14.2567 13.0024 13.6039C11.9242 13.6921 10.9177 14.1694 10.1782 14.9431C9.43863 15.717 9.01883 16.7322 9 17.7921V19.4852C9 20.5545 9.54579 21 10.5464 21H21.826C22.7356 21 23.3723 20.5545 23.3723 19.4852V17.7921C23.3723 15.6535 21.644 13.8713 19.37 13.6039ZM16.1862 5.31682C15.6367 5.31682 15.0926 5.42293 14.585 5.62887C14.0772 5.83482 13.6159 6.13674 13.2274 6.51738C12.4427 7.28614 12.0019 8.32868 12.0019 9.41583C12.0136 9.95421 12.1336 10.485 12.3549 10.9779C12.5761 11.4707 12.8944 11.9161 13.2915 12.2884C13.6885 12.6608 14.1565 12.953 14.6688 13.1481C15.1811 13.3432 15.7277 13.4376 16.2772 13.4257C17.3555 13.403 18.3832 12.9732 19.146 12.2262C19.9086 11.479 20.3473 10.4722 20.3705 9.41583C20.3705 8.32868 19.9297 7.28614 19.145 6.51738C18.3602 5.74869 17.296 5.31682 16.1862 5.31682ZM25.5555 14.9406C27.1663 13.3543 28.0707 11.2078 28.0707 8.97033C28.0707 6.73277 27.1663 4.58633 25.5555 3L24.1911 4.33665C24.8182 4.94186 25.3161 5.6633 25.6559 6.45865C25.9958 7.25421 26.1708 8.10795 26.1708 8.97033C26.1708 9.83265 25.9958 10.6864 25.6559 11.4819C25.3161 12.2774 24.8182 12.9987 24.1911 13.6039L25.5555 14.9406Z" fill="white"></path><path d="M23.589 12.8049C24.0442 12.3265 24.4053 11.7578 24.6518 11.1314C24.8983 10.505 25.0252 9.83327 25.0252 9.15492C25.0252 8.4765 24.8983 7.80479 24.6518 7.17838C24.4053 6.55197 24.0442 5.98326 23.589 5.50488L22.3494 6.81518C22.6438 7.12014 22.8778 7.48416 23.0376 7.88595C23.1974 8.28768 23.2795 8.71904 23.2795 9.15492C23.2795 9.59072 23.1974 10.0221 23.0376 10.4239C22.8778 10.8256 22.6438 11.1897 22.3494 11.4947L23.589 12.8049Z" fill="white"></path><path d="M46.7105 11.4247C47.0965 11.6888 47.0965 12.3112 46.7105 12.5753L38.9298 17.8982C38.5236 18.1761 38 17.8522 38 17.323V6.67705C38 6.14785 38.5236 5.82389 38.9298 6.10176L46.7105 11.4247Z" fill="#005282"></path>`;
  private _playPauseInterval = -1;
  private _previousReadenWord: { word: string; wordIndex: number } = {
    word: "",
    wordIndex: -1,
  };
  private _readenText?: HTMLElement;
  private _stateSynth = false;
  private _synthVocaleLeftClickMessageTimeOut = -1;
  private _target?: HTMLElement;
  private _utterThis?: SpeechSynthesisUtterance;
  private _vocalSynthesisMode = 0;
  private _walker: LisioTextTreeWalker;
  private clickRightSynthEventListenerBind =
    this.clickRightSynthEventListener.bind(this);
  private clickSynthEventListenerBind = this.clickSynthEventListener.bind(this);
  private clickSynthPauseEventListenerBind =
    this.clickSynthPauseEventListener.bind(this);

  constructor(
    walker: LisioTextTreeWalker,
    mutationObserver: LisioMutationObserverController,
    params: LisioParamsController,
    vocalSynthesisMode: number,
  ) {
    super();
    this._walker = walker;
    this._mutationObserver = mutationObserver;
    this._params = params;
    this._cursorSize =
      (this._params.params.get(LisioStringParameterNames.CURSOR_SIZE) as
        | string
        | undefined) || "default";
    this._vocalSynthesisMode = vocalSynthesisMode;

    window.addEventListener("lisio-lang-change", () => {
      LisioTranslationController.current.translateSpeechSynthesisButtons();
    });
  }

  public set iText(value: number) {
    this._iText = value;
  }

  public get lastIText() {
    return this._lastIText;
  }

  public adapt(walker: LisioTextTreeWalker, value: boolean): void {
    if (value) {
      const playMap = new Map<LisioCSSProperties, string>();
      const playBigMap = new Map<LisioCSSProperties, string>();
      const playVeryBigMap = new Map<LisioCSSProperties, string>();

      const pauseMap = new Map<LisioCSSProperties, string>();
      const pauseBigMap = new Map<LisioCSSProperties, string>();
      const pauseVeryBigMap = new Map<LisioCSSProperties, string>();

      const vocalMap = new Map<LisioCSSProperties, string>();
      const vocalBigMap = new Map<LisioCSSProperties, string>();
      const vocalVeryBigMap = new Map<LisioCSSProperties, string>();

      if (__BUILD_TARGET__ === "extension") {
        playMap.set(
          LisioCSSProperties.CURSOR,
          `url(${chrome.runtime.getURL("/cursors/play/play.svg")}), default`,
        );
        playBigMap.set(
          LisioCSSProperties.CURSOR,
          `url(${chrome.runtime.getURL("/cursors/play/play-big.svg")}), default`,
        );
        playVeryBigMap.set(
          LisioCSSProperties.CURSOR,
          `url(${chrome.runtime.getURL("/cursors/play/play-verybig.svg")}), default`,
        );

        pauseMap.set(
          LisioCSSProperties.CURSOR,
          `url(${chrome.runtime.getURL("/cursors/pause/pause.svg")}), default`,
        );
        pauseBigMap.set(
          LisioCSSProperties.CURSOR,
          `url(${chrome.runtime.getURL("/cursors/pause/pause-big.svg")}), default`,
        );
        pauseVeryBigMap.set(
          LisioCSSProperties.CURSOR,
          `url(${chrome.runtime.getURL("/cursors/pause/pause-verybig.svg")}), default`,
        );

        vocalMap.set(
          LisioCSSProperties.CURSOR,
          `url(${chrome.runtime.getURL("/cursors/vocal/vocal.svg")}), default`,
        );
        vocalBigMap.set(
          LisioCSSProperties.CURSOR,
          `url(${chrome.runtime.getURL("/cursors/vocal/vocal-big.svg")}), default`,
        );
        vocalVeryBigMap.set(
          LisioCSSProperties.CURSOR,
          `url(${chrome.runtime.getURL("/cursors/vocal/vocal-verybig.svg")}), default`,
        );
      } else {
        playMap.set(
          LisioCSSProperties.CURSOR,
          `url("${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/cursors/play/play.svg"), default`,
        );
        playBigMap.set(
          LisioCSSProperties.CURSOR,
          `url("${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/cursors/play/play-big.svg"), default`,
        );
        playVeryBigMap.set(
          LisioCSSProperties.CURSOR,
          `url("${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/cursors/play/play-verybig.svg"), default`,
        );

        pauseMap.set(
          LisioCSSProperties.CURSOR,
          `url("${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/cursors/pause/pause.svg"), default`,
        );
        pauseBigMap.set(
          LisioCSSProperties.CURSOR,
          `url("${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/cursors/pause/pause-big.svg"), default`,
        );
        pauseVeryBigMap.set(
          LisioCSSProperties.CURSOR,
          `url("${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/cursors/pause/pause-verybig.svg"), default`,
        );

        vocalMap.set(
          LisioCSSProperties.CURSOR,
          `url("${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/cursors/vocal/vocal.svg"), default`,
        );
        vocalBigMap.set(
          LisioCSSProperties.CURSOR,
          `url("${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/cursors/vocal/vocal-big.svg"), default`,
        );
        vocalVeryBigMap.set(
          LisioCSSProperties.CURSOR,
          `url("${import.meta.env.VITE_LISIO_DOMAIN}/solution/dist-site/cursors/vocal/vocal-verybig.svg"), default`,
        );
      }
      LisioStyleSheetController.current.insertRule(
        ".lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover,.lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover *,.lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover::placeholder",
        playMap,
      );
      LisioStyleSheetController.current.insertRule(
        ".lisio-vocal-synthesis-active.synth-big:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover,.lisio-vocal-synthesis-active.synth-big:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover *,.lisio-vocal-synthesis-active.synth-big:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover::placeholder",
        playBigMap,
      );
      LisioStyleSheetController.current.insertRule(
        ".lisio-vocal-synthesis-active.synth-very-big:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover,.lisio-vocal-synthesis-active.synth-very-big:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover *,.lisio-vocal-synthesis-active.synth-very-big:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover::placeholder",
        playVeryBigMap,
      );

      LisioStyleSheetController.current.insertRule(
        ".lisio-vocal-synthesis-active.lisio-vocal-synthesis-reading.lisio-default *",
        pauseMap,
      );
      LisioStyleSheetController.current.insertRule(
        ".lisio-vocal-synthesis-active.lisio-vocal-synthesis-reading.lisio-default.synth-big *",
        pauseBigMap,
      );
      LisioStyleSheetController.current.insertRule(
        ".lisio-vocal-synthesis-active.lisio-vocal-synthesis-reading.lisio-default.synth-verybig * ",
        pauseVeryBigMap,
      );

      LisioStyleSheetController.current.insertRule(
        ".lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading):not(.lisio-vocal-synthesis-mode-1) *",
        vocalMap,
      );
      LisioStyleSheetController.current.insertRule(
        ".lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading):not(.lisio-vocal-synthesis-mode-1).synth-big *",
        vocalBigMap,
      );
      LisioStyleSheetController.current.insertRule(
        ".lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading):not(.lisio-vocal-synthesis-mode-1).synth-verybig *",
        vocalVeryBigMap,
      );

      LisioStyleSheetController.current.insertRawRule(
        "start-synth",
        `.start-synth{border: none;background-color: transparent;padding: 0;}`,
      );
      LisioStyleSheetController.current.insertRawRule(
        "start-synth svg",
        `.start-synth svg{display: block;}`,
      );
      LisioStyleSheetController.current.insertRawRule(
        "start-synth-first",
        `.start-synth-first{position: absolute; z-index:2147483647}`,
      );
      LisioStyleSheetController.current.insertRawRule(
        "lisio-vocal-synthesis-active:not",
        `.lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover,.lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover *,.lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading) *[data-lisio-index]:hover::placeholder{background-color: rgb(197, 241, 243)!important;border-radius: 5px;color: black !important;-webkit-text-fill-color: black !important;}`,
      );
      LisioStyleSheetController.current.insertRawRule(
        "lisio-vocal-synthesis-active",
        `.lisio-vocal-synthesis-active.lisio-vocal-synthesis-reading .lisio-speech-active, .lisio-vocal-synthesis-active.lisio-vocal-synthesis-reading .lisio-speech-active::placeholder{box-shadow: 0px 0px 0px 2px #4ab7ba;background-color: white !important;color: black !important;-webkit-text-fill-color: black !important;border-radius: 5px;}`,
      );
      LisioStyleSheetController.current.insertRawRule(
        "mark",
        `mark[data-mark="lisio"],mark[data-mark="lisio"] *{background-color: rgb(197, 241, 243)!important;border-radius: 3px;color: black !important;-webkit-text-fill-color: black !important;padding: 0 !important; font: inherit !important}`,
      );

      if (this._offsetTexts.length === 0) {
        this.initialize(walker);
      }
      //si activé
      this._cursorSize =
        (this._params.params.get(LisioStringParameterNames.CURSOR_SIZE) as
          | string
          | undefined) || "default";
      // const res = await this.populateVoiceList();

      if (!isTouchDeviceLisio() /*&& this._vocalSynthesisMode !== 1*/) {
        // Ajouter l'événement `click` au document
        for (const element of document.querySelectorAll<HTMLElement>(
          "[data-lisio-index]",
        )) {
          element.addEventListener("click", this.clickSynthEventListenerBind);
        }
      }
      if (!isTouchDeviceLisio() /*&& this._vocalSynthesisMode !== 1*/) {
        document.documentElement.addEventListener(
          "click",
          this.clickSynthPauseEventListenerBind,
        );
      }
      if (!isTouchDeviceLisio() /*&& this._vocalSynthesisMode !== 1*/) {
        document.addEventListener("keydown", (e) => {
          this._isFromKeyboard =
            e.key === "ContextMenu" || (e.shiftKey && e.key === "F10");
        });

        // Ajouter l'événement `contextmenu` au document
        document.addEventListener(
          "contextmenu",
          this.clickRightSynthEventListenerBind,
        );
      }
      if (!this._utterThis) {
        this._utterThis = new SpeechSynthesisUtterance(""); //voir doc SpeechSynthesisUtterance

        this._utterThis.addEventListener("end", () => {
          if (!this._autoEnabled) {
            this._autoTimeout = setTimeout(this.next.bind(this), 100);
          }
        });
      }

      this._stateSynth = true;
      document.documentElement.classList.add("lisio-vocal-synthesis-active");
      if (this._cursorSize === "big") {
        document.documentElement.classList.add("synth-big");
      } else if (this._cursorSize === "very_big") {
        document.documentElement.classList.add("synth-verybig");
      }
      for (const button of document.querySelectorAll<HTMLButtonElement>(
        "button.start-synth",
      )) {
        button.style.width = "57px";
        button.style.height = "24px";
        button.classList.remove("lisio-hidden");
        const altAria =
          LisioTranslationController.current.getTranslation("pauseSynthButton");
        if (
          window.speechSynthesis?.speaking /*&&
          (button.children[0] as HTMLImageElement | undefined)?.src !=
            this._lisioConfig.urls.pause*/
        ) {
          if (button.children[0] as HTMLImageElement) {
            (button.children[0] as SVGElement).innerHTML = this._pauseButton;
          }
          button.setAttribute("aria-label", altAria);
        }
      }
    } else if (!value) {
      LisioStyleSheetController.current.removeRule(
        ".lisio-vocal-synthesis-active *[data-lisio-index], .lisio-vocal-synthesis-active *[data-lisio-index] *",
      );
      LisioStyleSheetController.current.removeRule(
        ".lisio-vocal-synthesis-active.synth-big *[data-lisio-index], .lisio-vocal-synthesis-active.synth-big *[data-lisio-index] *",
      );
      LisioStyleSheetController.current.removeRule(
        ".lisio-vocal-synthesis-active.synth-verybig *[data-lisio-index], .lisio-vocal-synthesis-active.synth-verybig *[data-lisio-index] * ",
      );

      LisioStyleSheetController.current.removeRule(
        ".lisio-vocal-synthesis-active.lisio-vocal-synthesis-reading.lisio-default *",
      );
      LisioStyleSheetController.current.removeRule(
        ".lisio-vocal-synthesis-active.lisio-vocal-synthesis-reading.lisio-default.synth-big *",
      );
      LisioStyleSheetController.current.removeRule(
        ".lisio-vocal-synthesis-active.lisio-vocal-synthesis-reading.lisio-default.synth-verybig * ",
      );

      LisioStyleSheetController.current.removeRule(
        ".lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading):not(.lisio-vocal-synthesis-mode-1) *",
      );
      LisioStyleSheetController.current.removeRule(
        ".lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading):not(.lisio-vocal-synthesis-mode-1).synth-big *",
      );
      LisioStyleSheetController.current.removeRule(
        ".lisio-vocal-synthesis-active:not(.lisio-vocal-synthesis-reading):not(.lisio-vocal-synthesis-mode-1).synth-verybig *",
      );

      LisioStyleSheetController.current.removeRule("start-synth");
      LisioStyleSheetController.current.removeRule("start-synth svg");
      LisioStyleSheetController.current.removeRule(
        "lisio-vocal-synthesis-active:not",
      );
      LisioStyleSheetController.current.removeRule(
        "lisio-vocal-synthesis-active",
      );
      LisioStyleSheetController.current.removeRule("mark");

      this._iText = 0;
      document.removeEventListener("click", this.clickSynthEventListenerBind);
      document.removeEventListener(
        "contextmenu",
        this.clickRightSynthEventListenerBind,
      );
      document.documentElement.classList.remove("lisio-vocal-synthesis-active");
      document.body.classList.remove("lisio-cursor-vocal");
      document.documentElement.classList.remove("synth-verybig");
      document.documentElement.classList.remove("synth-big");
      for (const elt of document.querySelectorAll<HTMLElement>(
        ".lisio-cursor-play",
      )) {
        elt.classList.remove("lisio-cursor-play");
      }

      window.speechSynthesis?.cancel();
      this.removeMarker();
      this._autoEnabled = true;
      this._stateSynth = false;
      if (this._autoTimeout != undefined) {
        clearTimeout(this._autoTimeout);
      }
      this._lastTarget = document.querySelector(".lisio-speech-active") as
        | HTMLElement
        | undefined;
      if (this._lastTarget != null) {
        this._lastTarget.classList.remove("lisio-speech-active"); //on enlève le cadre pour le dernier texte lu
      }
      const altAria =
        LisioTranslationController.current.getTranslation("startSynthButton");
      for (const synthButton of document.querySelectorAll(".start-synth")) {
        synthButton.classList.add("lisio-hidden");
        if (synthButton.classList.contains("start-synth-first")) {
          synthButton.setAttribute(
            "aria-label",
            LisioTranslationController.current.getTranslation(
              "startSynthFirstButton",
            ),
          );
        } else {
          synthButton.setAttribute("aria-label", altAria);
        }
        if (synthButton.children[0] != undefined) {
          (synthButton.children[0] as SVGElement).innerHTML = this._playButton;
        }
      }
      this._utterThis = undefined;
      if (!isTouchDeviceLisio() /*&& this._vocalSynthesisMode !== 1*/) {
        document.documentElement.removeEventListener(
          "click",
          this.clickSynthPauseEventListenerBind,
        );
      }
    }
  }

  public initialize(walker: LisioTextTreeWalker) {
    const hsX: HTMLElement[][] = [[], [], [], [], [], []];
    const txtsX: HTMLElement[][] = [[], [], []];
    let max = 20;
    for (let i = walker.tags.size - 1; i > -1; i--) {
      //on parcourt le tableau des balises à l'envers
      const tag = walker.tags.get(i);
      if (tag != undefined && tag.textContent) {
        for (const child of tag.querySelectorAll("[data-lisio-index]")) {
          delete (child as HTMLElement).dataset.lisioIndex;
        }

        if (isInViewport(tag)) {
          if (/h[123456]/g.test(tag.localName)) {
            hsX[parseInt(tag.tagName.charAt(1)) - 1].push(tag);
          } else if (tag.textContent.length > 79 && !txtsX[0].includes(tag)) {
            txtsX[0].push(tag);
          } else if (tag.textContent.length > 39 && !txtsX[1].includes(tag)) {
            txtsX[1].push(tag);
          } else if (tag.textContent.length > 19 && !txtsX[2].includes(tag)) {
            txtsX[2].push(tag);
          }
        }
      }
      if (
        tag?.nodeType === Node.ELEMENT_NODE &&
        tag.tagName === "INPUT" &&
        !txtsX[0].includes(tag)
      ) {
        txtsX[0].push(tag);
      }
    }

    //que si mode n'est pas 1
    if (this._vocalSynthesisMode === 0) {
      if (isTouchDeviceLisio()) {
        let i = 0;
        hsX.forEach((hs) => {
          while (max > 0 && i < hs.length) {
            if (hs[i].previousElementSibling == null) {
              this.makeSynthButton(hs[i]);
            } else {
              if (hs[i].previousElementSibling?.localName != "button") {
                this.makeSynthButton(hs[i]);
              }
            }
            max--;
            i++;
          }
          i = 0;
        });
        txtsX.forEach((txts) => {
          while (max > 0 && i < txts.length) {
            if (
              txts[i].previousElementSibling == null &&
              txts[i].dataset.lisioIndex &&
              isInViewport(txts[i])
            ) {
              this.makeSynthButton(txts[i]);
            } else {
              if (
                txts[i].previousElementSibling &&
                txts[i].previousElementSibling?.localName != "button" &&
                txts[i].dataset.lisioIndex &&
                isInViewport(txts[i])
              ) {
                this.makeSynthButton(txts[i]);
              }
            }
            max--;
            i++;
          }
          i = 0;
        });
      }
      if (
        !document.body?.firstElementChild?.classList.contains("start-synth") &&
        !document.body?.firstElementChild?.classList.contains("skiptranslate")
      ) {
        const button = document.createElement("button");
        const svg = document.createElementNS(
          "http://www.w3.org/2000/svg",
          "svg",
          {},
        );
        svg.setAttribute("width", "57");
        svg.setAttribute("height", "24");
        svg.setAttribute("viewBox", "0 0 57 24");
        svg.setAttribute("fill", "none");
        svg.innerHTML = this._playButton;
        const altAria = LisioTranslationController.current.getTranslation(
          "startSynthFirstButton",
        );
        button.setAttribute("aria-label", altAria);
        button.className =
          "start-synth start-synth-first lisio-hidden notranslate";
        button.appendChild(svg);
        button.addEventListener("click", () => {
          const htmlVocalReading = document.querySelector(
            ".lisio-vocal-synthesis-reading",
          );
          if (htmlVocalReading) {
            htmlVocalReading.classList.remove("lisio-vocal-synthesis-reading");
          } else {
            document.documentElement.classList.add(
              "lisio-vocal-synthesis-reading",
            );
            document.documentElement.classList.add(`lisio-${this._cursorSize}`);
          }
          this._iText = 0;
          this.auto();
        });
        document.body.prepend(button);
      }
    } else {
      document
        .querySelectorAll<HTMLElement>("button.start-synth")
        .forEach((button) => {
          this.makeSynthButton(button, false);
        });
    }

    const activeSpeechElement = document.querySelector<HTMLElement>(
      ".lisio-speech-active",
    );
    this._iText =
      activeSpeechElement != undefined &&
      activeSpeechElement.dataset.lisioIndex != undefined
        ? Number.parseInt(activeSpeechElement.dataset.lisioIndex)
        : 0;
    this._offsetTexts = Array.from(
      document.querySelectorAll<HTMLElement>("[data-lisio-index]"),
    )
      .filter((elt) => elt.dataset.lisioIndex != undefined)
      .map((elt) =>
        Number.parseInt(elt.dataset.lisioIndex || "-1"),
      ) as number[];
  }

  /**
   * lance la lecture d'un texte
   * @param {string} speech
   */
  public synthVocal(speech: HTMLElement) {
    if (this._utterThis != undefined) {
      this._readenText = speech;
      this._lastIText = this._iText;

      const rate = this._params.params.get(
        LisioNumericParameterNames.SPEECH_SYNTHESIS_RATE,
      ) as number | undefined;
      const pitch = this._params.params.get(
        LisioNumericParameterNames.SPEECH_SYNTHESIS_PITCH,
      ) as number | undefined;
      const deeplLang = this._params.params.get(
        LisioStringParameterNames.DEEPL_TRANSLATION,
      ) as string | undefined;
      const googleLang = this._params.params.get(
        LisioStringParameterNames.GOOGLE_TRANSLATION,
      ) as string | undefined;
      const deeplOrGoogleLang = deeplLang || googleLang;
      this._utterThis.text = speech.textContent
        ? speech.textContent
        : speech.getAttribute("placeholder") || "";
      this._utterThis.rate = rate || 1; //règle le rate de la voix
      this._utterThis.pitch = pitch || 1; //règle le pitch de la voix

      const lang = this._readenText.lang;

      if (lang != undefined && lang != "") {
        this._utterThis.lang = lang;
      } else {
        this._utterThis.lang =
          deeplOrGoogleLang == undefined || deeplOrGoogleLang == "default"
            ? window.location.origin == "https://alpine-region.eu"
              ? "en"
              : document.documentElement.lang
            : deeplOrGoogleLang;
      }

      this._utterThis.voice = null;
      const voices = speechSynthesis.getVoices();

      const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
      if (isSafari) {
        if (this._utterThis?.lang.toLowerCase().startsWith("fr")) {
          this._utterThis.voice = voices.find(v => v.name === "Thomas") || null;
        } else if (this._utterThis?.lang.toLowerCase().startsWith("en")) {
          this._utterThis.voice = voices.find(v => v.name === "Samantha") || null;
        }
        else{
          const matching = voices.find(v => v.lang.toLowerCase().startsWith(this._utterThis?.lang.toLowerCase() ?? "fr"));
          if (matching) {
            this._utterThis.voice = matching;
          }
        }
      }

      if(this._utterThis.voice === null){
        const matching = voices.find(v => v.lang.toLowerCase().startsWith(this._utterThis?.lang.toLowerCase() ?? "fr"));
        if (matching) {
          this._utterThis.voice = matching;
        }
      }  

      this._utterThis.volume = 1;

      // Obtenir le contenu HTML original
      // let originalHTML = this._readenText.innerHTML;
      // originalHTML = originalHTML.replace(/&amp;/gm, "&"); // a voir si y a mieux
      this._currentIndexSynthVocal = 0;

      // Gestionnaire onboundary
      this._utterThis.onboundary = (event: SpeechSynthesisEvent) => {
        const googleTrans = this._params.params.get(
          LisioStringParameterNames.GOOGLE_TRANSLATION,
        ) as string | undefined;
        // const deeplTrans = this._params.params.get(
        //   LisioStringParameterNames.DEEPL_TRANSLATION,
        // ) as string | undefined;
        if (
          googleTrans == undefined ||
          googleTrans === "default" /*&&
          (deeplTrans == undefined || deeplTrans === "default")*/
        ) {
          this._mutationObserver.disconnectObserver();

          if (event.name === "sentence") return;

          // Obtenir le mot lu
          const word = event.utterance.text
            .substring(event.charIndex, event.charIndex + event.charLength)
            .trim();

          // Surligner le mot lu
          if (this._readenText) {
            this.highlightWord(word, event.charIndex, this._readenText);
          }
        }
      };

      // END HERE

      this._utterThis.onend = () => {
        if (!this._autoEnabled) {
          this._autoTimeout = setTimeout(this.next.bind(this), 200);
        }

        clearInterval(this._playPauseInterval);
        this.removeMarker();
        if (this._target) {
          this._target.classList.remove("lisio-speech-active");
        }
      };

      this._target = this._readenText;
      if (getComputedStyle(this._readenText).display == "flex") {
        document
          .querySelector(
            `[data-lisio-index='${this._readenText.dataset.lisioIndex}']`,
          )
          ?.childNodes.forEach((child) => {
            if (child.nodeType === 3) {
              const span = document.createElement("span");
              span.setAttribute("data-mark", "lisio");
              span.textContent = child.textContent;
              child.parentNode?.insertBefore(span, child);
              child.remove();
            }
          });
      }

      window.speechSynthesis?.speak(this._utterThis);
      const userAgent = navigator.userAgent.toLowerCase();
      const isChrome =
        userAgent.includes("chrome") &&
        !userAgent.includes("edge") &&
        !userAgent.includes("opr");
      if (isChrome) {
        this._playPauseInterval = setInterval(() => {
          if (!window.speechSynthesis?.speaking) {
            clearInterval(this._playPauseInterval);
          } else {
            window.speechSynthesis?.pause();
            window.speechSynthesis?.resume();
          }
        }, 14000);
      }
    }
  }

  // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
  protected adaptFunction(_element: HTMLElement, _value: boolean): void {}

  /**
   * Permet d'activer/désactiver la lecture auto
   */
  private auto() {
    this._mutationObserver.disconnectObserver();
    if (this._autoEnabled) {
      // document.removeEventListener("selectionchange", this.selectionChangeVoc);
      this._autoEnabled = false;
      this.next();
      const altAria =
        LisioTranslationController.current.getTranslation("pauseSynthButton");
      document
        .querySelectorAll<HTMLElement>(".start-synth")
        .forEach((button) => {
          if (button.children[0]) {
            (button.children[0] as SVGElement).innerHTML = this._pauseButton;
          }
          button.setAttribute("aria-label", altAria);
        });
    } else {
      this._autoEnabled = true;
      if (this._autoTimeout != undefined) {
        clearTimeout(this._autoTimeout);
      }
      const altAria =
        LisioTranslationController.current.getTranslation("startSynthButton");
      this.stopSynthVocalEntirely();
      document.querySelectorAll(".start-synth").forEach((button) => {
        if (button.children[0]) {
          (button.children[0] as SVGElement).innerHTML = this._playButton;
        }
        if (button.classList.contains("start-synth-first")) {
          button.setAttribute(
            "aria-label",
            LisioTranslationController.current.getTranslation(
              "startSynthFirstButton",
            ),
          );
        } else {
          button.setAttribute("aria-label", altAria);
        }
      });
      this._mutationObserver.setObserver();
    }
  }

  private clickRightSynthEventListener(event: MouseEvent) {
    if (!event.ctrlKey && !this._isFromKeyboard) {
      event.preventDefault();
      this._lisioExplainClick?.remove();
      this._lisioExplainClick = undefined;
      this.stopSynthVocalEntirely();
      if ((event.target as HTMLElement | undefined)?.dataset.lisioIndex) {
        const indexInOffsetText = this._offsetTexts.findIndex(
          (elt) =>
            elt ===
            Number.parseInt(
              (event.target as HTMLElement | undefined)?.dataset.lisioIndex ||
                "-1",
            ),
        );
        this._iText = indexInOffsetText;
        if (this._autoEnabled) {
          document.documentElement.classList.add(
            "lisio-vocal-synthesis-reading",
          );
          document.documentElement.classList.add(`lisio-${this._cursorSize}`);
        }
        this.auto();
      } else if (
        (event.target as HTMLElement | undefined)?.classList.contains(
          "start-synth",
        )
      ) {
        this.start(event);
      } else {
        const parentWithDataLisioIndex = (
          event.target as HTMLElement | undefined
        )?.closest<HTMLElement>("[data-lisio-index]");
        if (parentWithDataLisioIndex) {
          if (this._autoEnabled) {
            document.documentElement.classList.add(
              "lisio-vocal-synthesis-reading",
            );
            document.documentElement.classList.add(`lisio-${this._cursorSize}`);
          }
          const indexInOffsetText = this._offsetTexts.findIndex(
            (elt) =>
              elt ===
              Number.parseInt(
                parentWithDataLisioIndex.dataset.lisioIndex || "-1",
              ),
          );
          this._iText = indexInOffsetText;
          this.auto();
        } else {
          if (!this._autoEnabled) {
            this.auto();
          } else {
            this._autoEnabled = true;
          }
        }
      }
    } else {
      this._isFromKeyboard = false;
    }
  }

  private clickSynthEventListener(event: MouseEvent) {
    if (event.detail === 1 && this._stateSynth === true) {
      document.removeEventListener("mousemove", this.lisioMouseMoveHandler);
      // Créer dynamiquement la div de message
      this._lisioExplainClick?.remove();
      const message = document.createElement("div");
      this._lisioExplainClick = message;
      message.id = "lisio-explain-click";

      message.innerHTML = `
      <div>
        <svg width="20" height="20" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12.5098 0C19.4105 0 25.0098 5.59591 25.0098 12.5C25.0098 19.4007 19.4105 25 12.5098 25C5.60568 25 0.00976562 19.4007 0.00976562 12.5C0.00976562 5.59591 5.60568 0 12.5098 0ZM14.0298 4.41889C14.9714 4.41889 15.7348 5.18563 15.7348 6.12725C15.7348 7.06887 14.9714 7.83226 14.0298 7.83226C13.0848 7.83226 12.3214 7.06887 12.3214 6.12725C12.3214 5.18563 13.0848 4.41889 14.0298 4.41889ZM9.36207 10.7883C10.4651 8.55529 15.1631 7.83226 14.3257 10.8892C13.8482 12.6345 12.5838 17.2653 12.3853 18.0421C12.1432 19.0039 13.2462 19.0577 14.4266 18.5297C13.4211 21.1225 8.34982 21.5362 9.43269 18.106C9.43269 18.106 10.872 12.8699 11.2621 11.6862C11.6993 10.3713 10.2162 10.3545 9.36207 10.7849V10.7883ZM12.5098 1.18375C6.25809 1.18375 1.19352 6.24832 1.19352 12.4966C1.19352 18.7483 6.26145 23.8129 12.5098 23.8129C18.7614 23.8129 23.826 18.7483 23.826 12.4966C23.826 6.24832 18.7581 1.18375 12.5098 1.18375Z" fill="white"/></svg>
      </div>
      <p class="notranslate">
        ${LisioTranslationController.current.getTranslation("explainCursor")}
      </p>`;
      LisioShadowRootController.current.appendElement(message, helperStyles);
      // Ajouter la div au body

      const cursorHeight =
        this._cursorSize === "default"
          ? 31
          : this._cursorSize === "big"
            ? 44
            : 64;
      message.style.left = `${event.clientX}px`;
      message.style.top = `${event.clientY}px`;

      const messageHeight = this._lisioExplainClick.offsetHeight;
      const messageWidth = this._lisioExplainClick.offsetWidth;
      let transform = "";
      if (event.pageY - messageHeight < 0) {
        transform += `translateY(${cursorHeight}px) `;
      } else {
        transform += `translateY(-100%) `;
      }
      if (event.pageX + messageWidth > document.documentElement.clientWidth) {
        transform += `translateX(-100%)`;
      } else {
        transform += `translateX(0px) `;
      }
      message.style.transform = transform;

      this.lisioMouseMoveHandler = this.lisioMouseMoveHandler.bind(this);
      document.addEventListener("mousemove", this.lisioMouseMoveHandler);

      // Supprimer le message après 3 secondes
      clearTimeout(this._synthVocaleLeftClickMessageTimeOut);
      this._synthVocaleLeftClickMessageTimeOut = setTimeout(() => {
        message.remove();
        document.removeEventListener("mousemove", this.lisioMouseMoveHandler);
      }, 3000);
    }
  }

  private clickSynthPauseEventListener(event: MouseEvent) {
    if (
      event.detail === 1 &&
      (event.currentTarget as HTMLElement | undefined)?.classList.contains(
        "lisio-vocal-synthesis-reading",
      )
    ) {
      this.clickSynthEventListener(event);
    }
  }

  /**
   *
   * @param html
   * @returns
   * Ne pas demander à Flora ce qu'il se passe ici
   */
  private flattenHTML(html: string) {
    let flatText = "";
    const mapping: number[] = []; // Indices du texte brut vers le HTML
    let insideTag = false;
    let entityBuffer = ""; // Pour accumuler les entités HTML comme &nbsp;

    for (let i = 0; i < html.length; i++) {
      if (html[i] === "<") {
        insideTag = true;
      }

      if (!insideTag) {
        if (html[i] === "&") {
          // Début potentiel d'une entité HTML
          entityBuffer += html[i];
        } else if (entityBuffer) {
          entityBuffer += html[i];
          if (html[i] === ";") {
            // Entité HTML complète, essayons de la décoder
            const decodedChar = decodeHTMLEntity(entityBuffer);
            if (decodedChar !== entityBuffer) {
              // Décodage réussi
              flatText += decodedChar;
              // Mapper chaque caractère décodé à la position originale
              // eslint-disable-next-line @typescript-eslint/no-unused-vars
              for (const _j of decodedChar) {
                mapping.push(i);
              }
            } else {
              // Pas une entité valide, ajouter le buffer tel quel
              flatText += entityBuffer;
              for (let j = 0; j < entityBuffer.length; j++) {
                mapping.push(i - entityBuffer.length + j + 1);
              }
            }
            entityBuffer = ""; // Réinitialiser le buffer
          } else if (!entityBuffer.match(/^&[\w#]*$/)) {
            // Pas une entité valide, ajouter le buffer tel quel
            flatText += entityBuffer;
            for (let j = 0; j < entityBuffer.length; j++) {
              mapping.push(i - entityBuffer.length + j + 1);
            }
            entityBuffer = ""; // Réinitialiser le buffer
          }
        } else {
          // Pas une entité HTML, ajouter normalement
          flatText += html[i];
          mapping.push(i);
        }
      }

      if (html[i] === ">") {
        insideTag = false;
      }
    }

    // Si un buffer reste non traité (ex: fin de fichier avec &), on l'ajoute
    if (entityBuffer) {
      flatText += entityBuffer;
      for (let j = 0; j < entityBuffer.length; j++) {
        mapping.push(html.length - entityBuffer.length + j);
      }
    }

    return { flatText, mapping };
  }

  // Fonction pour surligner un mot lu
  private highlightWord(word: string, wordIndex: number, element: HTMLElement) {
    // Aplatir le texte et obtenir le mapping
    if (
      !this._previousReadenWord ||
      word !== this._previousReadenWord.word ||
      wordIndex !== this._previousReadenWord.wordIndex
    ) {
      const originalHTML = element.innerHTML.replace(/amp;/gm, "");
      let { flatText, mapping } = this.flattenHTML(originalHTML);

      // Suivre l'état des mots lus
      // let currentIndex = 0; // Position actuelle dans le texte brut
      const escapedWord = escapeRegexSpecialChars(word);
      // Rechercher la prochaine occurrence du mot lu
      const regex = new RegExp(
        `(?=|^|\\s|[^\\\\w])(${escapedWord})(?=\\s|$|[^\\\\w]|<)`,
        "gi",
      );
      // let regex = new RegExp(`(${escapedWord})`, "gi");
      let match;

      // Parcourir toutes les correspondances
      const matches = [];
      while ((match = regex.exec(flatText)) !== null) {
        matches.push({
          word: match[1], // Correspondance trouvée
          index: match.index + match[0].indexOf(match[1]), // Index du mot dans le texte
        });
      }

      for (const match of matches) {
        if (match.index >= this._currentIndexSynthVocal) {
          const start = mapping[match.index];
          let tempIndex = start; // Index dans le HTML original
          let remainingLength = word.length; // Longueur restante du mot à marquer
          let markedHTML = ""; // Partie HTML avec les <mark> insérés

          while (remainingLength > 0 && tempIndex < originalHTML.length) {
            if (originalHTML[tempIndex] === "<") {
              // Ajouter toute la balise HTML telle quelle
              let tagEnd = tempIndex;
              while (
                tagEnd < originalHTML.length &&
                originalHTML[tagEnd] !== ">"
              ) {
                tagEnd++;
              }
              tagEnd++;
              markedHTML += originalHTML.slice(tempIndex, tagEnd);
              tempIndex = tagEnd;
            } else {
              // Ajouter les caractères visibles avec <mark>
              const segmentStart = tempIndex;
              while (
                remainingLength > 0 &&
                tempIndex < originalHTML.length &&
                originalHTML[tempIndex] !== "<"
              ) {
                tempIndex++;
                remainingLength--;
              }
              markedHTML += `<mark data-mark='lisio'>${originalHTML.slice(
                segmentStart,
                tempIndex,
              )}</mark>`;
            }
          }

          // Insérer la partie marquée dans le HTML original
          const newHTML =
            originalHTML.slice(0, start) +
            markedHTML +
            originalHTML.slice(tempIndex);

          // Réaplatir le texte après modification
          element.innerHTML = newHTML;
          ({ flatText, mapping } = this.flattenHTML(newHTML));

          // Mettre à jour l'état
          this._currentIndexSynthVocal = match.index + match.word.length;
          break;
        }
      }
    }
    this._previousReadenWord = { word: word, wordIndex: wordIndex };
  }

  private lisioMouseMoveHandler(event: MouseEvent) {
    if (this._lisioExplainClick != undefined) {
      const cursorHeight =
        this._cursorSize === "default"
          ? 31
          : this._cursorSize === "big"
            ? 44
            : 64;
      this._lisioExplainClick.style.left = `${event.clientX}px`;
      this._lisioExplainClick.style.top = `${event.clientY}px`;

      const messageHeight = (
        document.querySelector("#lisio-explain-clic") as HTMLElement
      )?.offsetHeight;
      const messageWidth = (
        document.querySelector("#lisio-explain-clic") as HTMLElement
      )?.offsetWidth;
      let transform = "";
      if (event.pageY - messageHeight < 0) {
        transform += `translateY(${cursorHeight}px) `;
      } else {
        transform += `translateY(-100%) `;
      }
      if (event.pageX + messageWidth > document.documentElement.clientWidth) {
        transform += `translateX(-100%)`;
      } else {
        transform += `translateX(0px) `;
      }
      this._lisioExplainClick.style.transform = transform;
    }
  }

  private makeSynthButton(element: HTMLElement, create = true) {
    let button;
    if (create) {
      button = document.createElement("button");
      element.parentElement?.insertBefore(button, element);
    } else {
      button = element;
    }

    if (!button.hasChildNodes()) {
      const svg = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "svg",
        {},
      );
      const altAria =
        LisioTranslationController.current.getTranslation("startSynthButton");
      button.className = "start-synth lisio-hidden notranslate";
      button.setAttribute("aria-label", altAria);
      button.append(svg);
      svg.setAttribute("width", "57");
      svg.setAttribute("height", "24");
      svg.setAttribute("viewBox", "0 0 57 24");
      svg.setAttribute("fill", "none");
      svg.innerHTML = this._playButton;
      button.addEventListener("click", this.start.bind(this));
    }
  }

  /**
   * Permet de lire le texte suivant
   */
  private next() {
    //même fonctionnement que pour prev
    clearTimeout(this._autoTimeout);
    window.speechSynthesis?.cancel();
    let target = this._walker.tags.get(this._offsetTexts[this._iText]);
    if (target != undefined) {
      let bounding = target.getBoundingClientRect();
      let style = window.getComputedStyle(target);
      while (
        target?.dataset.lisioIndex == undefined ||
        target == this._lastTarget ||
        bounding.height == 0 ||
        bounding.width == 0 ||
        style.display == "none" ||
        style.visibility == "lisio-hidden"
      ) {
        this._iText++;
        if (this._iText >= this._offsetTexts.length - 1) {
          this._iText = 0;
        }
        target = this._walker.tags.get(this._offsetTexts[this._iText]);
        if (target != undefined) {
          bounding = target.getBoundingClientRect();
          style = window.getComputedStyle(target);
        }
      }
      this._lastTarget = document.querySelector(".lisio-speech-active") as
        | HTMLElement
        | undefined;
      if (this._lastTarget != null) {
        this._lastTarget.classList.remove("lisio-speech-active");
      }
      target?.classList.add("lisio-speech-active");
      target?.scrollIntoView({
        behavior: "smooth",
        block: "center",
        inline: "nearest",
      });
      this._lastTarget = target;
      if (target != undefined) {
        this.synthVocal(target);
      }
    }
  }

  private removeMarker() {
    this._previousReadenWord = { word: "", wordIndex: -1 };
    this._currentIndexSynthVocal = 0;
    const markers = document.querySelectorAll('[data-mark="lisio"]');
    for (const marker of markers) {
      if (marker != undefined && marker.parentNode != undefined) {
        const parent = marker.parentNode;

        // Créer un DocumentFragment pour contenir les enfants du mark
        const fragment = document.createDocumentFragment();

        // Ajouter chaque enfant du mark dans le fragment
        while (marker.firstChild) {
          fragment.appendChild(marker.firstChild);
        }

        // Insérer le fragment à la place du mark, et supprimer le mark
        parent.replaceChild(fragment, marker);
        // Parcourir les nœuds de texte du parent pour fusionner ceux qui sont adjacents
        let child = parent.firstChild;
        while (child && child.nextSibling && child.nodeValue) {
          if (
            child.nodeType === Node.TEXT_NODE &&
            child.nextSibling.nodeType === Node.TEXT_NODE
          ) {
            // Fusionner les nœuds texte adjacents
            child.nodeValue += child.nextSibling.nodeValue;
            parent.removeChild(child.nextSibling);
          } else {
            child = child.nextSibling;
          }
        }
      }
    }
  }

  /**
   * lance le mode auto de la synthèse vocale
   * @param {Event} event
   */
  private start(event: Event) {
    event.preventDefault();
    event.stopPropagation();
    document.documentElement.classList.add("lisio-vocal-synthesis-reading");
    document.documentElement.classList.add(`lisio-${this._cursorSize}`);
    this._iText = this._offsetTexts.findIndex((elt) => {
      let nextSibling = (event.currentTarget as HTMLElement)
        .nextElementSibling as HTMLElement | undefined;

      // Parcourir les nœuds jusqu'à trouver un élément HTML avec l'attribut data-lisio-index
      while (nextSibling) {
        if (nextSibling.dataset.lisioIndex != undefined) {
          // Vérifie si l'élément courant (elt) correspond au nœud trouvé
          return elt == Number.parseInt(nextSibling.dataset.lisioIndex || "-1");
        } else if (nextSibling.querySelector("[data-lisio-index]")) {
          return (
            elt ==
            Number.parseInt(
              nextSibling.querySelector<HTMLElement>("[data-lisio-index]")
                ?.dataset.lisioIndex || "-1",
            )
          );
        }
        nextSibling = nextSibling.nextElementSibling as HTMLElement | undefined;
      }

      // Si aucun élément correspondant n'est trouvé
      return false; // Pour exclure cet élément dans findIndex
    });

    clearInterval(this._playPauseInterval);
    this.removeMarker();
    window.speechSynthesis?.cancel();
    if (this._readenText) {
      this._readenText.classList.remove("lisio-speech-active");
    }

    if (!this._autoEnabled) {
      window.speechSynthesis?.cancel();
      clearTimeout(this._autoTimeout);
    }
    if (
      event.target &&
      this._lastTarget == (event.target as HTMLElement).nextSibling
    ) {
      this._lastTarget = undefined;
    }
    this.auto();
  }

  private stopSynthVocalEntirely() {
    this._lastTarget = undefined;
    clearInterval(this._playPauseInterval);
    this.removeMarker();
    window.speechSynthesis?.cancel();
    document.documentElement.classList.remove("lisio-vocal-synthesis-reading");
    const elemActive = document.querySelector(".lisio-speech-active");
    if (elemActive != undefined) {
      elemActive.classList.remove("lisio-speech-active");
    }
  }
}

export { LisioSpeechSynthesisAdapter };
