<script lang="ts">
  import swipeable from "$lib/util/actions/swipeable";
  import { clamp } from "$lib/util/functions";
  import { type Coords, Direction } from "$lib/util/types";
  import { createEventDispatcher } from "svelte";
  import IconButton from "./IconButton.svelte";
  import { mdiChevronLeft, mdiChevronRight } from "@mdi/js";

  export let hasPrev = true;
  export let hasNext = true;
  /** Bind to get the current state */
  export let swiping = false;
  export let arrows = false;
  export let cls = "";

  const dispatch = createEventDispatcher();

  let width: number;
  let pos: number = 0;
  $: pos = -width + offset;

  let offset = 0;

  function onSwipeStart() {
    swiping = true;
  }

  function onSwipeMove(event: CustomEvent<Coords & { dx: number; dy: number }>) {
    offset = clamp(offset + event.detail.dx, hasNext ? -width : 0, hasPrev ? width : 0);
  }

  function onSwipeEnd(event: CustomEvent<{ direction: Direction | null }>) {
    reset();
    const dir = event.detail.direction;

    if (
      (dir === Direction.Forward && hasNext) ||
      (dir === Direction.Backward && hasPrev)
    ) {
      dispatch("swiped", dir);
    }
  }

  function reset() {
    swiping = false;
    offset = 0;
  }
</script>

<div
  class="carousel {cls}"
  bind:clientWidth={width}
  use:swipeable={{ thresholdProvider: () => width / 3 }}
  on:swipeStart={onSwipeStart}
  on:swipeMove={onSwipeMove}
  on:swipeEnd={onSwipeEnd}
  on:swipeFailed={reset}
>
  <div
    class="flex absolute h-full overflow-hidden"
    class:pointer-events-none={offset !== 0}
    style={swiping ? `transform: translateX(${pos}px)` : undefined}
  >
    {#if swiping}
      <div style={`width: ${width}px; min-width: ${width}px;`}>
        <slot name="prev" />
      </div>
    {/if}
    <div style={`width: ${width}px; min-width: ${width}px;`}>
      <slot name="curr" />
    </div>
    {#if swiping}
      <div style={`width: ${width}px; min-width: ${width}px;`}>
        <slot name="next" />
      </div>
    {/if}
  </div>
  {#if arrows}
    <div class="arrow left-0">
      <div>
        <IconButton
          path={mdiChevronLeft}
          size="xs"
          color="default"
          on:click={() => dispatch("swiped", Direction.Backward)}
        />
      </div>
    </div>
    <div class="arrow right-0">
      <div>
        <IconButton
          path={mdiChevronRight}
          size="xs"
          color="default"
          on:click={() => dispatch("swiped", Direction.Forward)}
        />
      </div>
    </div>
  {/if}
</div>

<style lang="postcss">
  .carousel {
    grid-area: trackname;
    position: relative;
  }

  .arrow {
    @apply absolute top-0 h-full w-6;
    @apply flex items-center;
    @apply pointer-events-none;
  }

  .arrow > div {
    @apply h-6 w-6 pointer-events-auto;
  }

  .arrow > div > :global(button) {
    @apply hidden;
  }

  .arrow > div:hover > :global(button) {
    @apply flex;
  }
</style>
