window.addEventListener("load", function () {
	var userAgent = window.navigator.userAgent;
	var isIe11 = userAgent.indexOf("Trident/") > 0;
	var isIe = userAgent.indexOf("MSIE ") > 0;
	if (isIe11 || isIe) {
		var listenButton = document.getElementById("listen-button");
		listenButton.parentNode.removeChild(listenButton);
	}
	return;
});

window.addEventListener("load", async function () {
	class AudioPlayerWithHighlight {
		constructor(playerContainer, contentContainer, timings) {
			this.playerContainer = playerContainer;
			this.audio = playerContainer.querySelector("audio");
			this.controlsContainer = playerContainer.querySelector('div[data-contains="controls"]');
			this.optionsContainer = playerContainer.querySelector('div[data-contains="options"]');
			this.listenButtonContainer = playerContainer.querySelector(
				'div[data-contains="listen-button"]'
			);
			this.contentContainer = contentContainer;
			this.timings = timings;

			this.listenButton = playerContainer.querySelector('button[data-action="listen"]');
			this.seekBackButton = playerContainer.querySelector('button[data-action="seek-back"]');
			this.playPauseButton = playerContainer.querySelector(
				'button[data-action="play-pause"]'
			);
			this.seekForwardButton = playerContainer.querySelector(
				'button[data-action="seek-forward"]'
			);
			this.toggleOptionsButton = playerContainer.querySelector(
				'button[data-action="toggle-options"]'
			);
			this.speedButtonContainer = playerContainer.querySelector("#speed-buttons");
			this.toggleAutoScrollInput = playerContainer.querySelector(
				'input[data-action="toggle-auto-scroll"]'
			);

			this.setInitialSpeed();
		}

		currentInterval = null;

		updateHighlight() {
			const currentTime = this.audio.currentTime;

			const currentTimingIndex = this.timings.findIndex((timing) => {
				return currentTime >= timing.startTime && currentTime < timing.endTime;
			});

			if (currentTimingIndex === -1) return;

			const alreadyHighlighted =
				this.contentContainer.querySelector("[data-highlight='true']");
			const spanToHighlight = this.contentContainer.querySelector(
				`[data-timing-index="${currentTimingIndex}"]`
			);

			if (alreadyHighlighted === spanToHighlight) {
				return;
			}

			if (this.automaticallyScroll) {
				this.scrollToSpan(spanToHighlight);
			}

			if (alreadyHighlighted && alreadyHighlighted !== spanToHighlight) {
				alreadyHighlighted.removeAttribute("data-highlight");
			}

			spanToHighlight.setAttribute("data-highlight", "true");
		}

		showControls() {
			// replace the listen button with the audio controls
			this.listenButtonContainer.style.display = "none";
			this.controlsContainer.style.display = "flex";
		}

		onListenClick() {
			this.showControls();
			this.audio.play();
			this.playPauseButton.focus();
		}

		onPlayPauseClick() {
			if (!this.audio.paused) {
				this.audio.pause();
				clearInterval(this.currentInterval);
			} else {
				this.audio.play();
			}
		}

		onSeekBackClick() {
			function getPreviousParent(elem, selector) {
				// Get the next sibling element
				var sibling = elem.previousElementSibling;
				var isFirstChild = !sibling;

				if (isFirstChild) {
					// if it's a table row, go up a level
					const isTableRow = elem.tagName === "TR";

					if (isTableRow) {
						const tableTag = elem.closest("table");
						return getPreviousParent(tableTag, selector);
					}

					// it's the first child, so go up a level
					var parentElement = elem.parentElement;

					var parentIsRoot = parentElement.hasAttribute("data-tts-content");
					if (parentIsRoot) return parentElement;
					// if (parent.tag) {}
				}

				// If there's no selector, return the first sibling
				if (!selector) return sibling;

				// If the sibling matches our selector, use it
				// If not, jump to the next sibling and continue the loop
				while (sibling) {
					if (sibling.tagName === "TABLE") {
						// get the last row
						const rows = sibling.querySelectorAll("tr");
						sibling = rows[rows.length - 1];
					}
					if (sibling.matches(selector)) return sibling;
					sibling = sibling.previousElementSibling;
				}
			}

			const alreadyHighlighted =
				this.contentContainer.querySelector("[data-highlight='true']");
			const currentParent = alreadyHighlighted.closest(
				"tr,h1,h2,h3:not(.word-list-term),h4,.h2,.h3"
			);
			const previousParent = getPreviousParent(
				currentParent,
				"tr,h1,h2,h3:not(.word-list-term),h4,.h2,.h3"
			);
			if (previousParent === null) return;
			const firstSpanInPrevious = previousParent.querySelector("[data-timing-index]");
			const timingIndex = firstSpanInPrevious.getAttribute("data-timing-index");
			const timing = this.timings[timingIndex];
			this.audio.currentTime = timing.startTime;
			this.updateHighlight();
		}

		onSeekForwardClick() {
			function getNextParent(elem, selector) {
				// Get the next sibling element
				var sibling = elem.nextElementSibling;
				var isLastChild = !sibling;

				if (isLastChild) {
					// if it's a table row, go up a level
					const isTableRow = elem.tagName === "TR";

					if (isTableRow) {
						const tableTag = elem.closest("table");
						return getNextParent(tableTag, selector);
					}

					// it's the first child, so go up a level
					var parentElement = elem.parentElement;

					var parentIsRoot = parentElement.hasAttribute("data-tts-content");
					if (parentIsRoot) return null;
				}

				// If there's no selector, return the first sibling
				if (!selector) return sibling;

				// If the sibling matches our selector, use it
				// If not, jump to the next sibling and continue the loop
				while (sibling) {
					if (sibling.tagName === "TABLE") {
						sibling = sibling.querySelector("tr");
					}
					if (sibling.matches(selector)) return sibling;
					sibling = sibling.nextElementSibling;
				}
			}

			const alreadyHighlighted =
				this.contentContainer.querySelector("[data-highlight='true']");
			// const previousSpan =
			const currentParent = alreadyHighlighted.closest(
				"tr,h1,h2,h3:not(.word-list-term),h4,.h2,.h3"
			);
			const nextParent = getNextParent(
				currentParent,
				"tr,h1,h2,h3:not(.word-list-term),h4,.h2,.h3"
			);
			if (nextParent === null) return;
			const firstSpanInNext = nextParent.querySelector("[data-timing-index]");
			const timingIndex = firstSpanInNext.getAttribute("data-timing-index");
			const timing = this.timings[timingIndex];
			this.audio.currentTime = timing.startTime;
			this.updateHighlight();
		}

		onToggleOptionsClick() {
			const shouldShowOptions =
				this.toggleOptionsButton.getAttribute("aria-expanded") === "false";

			if (shouldShowOptions) {
				this.optionsContainer.style.display = "flex";
				this.toggleOptionsButton.setAttribute("aria-expanded", "true");
			} else {
				this.optionsContainer.style.display = "none";
				this.toggleOptionsButton.setAttribute("aria-expanded", "false");
			}
		}

		setSpeed(playbackRate) {
			const rounded = Math.round(playbackRate * 10) / 10;

			if (rounded >= 3.1) {
				return;
			} else if (rounded <= 0.4) {
				return;
			}

			this.audio.playbackRate = rounded;
			this.speedButtonContainer
				.querySelectorAll('[data-action="set-speed"]')
				.forEach((button) => {
					button.removeAttribute("aria-selected");
				});
			const selectedButton = this.speedButtonContainer.querySelector(
				`[data-speed="${playbackRate}"]`
			);

			if (selectedButton) {
				selectedButton.setAttribute("aria-selected", "true");
			}
		}

		onSetSpeedClick(event) {
			const button = event.target;
			const speed = parseFloat(button.getAttribute("data-speed"));
			this.setSpeed(speed);

			// save speed to session storage
			sessionStorage.setItem("ttsPlaybackRate", speed);
		}

		onSlowerSpeedClick() {
			this.setSpeed(this.audio.playbackRate - 0.1);
		}

		onFasterSpeedClick() {
			this.setSpeed(this.audio.playbackRate + 0.1);
		}

		scrollToSpan(spanElem) {
			const spanRect = spanElem.getBoundingClientRect();

			const distanceFromTopOfScreen = spanRect.top;
			const windowHeight = window.innerHeight;

			if (distanceFromTopOfScreen + 100 > windowHeight) {
				window.scrollBy({
					left: 0,
					top: distanceFromTopOfScreen - windowHeight + windowHeight / 3,
					behavior: "smooth"
				});
			}

			if (distanceFromTopOfScreen < 50) {
				window.scrollBy({
					left: 0,
					top: distanceFromTopOfScreen - 100,
					behavior: "smooth"
				});
			}
		}

		onToggleAutoScroll(event) {
			this.automaticallyScroll = event.target.checked;
		}

		onAudioPlaying() {
			const boundUpdateHighlight = this.updateHighlight.bind(this);
			this.currentInterval = setInterval(boundUpdateHighlight, 100);
			// Show playing state
			this.playPauseButton.querySelector('[data-show-when="paused"]').style.display = "none";
			this.playPauseButton.querySelector('[data-show-when="playing"]').style.display = "flex";
			this.playPauseButton.setAttribute("aria-label", "Pause");
		}

		onAudioEnd() {
			clearInterval(this.currentInterval);
			this.playPauseButton.querySelector('[data-show-when="paused"]').style.display = "flex";
			this.playPauseButton.querySelector('[data-show-when="playing"]').style.display = "none";
			this.playPauseButton.setAttribute("aria-label", "Play");
		}

		onAudioPause() {
			clearInterval(this.currentInterval);
			this.playPauseButton.querySelector('[data-show-when="paused"]').style.display = "flex";
			this.playPauseButton.querySelector('[data-show-when="playing"]').style.display = "none";
			this.playPauseButton.setAttribute("aria-label", "Play");
		}

		setInitialSpeed() {
			const savedSpeed = sessionStorage.getItem("ttsPlaybackRate");
			if (savedSpeed) {
				this.setSpeed(parseFloat(savedSpeed));
			}
		}

		addEventListeners() {
			// buttons
			this.listenButton.addEventListener("click", this.onListenClick.bind(this));
			this.seekBackButton.addEventListener("click", this.onSeekBackClick.bind(this));
			this.playPauseButton.addEventListener("click", this.onPlayPauseClick.bind(this));
			this.seekForwardButton.addEventListener("click", this.onSeekForwardClick.bind(this));
			this.toggleOptionsButton.addEventListener(
				"click",
				this.onToggleOptionsClick.bind(this)
			);
			this.toggleAutoScrollInput.addEventListener(
				"change",
				this.onToggleAutoScroll.bind(this)
			);
			this.speedButtonContainer
				.querySelectorAll('[data-action="set-speed"]')
				.forEach((button) => {
					button.addEventListener("click", this.onSetSpeedClick.bind(this));
				});

			// audio
			this.audio.addEventListener("playing", this.onAudioPlaying.bind(this));
			this.audio.addEventListener("ended", this.onAudioEnd.bind(this));
			this.audio.addEventListener("pause", this.onAudioPause.bind(this));
		}

		automaticallyScroll = false;

		setAutomaticallyScroll() {
			const prefersReducedMotion =
				window.matchMedia("(prefers-reduced-motion: reduce)").matches === "true";

			if (this.toggleAutoScrollInput.checked) {
				this.automaticallyScroll = true;
				return;
			}

			if (prefersReducedMotion) {
				this.automaticallyScroll = false;
				this.toggleAutoScrollInput.checked = false;
			} else {
				this.automaticallyScroll = true;
				this.toggleAutoScrollInput.checked = true;
			}
		}

		init() {
			this.setAutomaticallyScroll();
			this.addEventListeners();
		}
	}

	const audioPlayerContainer = document.querySelector("[data-tts-player]");
	const contentContainer = document.querySelector("[data-tts-content='true']");
	const timings = window.listenTimings;

	const audioPlayer = new AudioPlayerWithHighlight(
		audioPlayerContainer,
		contentContainer,
		timings
	);
	audioPlayer.init();
});
