<script setup lang="ts">
import { useStore } from "vuex";
import { onMounted, onBeforeUnmount, ref } from "vue";
import Toast from "toasters";
import aw from "@/aw";
import ws from "@/ws";
import { useModalState, useModalActions } from "@/vuex_helpers";

const store = useStore();

const props = defineProps({
	modalUuid: { type: String, default: "" }
});

const interval = ref(null);
const loaded = ref(false);
const canvasWidth = ref(760);
const volumeSliderValue = ref(20);
const durationCanvas = ref(null);
const activityWatchVideoDataInterval = ref(null);
const activityWatchVideoLastStatus = ref("");
const activityWatchVideoLastStartDuration = ref(0);

const { videoId, youtubeId, video, player } = useModalState(
	"modals/viewYoutubeVideo/MODAL_UUID",
	{ modalUuid: props.modalUuid }
);

const {
	updatePlayer,
	stopVideo,
	loadVideoById,
	pauseVideo,
	setPlaybackRate,
	viewYoutubeVideo
} = useModalActions(
	"modals/viewYoutubeVideo/MODAL_UUID",
	[
		"updatePlayer",
		"stopVideo",
		"loadVideoById",
		"pauseVideo",
		"setPlaybackRate",
		"viewYoutubeVideo"
	],
	{
		modalUuid: props.modalUuid
	}
);

const openModal = payload =>
	store.dispatch("modalVisibility/openModal", payload);

const closeCurrentModal = () =>
	store.dispatch("modalVisibility/closeCurrentModal");

const { socket } = store.state.websockets;

const remove = () => {
	socket.dispatch("youtube.removeVideos", videoId, res => {
		if (res.status === "success") {
			new Toast("YouTube video successfully removed.");
			closeCurrentModal();
		} else {
			new Toast("Youtube video with that ID not found.");
		}
	});
};

const handleConfirmed = ({ action, params }) => {
	if (typeof action === "function") {
		if (params) action(params);
		else action();
	}
};

const confirmAction = ({ message, action, params }) => {
	openModal({
		modal: "confirm",
		data: {
			message,
			action,
			params,
			onCompleted: handleConfirmed
		}
	});
};

const seekTo = position => {
	// TODO fix recursive/eslint warning
	// eslint-disable-next-line
	settings("play");
	player.player.seekTo(position);
};

const settings = type => {
	switch (type) {
		case "stop":
			stopVideo();
			pauseVideo(true);
			break;
		case "pause":
			pauseVideo(true);
			break;
		case "play":
			pauseVideo(false);
			break;
		case "skipToLast10Secs":
			seekTo(Number(player.duration) - 10);
			break;
		default:
			break;
	}
};

const play = () => {
	if (player.player.getVideoData().video_id !== video.youtubeId) {
		video.duration = -1;
		loadVideoById(video.youtubeId);
	}
	settings("play");
};

const changeVolume = () => {
	const { volume } = player;
	localStorage.setItem("volume", volume);
	player.player.setVolume(volume);
	if (volume > 0) {
		player.player.unMute();
		player.muted = false;
	}
};

const toggleMute = () => {
	const previousVolume = parseFloat(localStorage.getItem("volume"));
	const volume = player.player.getVolume() <= 0 ? previousVolume : 0;
	player.muted = !player.muted;
	volumeSliderValue.value = volume;
	player.player.setVolume(volume);
	if (!player.muted) localStorage.setItem("volume", volume.toString());
};

// const increaseVolume = () => {
// 	const previousVolume = parseFloat(localStorage.getItem("volume"));
// 	let volume = previousVolume + 5;
// 	player.muted = false;
// 	if (volume > 100) volume = 100;
// 	player.volume = volume;
// 	player.player.setVolume(volume);
// 	localStorage.setItem("volume", volume.toString());
// };

const drawCanvas = () => {
	if (!loaded.value) return;
	const canvasElement = durationCanvas;
	if (!canvasElement) return;
	const ctx = canvasElement.getContext("2d");

	const videoDuration = Number(player.duration);

	const duration = Number(video.duration);
	const afterDuration = videoDuration - duration;

	canvasWidth.value = Math.min(document.body.clientWidth - 40, 760);
	const width = canvasWidth.value;

	const currentTime =
		player.player && player.player.getCurrentTime
			? player.player.getCurrentTime()
			: 0;

	const widthDuration = (duration / videoDuration) * width;
	const widthAfterDuration = (afterDuration / videoDuration) * width;

	const widthCurrentTime = (currentTime / videoDuration) * width;

	const durationColor = "#03A9F4";
	const afterDurationColor = "#41E841";
	const currentDurationColor = "#3b25e8";

	ctx.fillStyle = durationColor;
	ctx.fillRect(0, 0, widthDuration, 20);
	ctx.fillStyle = afterDurationColor;
	ctx.fillRect(widthDuration, 0, widthAfterDuration, 20);

	ctx.fillStyle = currentDurationColor;
	ctx.fillRect(widthCurrentTime, 0, 1, 20);
};

const setTrackPosition = event => {
	seekTo(
		Number(
			Number(player.player.getDuration()) *
				((event.pageX - event.target.getBoundingClientRect().left) /
					canvasWidth.value)
		)
	);
};
const sendActivityWatchVideoData = () => {
	if (!player.paused) {
		if (activityWatchVideoLastStatus.value !== "playing") {
			activityWatchVideoLastStatus.value = "playing";
			activityWatchVideoLastStartDuration.value = Math.floor(
				parseFloat(player.currentTime)
			);
		}

		const videoData = {
			title: video.title,
			artists: video.author,
			youtubeId: video.youtubeId,
			muted: player.muted,
			volume: player.volume,
			startedDuration:
				activityWatchVideoLastStartDuration.value <= 0
					? 0
					: activityWatchVideoLastStartDuration.value,
			source: `viewYoutubeVideo#${video.youtubeId}`,
			hostname: window.location.hostname
		};

		aw.sendVideoData(videoData);
	} else {
		activityWatchVideoLastStatus.value = "not_playing";
	}
};

const init = () => {
	loaded.value = false;
	socket.dispatch("youtube.getVideo", videoId || youtubeId, true, res => {
		if (res.status === "success") {
			const youtubeVideo = res.data;
			viewYoutubeVideo(youtubeVideo);
			loaded.value = true;

			interval.value = setInterval(() => {
				if (
					video.duration !== -1 &&
					player.paused === false &&
					player.playerReady &&
					(player.player.getCurrentTime() > video.duration ||
						(player.player.getCurrentTime() > 0 &&
							player.player.getCurrentTime() >=
								player.player.getDuration()))
				) {
					stopVideo();
					pauseVideo(true);
					drawCanvas();
				}
				if (
					player.playerReady &&
					player.player.getVideoData &&
					player.player.getVideoData() &&
					player.player.getVideoData().video_id === video.youtubeId
				) {
					const currentTime = player.player.getCurrentTime();

					if (currentTime !== undefined)
						player.currentTime = currentTime.toFixed(3);

					if (player.duration.indexOf(".000") !== -1) {
						const duration = player.player.getDuration();

						if (duration !== undefined) {
							if (
								`${player.duration}` ===
								`${Number(video.duration).toFixed(3)}`
							)
								video.duration = duration.toFixed(3);

							player.duration = duration.toFixed(3);
							if (player.duration.indexOf(".000") !== -1)
								player.videoNote = "(~)";
							else player.videoNote = "";

							drawCanvas();
						}
					}
				}

				if (player.paused === false) drawCanvas();
			}, 200);

			activityWatchVideoDataInterval.value = setInterval(() => {
				sendActivityWatchVideoData();
			}, 1000);

			if (window.YT && window.YT.Player) {
				player.player = new window.YT.Player(
					`viewYoutubeVideoPlayer-${props.modalUuid}`,
					{
						height: 298,
						width: 530,
						videoId: null,
						host: "https://www.youtube-nocookie.com",
						playerVars: {
							controls: 0,
							iv_load_policy: 3,
							rel: 0,
							showinfo: 0,
							autoplay: 0
						},
						events: {
							onReady: () => {
								let volume = parseFloat(
									localStorage.getItem("volume")
								);
								volume =
									typeof volume === "number" ? volume : 20;
								player.player.setVolume(volume);
								if (volume > 0) player.player.unMute();

								player.playerReady = true;

								if (video && video._id)
									player.player.cueVideoById(video.youtubeId);

								setPlaybackRate(null);

								drawCanvas();
							},
							onStateChange: event => {
								drawCanvas();

								if (event.data === 1) {
									player.paused = false;
									const youtubeDuration =
										player.player.getDuration();
									const newYoutubeVideoDuration =
										youtubeDuration.toFixed(3);

									if (
										player.duration.indexOf(".000") !==
											-1 &&
										`${player.duration}` !==
											`${newYoutubeVideoDuration}`
									) {
										const songDurationNumber = Number(
											video.duration
										);
										const songDurationNumber2 =
											Number(video.duration) + 1;
										const songDurationNumber3 =
											Number(video.duration) - 1;
										const fixedSongDuration =
											songDurationNumber.toFixed(3);
										const fixedSongDuration2 =
											songDurationNumber2.toFixed(3);
										const fixedSongDuration3 =
											songDurationNumber3.toFixed(3);

										if (
											`${player.duration}` ===
												`${Number(
													video.duration
												).toFixed(3)}` &&
											(fixedSongDuration ===
												player.duration ||
												fixedSongDuration2 ===
													player.duration ||
												fixedSongDuration3 ===
													player.duration)
										)
											video.duration =
												newYoutubeVideoDuration;

										player.duration =
											newYoutubeVideoDuration;
										if (
											player.duration.indexOf(".000") !==
											-1
										)
											player.videoNote = "(~)";
										else player.videoNote = "";
									}

									if (video.duration === -1)
										video.duration = player.duration;

									if (video.duration > youtubeDuration + 1) {
										stopVideo();
										pauseVideo(true);
										return new Toast(
											"Video can't play. Specified duration is bigger than the YouTube song duration."
										);
									}
									if (video.duration <= 0) {
										stopVideo();
										pauseVideo(true);
										return new Toast(
											"Video can't play. Specified duration has to be more than 0 seconds."
										);
									}

									setPlaybackRate(null);
								} else if (event.data === 2) {
									player.paused = true;
								}

								return false;
							}
						}
					}
				);
			} else {
				updatePlayer({
					error: true,
					errorMessage: "Player could not be loaded."
				});
			}

			let volume = parseFloat(localStorage.getItem("volume"));
			volume =
				typeof volume === "number" && !Number.isNaN(volume)
					? volume
					: 20;
			localStorage.setItem("volume", volume.toString());
			updatePlayer({ volume });

			socket.dispatch("apis.joinRoom", `view-youtube-video.${videoId}`);

			socket.on(
				"event:youtubeVideo.removed",
				() => {
					new Toast("This YouTube video was removed.");
					closeCurrentModal();
				},
				{ modalUuid: props.modalUuid }
			);
		} else {
			new Toast("YouTube video with that ID not found");
			closeCurrentModal();
		}
	});
};

onMounted(() => {
	ws.onConnect(init);
});

onBeforeUnmount(() => {
	stopVideo();
	pauseVideo(true);
	player.duration = "0.000";
	player.currentTime = 0;
	player.playerReady = false;
	player.videoNote = "";
	clearInterval(interval.value);
	clearInterval(activityWatchVideoDataInterval.value);
	loaded.value = false;

	socket.dispatch(
		"apis.leaveRoom",
		`view-youtube-video.${videoId}`,
		() => {}
	);

	// Delete the VueX module that was created for this modal, after all other cleanup tasks are performed
	store.unregisterModule(["modals", "viewYoutubeVideo", props.modalUuid]);
});
</script>

<template>
	<modal title="View YouTube Video">
		<template #body>
			<div v-if="loaded" class="top-section">
				<div class="left-section">
					<p>
						<strong>ID:</strong>
						<span :title="video._id">{{ video._id }}</span>
					</p>
					<p>
						<strong>YouTube ID:</strong>
						<a
							:href="
								'https://www.youtube.com/watch?v=' +
								`${video.youtubeId}`
							"
							target="_blank"
						>
							{{ video.youtubeId }}
						</a>
					</p>
					<p>
						<strong>Title:</strong>
						<span :title="video.title">{{ video.title }}</span>
					</p>
					<p>
						<strong>Author:</strong>
						<span :title="video.author">{{ video.author }}</span>
					</p>
					<p>
						<strong>Duration:</strong>
						<span :title="video.duration">{{
							video.duration
						}}</span>
					</p>
				</div>
				<div class="right-section">
					<song-thumbnail :song="video" class="thumbnail-preview" />
				</div>
			</div>

			<div v-show="loaded" class="player-section">
				<div class="player-container">
					<div :id="`viewYoutubeVideoPlayer-${modalUuid}`" />
				</div>

				<div v-show="player.error" class="player-error">
					<h2>{{ player.errorMessage }}</h2>
				</div>

				<canvas
					ref="durationCanvas"
					class="duration-canvas"
					v-show="!player.error"
					height="20"
					:width="canvasWidth"
					@click="setTrackPosition($event)"
				/>
				<div class="player-footer">
					<div class="player-footer-left">
						<button
							class="button is-primary"
							@click="play()"
							@keyup.enter="play()"
							v-if="player.paused"
							content="Resume Playback"
							v-tippy
						>
							<i class="material-icons">play_arrow</i>
						</button>
						<button
							class="button is-primary"
							@click="settings('pause')"
							@keyup.enter="settings('pause')"
							v-else
							content="Pause Playback"
							v-tippy
						>
							<i class="material-icons">pause</i>
						</button>
						<button
							class="button is-danger"
							@click.exact="settings('stop')"
							@click.shift="settings('hardStop')"
							@keyup.enter.exact="settings('stop')"
							@keyup.shift.enter="settings('hardStop')"
							content="Stop Playback"
							v-tippy
						>
							<i class="material-icons">stop</i>
						</button>
						<tippy
							class="playerRateDropdown"
							:touch="true"
							:interactive="true"
							placement="bottom"
							theme="dropdown"
							ref="dropdown"
							trigger="click"
							append-to="parent"
							@show="
								() => {
									player.showRateDropdown = true;
								}
							"
							@hide="
								() => {
									player.showRateDropdown = false;
								}
							"
						>
							<div
								ref="trigger"
								class="control has-addons"
								content="Set Playback Rate"
								v-tippy
							>
								<button class="button is-primary">
									<i class="material-icons">fast_forward</i>
								</button>
								<button class="button dropdown-toggle">
									<i class="material-icons">
										{{
											player.showRateDropdown
												? "expand_more"
												: "expand_less"
										}}
									</i>
								</button>
							</div>

							<template #content>
								<div class="nav-dropdown-items">
									<button
										class="nav-item button"
										:class="{
											active: player.playbackRate === 0.5
										}"
										title="0.5x"
										@click="setPlaybackRate(0.5)"
									>
										<p>0.5x</p>
									</button>
									<button
										class="nav-item button"
										:class="{
											active: player.playbackRate === 1
										}"
										title="1x"
										@click="setPlaybackRate(1)"
									>
										<p>1x</p>
									</button>
									<button
										class="nav-item button"
										:class="{
											active: player.playbackRate === 2
										}"
										title="2x"
										@click="setPlaybackRate(2)"
									>
										<p>2x</p>
									</button>
								</div>
							</template>
						</tippy>
					</div>
					<div class="player-footer-center">
						<span>
							<span>
								{{ player.currentTime }}
							</span>
							/
							<span>
								{{ player.duration }}
								{{ player.videoNote }}
							</span>
						</span>
					</div>
					<div class="player-footer-right">
						<p id="volume-control">
							<i
								class="material-icons"
								@click="toggleMute()"
								:content="`${player.muted ? 'Unmute' : 'Mute'}`"
								v-tippy
								>{{
									player.muted
										? "volume_mute"
										: player.volume >= 50
										? "volume_up"
										: "volume_down"
								}}</i
							>
							<input
								v-model="player.volume"
								type="range"
								min="0"
								max="100"
								class="volume-slider active"
								@change="changeVolume()"
								@input="changeVolume()"
							/>
						</p>
					</div>
				</div>
			</div>

			<div v-if="!loaded" class="vertical-padding">
				<p>Video hasn't loaded yet</p>
			</div>
		</template>
		<template #footer>
			<button
				class="button is-primary icon-with-button material-icons"
				@click.prevent="
					openModal({ modal: 'editSong', data: { song: video } })
				"
				content="Create/edit song from video"
				v-tippy
			>
				music_note
			</button>
			<div class="right">
				<button
					class="button is-danger icon-with-button material-icons"
					@click.prevent="
						confirmAction({
							message:
								'Removing this video will remove it from all playlists and cause a ratings recalculation.',
							action: remove
						})
					"
					content="Delete Video"
					v-tippy
				>
					delete_forever
				</button>
			</div>
		</template>
	</modal>
</template>

<style lang="less" scoped>
.night-mode {
	.player-section,
	.top-section {
		background-color: var(--dark-grey-3) !important;
		border: 0 !important;

		.duration-canvas {
			background-color: var(--dark-grey-2) !important;
		}
	}
}

.top-section {
	display: flex;
	margin: 0 auto;
	padding: 10px;
	border: 1px solid var(--light-grey-3);
	border-radius: @border-radius;

	.left-section {
		display: flex;
		flex-direction: column;
		flex-grow: 1;

		p {
			text-overflow: ellipsis;
			white-space: nowrap;
			overflow: hidden;

			&:first-child {
				margin-top: auto;
			}

			&:last-child {
				margin-bottom: auto;
			}

			& > span,
			& > a {
				margin-left: 5px;
			}
		}
	}

	:deep(.right-section .thumbnail-preview) {
		width: 120px;
		height: 120px;
		margin: 0;
	}

	@media (max-width: 600px) {
		flex-direction: column-reverse;

		.left-section {
			margin-top: 10px;
		}
	}
}

.player-section {
	display: flex;
	flex-direction: column;
	margin: 10px auto 0 auto;
	border: 1px solid var(--light-grey-3);
	border-radius: @border-radius;
	overflow: hidden;

	.player-container {
		position: relative;
		padding-bottom: 56.25%; /* proportion value to aspect ratio 16:9 (9 / 16 = 0.5625 or 56.25%) */
		height: 0;
		overflow: hidden;

		:deep([id^="viewYoutubeVideoPlayer"]) {
			position: absolute;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			min-height: 200px;
		}
	}

	.duration-canvas {
		background-color: var(--light-grey-2);
	}

	.player-error {
		display: flex;
		height: 428px;
		align-items: center;

		* {
			margin: 0;
			flex: 1;
			font-size: 30px;
			text-align: center;
		}
	}

	.player-footer {
		display: flex;
		justify-content: space-between;
		height: 54px;
		padding-left: 10px;
		padding-right: 10px;

		> * {
			width: 33.3%;
			display: flex;
			align-items: center;
		}

		.player-footer-left {
			flex: 1;

			& > .button:not(:first-child) {
				margin-left: 5px;
			}

			:deep(& > .playerRateDropdown) {
				margin-left: 5px;
				margin-bottom: unset !important;

				.control.has-addons {
					margin-bottom: unset !important;

					& > .button {
						font-size: 24px;
					}
				}
			}

			:deep(.tippy-box[data-theme~="dropdown"]) {
				max-width: 100px !important;

				.nav-dropdown-items .nav-item {
					justify-content: center !important;
					border-radius: @border-radius !important;

					&.active {
						background-color: var(--primary-color);
						color: var(--white);
					}
				}
			}
		}

		.player-footer-center {
			justify-content: center;
			align-items: center;
			flex: 2;
			font-size: 18px;
			font-weight: 400;
			width: 200px;
			margin: 0 5px;

			img {
				height: 21px;
				margin-right: 12px;
				filter: invert(26%) sepia(54%) saturate(6317%) hue-rotate(2deg)
					brightness(92%) contrast(115%);
			}
		}

		.player-footer-right {
			justify-content: right;
			flex: 1;

			#volume-control {
				margin: 3px;
				margin-top: 0;
				display: flex;
				align-items: center;
				cursor: pointer;

				.volume-slider {
					width: 100%;
					padding: 0 15px;
					background: transparent;
					min-width: 100px;
				}

				input[type="range"] {
					-webkit-appearance: none;
					margin: 7.3px 0;
				}

				input[type="range"]:focus {
					outline: none;
				}

				input[type="range"]::-webkit-slider-runnable-track {
					width: 100%;
					height: 5.2px;
					cursor: pointer;
					box-shadow: 0;
					background: var(--light-grey-3);
					border-radius: @border-radius;
					border: 0;
				}

				input[type="range"]::-webkit-slider-thumb {
					box-shadow: 0;
					border: 0;
					height: 19px;
					width: 19px;
					border-radius: 100%;
					background: var(--primary-color);
					cursor: pointer;
					-webkit-appearance: none;
					margin-top: -6.5px;
				}

				input[type="range"]::-moz-range-track {
					width: 100%;
					height: 5.2px;
					cursor: pointer;
					box-shadow: 0;
					background: var(--light-grey-3);
					border-radius: @border-radius;
					border: 0;
				}

				input[type="range"]::-moz-range-thumb {
					box-shadow: 0;
					border: 0;
					height: 19px;
					width: 19px;
					border-radius: 100%;
					background: var(--primary-color);
					cursor: pointer;
					-webkit-appearance: none;
					margin-top: -6.5px;
				}
				input[type="range"]::-ms-track {
					width: 100%;
					height: 5.2px;
					cursor: pointer;
					box-shadow: 0;
					background: var(--light-grey-3);
					border-radius: @border-radius;
				}

				input[type="range"]::-ms-fill-lower {
					background: var(--light-grey-3);
					border: 0;
					border-radius: 0;
					box-shadow: 0;
				}

				input[type="range"]::-ms-fill-upper {
					background: var(--light-grey-3);
					border: 0;
					border-radius: 0;
					box-shadow: 0;
				}

				input[type="range"]::-ms-thumb {
					box-shadow: 0;
					border: 0;
					height: 15px;
					width: 15px;
					border-radius: 100%;
					background: var(--primary-color);
					cursor: pointer;
					-webkit-appearance: none;
					margin-top: 1.5px;
				}
			}
		}
	}
}
</style>
