export default (modules, q, qa) => {
	let self = {
		volumePopout: q(".videoControls .volumePopout"),
		volumeBar: q(".videoControls .volumePopout .volumeBar"),
		volumeText: q(".videoControls .volumePopout .volumeText"),
		fullscreenBtn: q(".videoControls .fullscreen")
	}

	const vidContainer = modules.player.vidContainer
	const vidCtrls = q(".videoControls")
	const playBtn = q(".videoControls .playBtn")
	const seek = q(".videoControls .timeline .seek")
	const seekThumb = q(".videoControls .timeline .fakeThumb")
	const hoverTime = q(".videoControls .timeline .hoverTimeContainer")
	const hoverTimeVideo = q(".videoControls .timeline .hoverTimeContainer .hoverTimeVideo")
	const hoverTimeText = q(".videoControls .timeline .hoverTimeContainer .hoverTimeText")
	const timecode = q(".videoControls .timecode")
	const volumeBtn = q(".videoControls .volumeBtn")
	// const volumePopout = q(".videoControls .volumePopout")
	// const volumeBar = q(".videoControls .volumePopout .volumeBar")
	// const volumeText = q(".videoControls .volumePopout .volumeText")
	const settingsBtn = q(".videoControls .settingsBtn")
	const settingsPopout = q(".videoControls .settingsPopout")
	// const fullscreenBtn = q(".videoControls .fullscreen")

	let cursorOverVideo = false
	modules.globals.video.addEventListener("mouseover", () => cursorOverVideo = true)
	modules.globals.video.addEventListener("mouseout", () => cursorOverVideo = false)

	// Auto hide
	let doAutoHide = true
	let hoveringSubmenu = false
	let controlHideTimeout
	let hideHoverTextTimeout
	let hideVolumeTimeout

	// Play button
	function updatePlayBtn() {
		if (!modules.globals.video.paused) {
			seek.classList.remove("paused")
			playBtn.classList.add("pause")
		} else {
			seek.classList.add("paused")
			playBtn.classList.remove("pause")
		}
	}
	modules.globals.video.addEventListener("playing", updatePlayBtn)
	modules.globals.video.addEventListener("pause", updatePlayBtn)
	playBtn.addEventListener("click", () => {
		modules.player.togglePlaying()
		modules.globals.video.focus()
	})

	// Seeking and timeline
	let seeking = false
	let totalLengthFormatted
	function updateTotalLengthFormatted() {
		totalLengthFormatted = new Date(1000 * modules.player.videoLength).toISOString().slice(11, 19)
	}
	updateTotalLengthFormatted()
	modules.globals.video.addEventListener("canplaythrough", () => {
		modules.player.videoLength = modules.globals.video.seekable.end(modules.globals.video.seekable.length - 1)
		updateTotalLengthFormatted()
	})


	self = {
		...self,
		updateVolume: newVol => {
			newVol = newVol || modules.volume.currentVolume
			self.volumeBar.value = newVol
			self.volumeText.innerHTML = `${Math.round(newVol * 100)}%`
		},

		seekFakeThumbUpdate: () => {
			seekThumb.style.left = `${(seek.value / modules.player.videoLength) * 100}%`
		},
	}

	// Volume change
	volumeBtn.addEventListener("click", function() {
		modules.globals.video.muted = !modules.globals.video.muted
		modules.globals.video.focus()
	})
	modules.globals.video.addEventListener("volumechange", function() {
		// Also show video controls and volume bar
		self.hideControls(true)

		let className = "volumeBtn videoControlBtn"
		if (modules.globals.video.muted || modules.volume.currentVolume <= 0)
			className += " mute"
		else {
			if (modules.volume.currentVolume < 0.15)
				className += " off"
			else if (modules.volume.currentVolume < 0.55)
				className += " low"
		}
		volumeBtn.className = className
	})

	// Volume popout displaying
	volumeBtn.addEventListener("mouseover", () => {
		self.hideVolume(false)
	})
	volumeBtn.addEventListener("mouseout", () => self.hideVolume(true))
	self.volumePopout.addEventListener("mouseover", function() {
		self.hideVolume(false)
	})
	self.volumePopout.addEventListener("mouseout", () => self.hideVolume(true))
	modules.globals.video.addEventListener("volumechange", () => self.updateVolume())
	self.volumeBar.addEventListener("input", function() {
		modules.volume.setVolume(self.volumeBar.value)
		modules.globals.video.muted = false
	})
	self.volumeBar.addEventListener("mouseup", () => {
		setTimeout(() => modules.globals.video.focus(), 1)
	})
	self.volumeBar.addEventListener("mousedown", () => self.hideVolume(false))

	// Settings popout...
	settingsBtn.addEventListener("click", () => {
		settingsBtn.classList.toggle("active")
		settingsPopout.classList.toggle("hidden")

		// If freshly opened, go to main page
		if (settingsBtn.classList.contains("active"))
			settingsPopout.setAttribute("data-page", "main")

		modules.globals.video.focus()
	})

	// "Back" buttons
	const backBtns = qa(".videoControls .settingsPopout .settingsPage .header.goBack")
	for (const backBtn of backBtns)
		backBtn.addEventListener("click", () => settingsPopout.setAttribute("data-page", "main"))

	// Settings page
	const settingAutoHide = q(".videoControls .settingsPopout .settingsPage[data-name='main'] .setting.autoHide")
	const settingLoop = q(".videoControls .settingsPopout .settingsPage[data-name='main'] .setting.loop")
	const settingSpeed = q(".videoControls .settingsPopout .settingsPage[data-name='main'] .setting.speed")
	const settingTricks = q(".videoControls .settingsPopout .settingsPage[data-name='main'] .setting.tricks")
	const settingCC = q(".videoControls .settingsPopout .settingsPage[data-name='main'] .setting.cc")
	const settingQuality = q(".videoControls .settingsPopout .settingsPage[data-name='main'] .setting.quality")
	settingAutoHide.addEventListener("click", () => {
		doAutoHide = settingAutoHide.classList.contains("active")
		settingAutoHide.classList.toggle("active")
		settingAutoHide.querySelector(".text").innerText = `Pin controls (${!doAutoHide ? "on" : "off"})`
	})
	settingLoop.addEventListener("click", () => {
		modules.globals.video.loop = !modules.globals.video.loop
		settingLoop.classList.toggle("active")
		settingLoop.querySelector(".text").innerText = `Loop (${modules.globals.video.loop ? "on" : "off"})`
	})
	settingSpeed.addEventListener("click", () => settingsPopout.setAttribute("data-page", "speed"))
	settingTricks.addEventListener("click", () => settingsPopout.setAttribute("data-page", "tricks"))
	if (settingCC)
		settingCC.addEventListener("click", () => settingsPopout.setAttribute("data-page", "cc"))
	settingQuality.addEventListener("click", () => settingsPopout.setAttribute("data-page", "quality"))

	// Speed bar
	const speedBar = q(".videoControls .settingsPopout .settingsPage[data-name='speed'] .speedBar")
	const speedText = q(".videoControls .settingsPopout .settingsPage[data-name='speed'] .speedText")
	speedBar.addEventListener("input", () => {
		modules.globals.video.playbackRate = speedBar.value
		speedText.innerText = `${speedBar.value}x`
		settingSpeed.querySelector(".text").innerText = `Speed (${speedBar.value}x)`
	})
	speedBar.addEventListener("mouseup", () => {
		setTimeout(() => modules.globals.video.focus(), 1)
	})

	// Tricks page
	const settingForceStereo = q(".videoControls .settingsPopout .settingsPage[data-name='tricks'] .forceStereo")
	// Needs to run after all modules are initialized
	modules.globals.video.addEventListener("canplaythrough", () => {
		if (modules.volume.getNumChannels) {
			settingForceStereo.addEventListener("click", () => {
				const currentNumChannels = modules.volume.getNumChannels()
				modules.volume.setNumChannels(3 - modules.volume.getNumChannels())
				settingForceStereo.querySelector(".text").innerText = `Force mono audio (${currentNumChannels == 1 ? "off" : "on"})`
			})
		} else {
			settingForceStereo.setAttribute("disabled", true)
		}
	})

	// Subtitles page
	const captionButtons = qa(".videoControls .settingsPopout .settingsPage[data-name='cc'] .setting.caption")
	const captionRedownloadButtons = qa(".videoControls .settingsPopout .settingsPage[data-name='cc'] .setting.caption .redownloadBtn")
	if (captionButtons && captionButtons.length > 0) {
		// Caption switching
		for (const captionBtn of captionButtons)
			captionBtn.addEventListener("click", () => self.clickCaptionBtn(captionBtn))

		// Redownload button
		for (const redownloadBtn of captionRedownloadButtons) {
			redownloadBtn.addEventListener("click", () => {
				for (const rdlb of captionRedownloadButtons)
					rdlb.setAttribute("disabled", true)

				const captionLabel = redownloadBtn.previousElementSibling.innerText
				let linkedTrack
				for (const track of modules.globals.video.textTracks) {
					if (track.label == captionLabel) {
						linkedTrack = track
						break
					}
				}

				newToastWhenReady("yellow", "loading", `Redownloading captions (${captionLabel})...`)
				modules.globals.video.focus()

				// fetch
				fetch(`/getCaption?url=${linkedTrack.url}&redownload=1`)
					.then(r => {
						if (r.status == 404 || r.status == 403 || r.status == 429) {
							if (r.status == 429) // Ratelimited
								newToastWhenReady("red", "x", `Too many requests. You may download captions \"${captionLabel}\" in ${r.headers.get("retry-after")} seconds...`)
							else
								newToastWhenReady("red", "x", `Failed to fetch captions (${r.status}: ${r.statusText})`)
							console.error(r)
							for (const rdlb of captionRedownloadButtons)
								rdlb.removeAttribute("disabled")
						} else {
							self.clickCaptionBtn(captionButtons[0])
							const previousSrc = linkedTrack.src
							linkedTrack.src = previousSrc
							self.clickCaptionBtn(redownloadBtn.parentNode)
							newToastWhenReady("green", "check", "Done!")
						}
					})
			})
		}
	}

	// Quality page
	self = {
		...self,
		qualityButtons: qa(".videoControls .settingsPopout .settingsPage[data-name='quality'] .setting.quality"),
		qualityRedownloadButtons: qa(".videoControls .settingsPopout .settingsPage[data-name='quality'] .setting.quality .redownloadBtn")
	}
	const setQualityText = quality => settingQuality.querySelector(".text").innerText = quality
	function switchQualityBtn(qualityBtn) {
		setQualityText(qualityBtn.getAttribute("data-label"))
		modules.quality.qualitySelected(qualityBtn.getAttribute("data-label"))
		modules.globals.video.focus()
	}

	// Quality switching
	for (const btn of self.qualityButtons)
		btn.addEventListener("click", () => {
			if (!btn.getAttribute("disabled"))
				switchQualityBtn(btn)
		})

	// Redownload buttons
	for (const redownloadBtn of self.qualityRedownloadButtons) {
		redownloadBtn.addEventListener("click", () => {
			if (!redownloadBtn.parentNode.getAttribute("disabled")) {
				modules.quality.setQualityButtons(false)
				setQualityButtonActive(null)

				const quality = redownloadBtn.parentNode.getAttribute("data-label")

				// Switch to a known safe quality if possible
				delete modules.quality.safeQualities[quality]
				if (Object.keys(modules.quality.safeQualities).length > 0) {
					const q = Object.keys(modules.quality.safeQualities)[0]
					modules.quality.setVideoSource(`/getVideo?v=${data.videoId}&q=${q}`)

					// Resize video
					for (const btn of self.qualityButtons) {
						if (btn.getAttribute("data-label") == q) {
							if (btn.getAttribute("data-w")) {
								modules.globals.video.setAttribute("width", btn.getAttribute("data-w"))
								modules.globals.video.setAttribute("height", btn.getAttribute("data-h"))
							}
							break
						}
					}
				}

				newToastWhenReady("yellow", "loading", `Requesting redownload for stream (${quality})...`)
				video.focus()

				// fetch
				let doFetch
				doFetch = () => {
					fetch(`/redownloadVideo?videoID=${data.videoId}&quality=${quality}`)
						.then(r => {
							if (r.status == 404 || r.status == 500) {
								newToastWhenReady("red", "x", `Failed to redownload video (${r.status}: ${r.statusText})`)
								console.error(r)
								modules.quality.setQualityButtons(true)
							} else if (r.status == 429) {
								const rateLimitToast = newToast("red", "x", `Too many requests. Downloading ${quality} in ${r.headers.get("retry-after")} seconds...`)
								setTimeout(() => {
									if (rateLimitToast)
										setToastRemove(rateLimitToast.container)
									doFetch()
								}, 1000 * Number(r.headers.get("retry-after")))
							} else {
								modules.quality.lastToast = newToast("yellow", "loading", `Redownloading ${quality}...`, true)
								const checkInt = setInterval(() => {
									fetch(`/cacheInfo?videoName=${data.videoId}-${quality}`)
										.then(r => r.json().then(r => {
											const status = r.status

											if (status == "found") {
												videoDownloaded(`/getVideo?v=${data.videoId}&q=${quality}`, quality)
												clearInterval(checkInt)
											}
										}))
								}, 1000 * 3)
							}
						})
				}
				doFetch()
			}
		})
	}

	// Fullscreen
	self.fullscreenBtn.addEventListener("click", () => {
		modules.player.toggleFullScreen()
		modules.globals.video.focus()
	})



	self = {
		...self,

		hideControls: doHide => {
			self.updateTime()

			// Restores mouse trail on Eir theme
			if (modules.globals.forceCursor && vidContainer.classList.contains("hideCursor"))
				modules.globals.forceCursor("default")
			vidContainer.classList.remove("hideCursor")
			vidCtrls.classList.remove("hidden")
			clearTimeout(controlHideTimeout)
			if (doHide && doAutoHide && !hoveringSubmenu)
				controlHideTimeout = setTimeout(() => {
					// Hides mouse trail on Eir theme
					if (modules.globals.forceCursor && cursorOverVideo)
						modules.globals.forceCursor(null)
					vidContainer.classList.add("hideCursor")
					vidCtrls.classList.add("hidden")
				}, 1000)
		},
		hideHoverText: doHide => {
			hoverTime.classList.remove("hidden")
			clearTimeout(hideHoverTextTimeout)
			if (doHide)
				hideHoverTextTimeout = setTimeout(() => hoverTime.classList.add("hidden"), 50)
		},
		hideVolume: doHide => {
			self.volumePopout.classList.remove("hidden")
			clearTimeout(hideVolumeTimeout)
			if (doHide)
				hideVolumeTimeout = setTimeout(() => self.volumePopout.classList.add("hidden"), 750)
		},

		setQualityButtonActive: qualityLabel => {
			for (const btn of self.qualityButtons)
				if (btn.getAttribute("data-label") != qualityLabel)
					btn.classList.remove("active")
				else
					btn.classList.add("active")
		},
		onVideoDownloaded: quality => {
			for (const btn of self.qualityButtons) {
				if (btn.getAttribute("data-label") == quality) {
					modules.quality.downloadMp4Btn.innerHTML = `Download ${btn.querySelector(".text").innerText.replace(" *", "")} (${btn.getAttribute("data-size")})`
					if (btn.getAttribute("data-w")) {
						modules.globals.video.setAttribute("width", btn.getAttribute("data-w"))
						modules.globals.video.setAttribute("height", btn.getAttribute("data-h"))
					}
				}
				btn.removeAttribute("disabled")
			}
			self.setQualityButtonActive(quality)
			setQualityText(quality)
		},

		updateTime: () => {
			if (!seeking) {
				seek.value = modules.globals.video.currentTime
				self.seekFakeThumbUpdate()
			}
			timecode.innerText = `${new Date(1000 * modules.globals.video.currentTime).toISOString().slice(11, 19)} / ${totalLengthFormatted}`
		},
		seekUpdate: () => {
			modules.globals.video.currentTime = seek.value
			modules.globals.video.focus()
		},

		clickCaptionBtn: captionBtn => {
			function onDone(track) {
				if (track) {
					if (track.eirWasHere)
						modules.captions.registerCueChange(track, true)
					else
						track.mode = "hidden"
					modules.player.lastTrack = track
				}
				settingCC.querySelector(".text").innerText = captionBtn.getAttribute("data-label")

				for (const btn of captionButtons)
					btn.classList.remove("active")
				captionBtn.classList.add("active")
				modules.globals.video.focus()
			}

			modules.globals.video.focus()

			let found = false
			for (let i = 0; i < modules.globals.video.textTracks.length; i++) {
				const track = modules.globals.video.textTracks[i]
				if (track.label == captionBtn.getAttribute("data-label")) {
					found = true
					// Format if needed
					if (!track.eirWasHere)
						modules.captions.replaceTrack(i).then(onDone)
					else
						onDone(track)
				} else
					if (track.eirWasHere)
						modules.captions.registerCueChange(track, false)
					else
						track.mode = "hidden"
			}
			if (!found)
				onDone()
		}
	}

	// Auto hide
	vidContainer.parentNode.addEventListener("mousemove", () => {
		self.hideControls(true)
	})
	vidCtrls.addEventListener("mouseover", () => {
		hoveringSubmenu = true
	})
	vidCtrls.addEventListener("mouseout", () => {
		hoveringSubmenu = false
		self.hideControls(true)
	})

	// Seeking and timeline
	modules.globals.video.addEventListener("timeupdate", function() {
		if (vidCtrls.classList.contains("hidden"))
			return

		self.updateTime()
	}, false)

	seek.addEventListener("input", self.seekFakeThumbUpdate)
	seek.addEventListener("change", () => {
		self.seekFakeThumbUpdate()
		self.seekUpdate()
	})
	seek.addEventListener("mousedown", () => {
		seeking = true
		self.hideControls(true)
	})
	seek.addEventListener("mouseup", () => {
		seeking = false
		self.seekUpdate()
	})
	seek.addEventListener("mouseover", () => {
		self.hideHoverText(false)
	})
	seek.addEventListener("mousemove", event => {
		self.hideHoverText(false)

		// Move hover text to match current mouse position over play bar
		const seekRect = seek.getBoundingClientRect()
		const progress = (event.clientX - seekRect.left) / seekRect.width
		// const progress = Math.max(event.clientX - (seekRect.left + 8), 0) / (seekRect.width - 16)
		hoverTime.style.left = `${progress * 100}%`
		hoverTimeText.innerText = new Date(1000 * (progress * modules.player.videoLength)).toISOString().slice(11, 19)
		hoverTimeVideo.currentTime = progress * modules.player.videoLength
	})
	seek.addEventListener("mouseout", () => {
		self.hideHoverText(true)
	})

	self.hideControls(true)

	return self
}
