<script lang="ts">
  import { throttle } from "lodash";
  import Spinner from "../../utils/Spinner.svelte";
  import Icon from "../../utils/Icon.svelte";
  import { useEntity } from "../../../lib/entity";
  import { i18n } from "../../../i18n";
  import { createEventDispatcher } from "svelte";
  import api from "../../../lib/api";
  const dispatch = createEventDispatcher();

  export let address: string;
  export let detail: boolean;

  const { entity } = useEntity(address);

  enum State {
    LOADING = "loading",
    PREVIEW = "preview",
    PREVIEWING = "previewing",
    PLAYING = "playing",
    ERRORED = "errored",
  }
  let state = State.LOADING;
  let supported = true;

  $: if (state == State.PREVIEW) dispatch("loaded");

  $: {
    if ($entity && videoEl) {
      const mime = $entity.get("FILE_MIME");
      if (mime) {
        supported = Boolean(videoEl.canPlayType(mime as string));
      }
    }
  }

  let videoEl: HTMLVideoElement;
  let currentTime: number;

  let timeCodeWidth: number;
  let timeCodeLeft: string;
  let timeCodeSize: string;

  const seek = throttle((progress: number) => {
    if (state === State.PREVIEWING && videoEl.duration) {
      currentTime = videoEl.duration * progress;

      if (timeCodeWidth) {
        let timeCodeLeftPx = Math.min(
          Math.max(videoEl.clientWidth * progress, timeCodeWidth / 2),
          videoEl.clientWidth - timeCodeWidth / 2
        );
        timeCodeLeft = `${timeCodeLeftPx}px`;
        timeCodeSize = `${videoEl.clientHeight / 9}px`;
      }
    }
  }, 100);

  function updatePreviewPosition(ev: MouseEvent) {
    if (state === State.PREVIEW || state === State.PREVIEWING) {
      state = State.PREVIEWING;
      const bcr = videoEl.getBoundingClientRect();
      const progress = (ev.clientX - bcr.x) / bcr.width;
      seek(progress);
    }
  }

  function resetPreview() {
    if (state === State.PREVIEWING) {
      state = State.PREVIEW;
      videoEl.load();
    }
  }

  function startPlaying() {
    if (detail) {
      state = State.PLAYING;
      videoEl.play();
    }
  }
</script>

<div class="video-viewer {state}" class:detail class:unsupported={!supported}>
  <div class="player" style="--icon-size: {detail ? 100 : 32}px">
    {#if state === State.LOADING}
      <Spinner />
    {/if}
    {#if state === State.LOADING || (!detail && state === State.PREVIEW)}
      <!-- svelte-ignore a11y-mouse-events-have-key-events -->
      <img
        class="thumb"
        src="{api.apiUrl}/thumb/{address}?mime=video"
        alt="Preview for {address}"
        loading="lazy"
        on:load={() => (state = State.PREVIEW)}
        on:mouseover={() => (state = State.PREVIEWING)}
        on:error={() => (state = State.ERRORED)}
      />
    {:else}
      <!-- svelte-ignore a11y-media-has-caption -->
      <video
        preload={detail ? "auto" : "metadata"}
        src="{api.apiUrl}/raw/{address}"
        poster="{api.apiUrl}/thumb/{address}?mime=video"
        on:mousemove={updatePreviewPosition}
        on:mouseleave={resetPreview}
        on:click|preventDefault={startPlaying}
        controls={state === State.PLAYING}
        bind:this={videoEl}
        bind:currentTime
      />
      {#if !supported}
        <div class="unsupported-message">
          <div class="label">
            {$i18n.t("UNSUPPORTED FORMAT")}
          </div>
        </div>
      {/if}
    {/if}
    <div class="play-icon">
      <Icon plain border name="play" />
    </div>
    <div
      class="timecode"
      bind:clientWidth={timeCodeWidth}
      style:left={timeCodeLeft}
      style:font-size={timeCodeSize}
    >
      {#if videoEl?.duration && currentTime}
        {#if videoEl.duration > 3600}{String(
            Math.floor(currentTime / 3600)
          ).padStart(2, "0")}:{/if}{String(
          Math.floor((currentTime % 3600) / 60)
        ).padStart(2, "0")}:{String(
          Math.floor((currentTime % 3600) % 60)
        ).padStart(2, "0")}
      {:else if supported}
        <Spinner />
      {/if}
    </div>
  </div>
</div>

<style lang="scss">
  .video-viewer {
    min-width: 0;
    min-height: 0;

    &,
    .player {
      display: flex;
      align-items: center;
      min-height: 0;
      flex-direction: column;
      width: 100%;
    }

    img,
    video {
      width: 100%;
      max-height: 100%;
      min-height: 0;
      object-fit: contain;

      // background: rgba(128, 128, 128, 128);

      transition: filter 0.2s;
    }

    .player {
      position: relative;
    }

    .play-icon {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);

      color: white;
      font-size: var(--icon-size);

      opacity: 0;
      transition: opacity 0.2s;

      pointer-events: none;
    }

    .timecode {
      display: none;
      pointer-events: none;

      position: absolute;
      top: 50%;
      left: var(--left);
      transform: translate(-50%, -50%);

      font-feature-settings: "tnum", "zero";
      font-weight: bold;
      color: white;
      opacity: 0.66;
    }

    &.unsupported.detail {
      .play-icon {
        display: none;
      }
    }

    .unsupported-message {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(1, 1, 1, 0.7);
      pointer-events: none;

      .label {
        position: absolute;
        top: 50%;
        left: 0;
        transform: translateY(-50%);

        width: 100%;
        text-align: center;

        font-weight: bold;

        color: darkred;
      }
    }

    &.loading {
      .player > * {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
      }
    }

    &.standby,
    &.preview {
      img,
      video {
        filter: brightness(0.75);
      }
      .play-icon {
        opacity: 0.8;
      }
    }

    &.previewing {
      .timecode {
        display: block;
      }
      video {
        cursor: pointer;
      }
    }
  }
</style>
