import { Accessor, Component, createEffect, createSignal, onMount, Setter, Show } from "solid-js";
import { DateTime } from "luxon";
import {
	TimeType,
	wubloaderTimeFromDateTime,
	busTimeFromDateTime,
	timeAgoFromDateTime,
	dateTimeFromWubloaderTime,
	dateTimeFromBusTime,
	dateTimeFromTimeAgo,
} from "./convertTime";
import styles from "./video.module.scss";
import "./video.scss";
import { HLSProvider } from "vidstack";
import { MediaPlayerElement } from "vidstack/elements";
import { StreamVideoInfo } from "./streamInfo";

import "vidstack/icons";
import "vidstack/player/styles/default/theme.css";
import "vidstack/player/styles/default/layouts/video.css";
import "vidstack/player";
import "vidstack/player/layouts/default";
import "vidstack/player/ui";

export const VIDEO_FRAMES_PER_SECOND = 30;

export const PLAYBACK_RATES = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 4, 8];

export interface StreamTimeSettingsProps {
	/** Getter for the signal to get the time when the run starts. */
	busStartTime: Accessor<DateTime>;
	/** Getter for the StreamVideoInfo signal. Used to set field values and, if applicable, apply defaults. */
	streamVideoInfo: Accessor<StreamVideoInfo>;
	/** Setter for the StreamVideoInfo signal. */
	setStreamVideoInfo: Setter<StreamVideoInfo>;
	/**
	 * If true, shows a link to the same page with the specified stream and time range. For the link to work, the host
	 * page must support defaulting the link data into the StreamVideoInfo.
	 */
	showTimeRangeLink: boolean;
	/** Getter for the signal containing the list of errors to display. */
	errorList: Accessor<string[]>;
	/** Setter for the signal containing the list of errors to display. */
	setErrorList: Setter<string[]>;
}

export const StreamTimeSettings: Component<StreamTimeSettingsProps> = (props) => {
	const [timeType, setTimeType] = createSignal<TimeType>(TimeType.UTC);

	const submitHandler = (event: SubmitEvent) => {
		event.preventDefault();

		const form = event.currentTarget as HTMLFormElement;
		const formData = new FormData(form);

		const streamName = formData.get("stream") as string;
		const startTimeEntered = formData.get("start-time") as string;
		const endTimeEntered = formData.get("end-time") as string;
		const timeType = +formData.get("time-type")! as TimeType;

		let startTime: DateTime | null = null;
		let endTime: DateTime | null = null;
		switch (timeType) {
			case TimeType.UTC:
				startTime = dateTimeFromWubloaderTime(startTimeEntered);
				if (endTimeEntered !== "") {
					endTime = dateTimeFromWubloaderTime(endTimeEntered);
				}
				break;
			case TimeType.BusTime:
				startTime = dateTimeFromBusTime(props.busStartTime(), startTimeEntered);
				if (endTimeEntered !== "") {
					endTime = dateTimeFromBusTime(props.busStartTime(), endTimeEntered);
				}
				break;
			case TimeType.TimeAgo:
				startTime = dateTimeFromTimeAgo(startTimeEntered);
				if (endTimeEntered !== "") {
					endTime = dateTimeFromTimeAgo(endTimeEntered);
				}
				break;
		}

		if (startTime === null || (endTimeEntered !== "" && endTime === null)) {
			const error = "A load boundary time could not be parsed. Check the format of your times.";
			props.setErrorList([...props.errorList(), error]);
			return;
		}

		props.setStreamVideoInfo({
			streamName: streamName,
			streamStartTime: startTime,
			streamEndTime: endTime,
		});
	};

	const startTimeDisplay = () => {
		const startTime = props.streamVideoInfo().streamStartTime;
		switch (timeType()) {
			case TimeType.UTC:
				return wubloaderTimeFromDateTime(startTime);
			case TimeType.BusTime:
				return busTimeFromDateTime(props.busStartTime(), startTime);
			case TimeType.TimeAgo:
				return timeAgoFromDateTime(startTime);
		}
	};

	const endTimeDisplay = () => {
		const endTime = props.streamVideoInfo().streamEndTime;
		if (endTime === null) {
			return "";
		}
		switch (timeType()) {
			case TimeType.UTC:
				return wubloaderTimeFromDateTime(endTime);
			case TimeType.BusTime:
				return busTimeFromDateTime(props.busStartTime(), endTime);
			case TimeType.TimeAgo:
				return timeAgoFromDateTime(endTime);
		}
	};

	const timeRangeLink = () => {
		const streamInfo = props.streamVideoInfo();
		const startTime = wubloaderTimeFromDateTime(streamInfo.streamStartTime);
		const query = new URLSearchParams({
			stream: streamInfo.streamName,
			start: startTime,
		});
		if (streamInfo.streamEndTime) {
			const endTime = wubloaderTimeFromDateTime(streamInfo.streamEndTime);
			query.append("end", endTime);
		}
		return `?${query}`;
	};

	return (
		<form onSubmit={submitHandler} class={styles.streamTimeSettings}>
			<label>
				<span class={styles.streamTimeSettingLabel}>Stream</span>
				<input type="text" name="stream" value={props.streamVideoInfo().streamName} />
			</label>
			<label>
				<span class={styles.streamTimeSettingLabel}>Start Time</span>
				<input type="text" name="start-time" value={startTimeDisplay()} />
			</label>
			<label>
				<span class={styles.streamTimeSettingLabel}>End Time</span>
				<input type="text" name="end-time" value={endTimeDisplay()} />
			</label>
			<div>
				<label>
					<input
						type="radio"
						name="time-type"
						value={TimeType.UTC}
						checked={timeType() === TimeType.UTC}
						onClick={(event) => setTimeType(TimeType.UTC)}
					/>
					UTC
				</label>
				<label>
					<input
						type="radio"
						name="time-type"
						value={TimeType.BusTime}
						checked={timeType() === TimeType.BusTime}
						onClick={(event) => setTimeType(TimeType.BusTime)}
					/>
					Bus Time
				</label>
				<label>
					<input
						type="radio"
						name="time-type"
						value={TimeType.TimeAgo}
						checked={timeType() === TimeType.TimeAgo}
						onClick={(event) => setTimeType(TimeType.TimeAgo)}
					/>
					Time Ago
				</label>
			</div>
			<div>
				<button type="submit">Update Time Range</button>
			</div>
			<Show when={props.showTimeRangeLink}>
				<div>
					<a href={timeRangeLink()}>Link to this time range</a>
				</div>
			</Show>
		</form>
	);
};

export interface VideoPlayerProps {
	src: Accessor<string>;
	setPlayerTime: Setter<number>;
	mediaPlayer: Accessor<MediaPlayerElement>;
	setMediaPlayer: Setter<MediaPlayerElement>;
}

export const VideoPlayer: Component<VideoPlayerProps> = (props) => {
	createEffect(() => {
		const player = props.mediaPlayer();
		const srcURL = props.src();
		player.src = srcURL;
	});

	let [playerTime, setPlayerTime] = createSignal(0);
	let [duration, setDuration] = createSignal(0);
	let [playbackSpeed, setPlaybackSpeed] = createSignal(1);
	let [qualityName, setQualityName] = createSignal("");

	onMount(() => {
		const player = props.mediaPlayer();
		player.subscribe(({ currentTime, duration, playbackRate }) => {
			setPlayerTime(currentTime);
			props.setPlayerTime(currentTime);
			setDuration(duration);
			setPlaybackSpeed(playbackRate);
		});
		player.streamType = "on-demand";
	});

	// The <media-time> elements provided by vidstack don't show milliseconds, so
	// we need to run our own for millisecond display.
	const formatTime = (time: number) => {
		const hours = Math.floor(time / 3600);
		const minutes = Math.floor((time / 60) % 60);
		const milliseconds = Math.floor((time % 1) * 1000);
		const seconds = Math.floor(time % 60);

		const minutesDisplay = minutes.toString().padStart(2, "0");
		const secondsDisplay = seconds.toString().padStart(2, "0");
		const millisecondsDisplay = milliseconds.toString().padStart(3, "0");

		if (hours === 0) {
			return `${minutesDisplay}:${secondsDisplay}.${millisecondsDisplay}`;
		}
		return `${hours}:${minutesDisplay}:${secondsDisplay}.${millisecondsDisplay}`;
	};

	return (
		<media-player
			src={props.src()}
			ref={props.setMediaPlayer}
			preload="auto"
			storage="thrimbletrimmer"
		>
			<media-provider />
			<media-gesture event="pointerup" action="toggle:paused" />
			<media-gesture event="dblpointerup" action="toggle:fullscreen" />
			<media-captions class="vds-captions" />
			<media-controls class="vds-controls">
				<media-controls-group class="vds-controls-group">
					<media-tooltip>
						<media-tooltip-trigger>
							<media-play-button class="vds-button">
								<media-icon type="play" class="vds-play-icon" />
								<media-icon type="pause" class="vds-pause-icon" />
								<media-icon type="replay" class="vds-replay-icon" />
							</media-play-button>
						</media-tooltip-trigger>
						<media-tooltip-content class="vds-tooltip-content" placement="top">
							<span class="vds-play-tooltip-text">Play</span>
							<span class="vds-pause-tooltip-text">Pause</span>
						</media-tooltip-content>
					</media-tooltip>

					<media-tooltip>
						<media-tooltip-trigger>
							<media-mute-button class="vds-button">
								<media-icon type="mute" class="vds-mute-icon" />
								<media-icon type="volume-low" class="vds-volume-low-icon" />
								<media-icon type="volume-high" class="vds-volume-high-icon" />
							</media-mute-button>
						</media-tooltip-trigger>
						<media-tooltip-content class="vds-tooltip-content" placement="top">
							<span class="vds-mute-tooltip-text">Unmute</span>
							<span class="vds-unmute-tooltip-text">Mute</span>
						</media-tooltip-content>
					</media-tooltip>

					<media-volume-slider class="vds-slider">
						<div class="vds-slider-track"></div>
						<div class="vds-slider-track vds-slider-track-fill"></div>
						<media-slider-preview class="vds-slider-preview">
							<media-slider-value class="vds-slider-value" />
						</media-slider-preview>
						<div class="vds-slider-thumb"></div>
					</media-volume-slider>

					<div>
						<span>{formatTime(playerTime())}</span>
						<span class="vds-time-divider">/</span>
						<span>{formatTime(duration())}</span>
					</div>

					<div class="vds-controls-spacer"></div>

					<media-menu class="vds-menu vds-settings-menu">
						<media-tooltip class="vds-tooltip">
							<media-tooltip-trigger>
								<media-menu-button class="vds-button vds-menu-button">
									<media-icon
										class="vds-icon vds-menu-settings-icon vds-rotate-icon"
										type="settings"
									/>
								</media-menu-button>
							</media-tooltip-trigger>
							<media-tooltip-content class="vds-tooltip-content" placement="top">
								Settings
							</media-tooltip-content>
						</media-tooltip>
						<media-menu-items class="vds-menu-items vds-settings-menu-items" placement="top">
							<section class="vds-menu-section" role="group">
								<div class="vds-menu-section-title">
									<header>Speed</header>
									<div class="vds-menu-section-value">{playbackSpeed()}x</div>
								</div>
								<div class="vds-menu-section-body">
									<div class="vds-menu-item vds-menu-slider-item">
										<media-speed-slider class="vds-slider vds-speed-slider" max={8}>
											<div class="vds-slider-track"></div>
											<div class="vds-slider-track vds-slider-track-fill"></div>
											<div class="vds-slider-thumb"></div>
											<media-slider-steps class="vds-slider-steps">
												<template>
													<div class="vds-slider-step"></div>
												</template>
											</media-slider-steps>
										</media-speed-slider>
									</div>
								</div>
							</section>
							<section class="vds-menu-section" role="group">
								<div class="vds-menu-section-title">
									<header>Quality</header>
									<div class="vds-menu-section-value"></div>
								</div>
								<div class="vds-menu-section-body">
									<div class="vds-menu-item vds-menu-slider-item">
										<media-quality-slider class="vds-slider vds-quality-slider">
											<div class="vds-slider-track"></div>
											<div class="vds-slider-track vds-slider-track-fill"></div>
											<div class="vds-slider-track vds-slider-track-progress"></div>
											<div class="vds-slider-thumb"></div>
											<media-slider-steps class="vds-slider-steps">
												<template>
													<div class="vds-slider-step"></div>
												</template>
											</media-slider-steps>
										</media-quality-slider>
									</div>
								</div>
							</section>
						</media-menu-items>
					</media-menu>

					<media-tooltip>
						<media-tooltip-trigger>
							<media-caption-button class="vds-button">
								<media-icon class="vds-cc-on-icon" type="closed-captions-on" />
								<media-icon class="vds-cc-off-icon" type="closed-captions" />
							</media-caption-button>
						</media-tooltip-trigger>
						<media-tooltip-content class="vds-tooltip-content" placement="top">
							<span class="vds-cc-on-tooltip-text">Turn Closed Captions Off</span>
							<span class="vds-cc-off-tooltip-text">Turn Closed Captions On</span>
						</media-tooltip-content>
					</media-tooltip>

					<media-tooltip>
						<media-tooltip-trigger>
							<media-fullscreen-button class="vds-button">
								<media-icon class="vds-fs-enter-icon" type="fullscreen" />
								<media-icon class="vds-fs-exit-icon" type="fullscreen-exit" />
							</media-fullscreen-button>
						</media-tooltip-trigger>
						<media-tooltip-content class="vds-tooltip-content" placement="top end">
							<span class="vds-fs-enter-tooltip-text">Enter Fullscreen</span>
							<span class="vds-fs-exit-tooltip-text">Exit Fullscreen</span>
						</media-tooltip-content>
					</media-tooltip>
				</media-controls-group>
				<media-controls-group class="vds-controls-group">
					<media-time-slider class="vds-time-slider vds-slider">
						<media-slider-chapters class="vds-slider-chapters">
							<template>
								<div class="vds-slider-chapter">
									<div class="vds-slider-track"></div>
									<div class="vds-slider-track vds-slider-track-fill"></div>
								</div>
							</template>
						</media-slider-chapters>
						<media-slider-preview class="vds-slider-preview">
							<media-slider-value class="vds-slider-value" />
						</media-slider-preview>
					</media-time-slider>
				</media-controls-group>
			</media-controls>
		</media-player>
	);
};

export interface KeyboardShortcutProps {
	includeEditorShortcuts: boolean;
}

export const KeyboardShortcuts: Component<KeyboardShortcutProps> = (
	props: KeyboardShortcutProps,
) => {
	return (
		<details class={styles.keyboardShortcutHelp}>
			<summary>Keyboard Shortcuts</summary>
			<ul>
				<li>Number keys (0-9): Jump to that 10% interval of the video (0% - 90%)</li>
				<li>K or Space: Toggle pause</li>
				<li>M: Toggle mute</li>
				<li>J: Back 10 seconds</li>
				<li>L: Forward 10 seconds</li>
				<li>Left arrow: Back 5 seconds</li>
				<li>Right arrow: Forward 5 seconds</li>
				<li>Shift+J: Back 1 second</li>
				<li>Shift+L: Forward 1 second</li>
				<li>Comma (,): Back 1 frame</li>
				<li>Period (.): Forward 1 frame</li>
				<li>Equals (=): Increase playback speed 1 step</li>
				<li>Hyphen (-): Decrease playback speed 1 step</li>
				<li>Shift+=: 2x or maximum playback speed</li>
				<li>Shift+-: Minimum playback speed</li>
				<li>Backspace: Reset playback speed to 1x</li>
				<Show when={props.includeEditorShortcuts}>
					<li>
						Left bracket ([): Set start point for active range (indicated by arrow) to current video
						time
					</li>
					<li>Right bracket (]): Set end point for active range to current video time</li>
					<li>O: Set active range one above current active range</li>
					<li>
						P: Set active range one below current active range, adding a new range if the current
						range is the last one
					</li>
				</Show>
			</ul>
		</details>
	);
};
