<script lang="ts">
  import ScrollerBase from '../ScrollerBase/ScrollerBase.svelte';
  import type { ContainerWidth } from '../@types/global';

  type Graphic = {
    src: string;
    srcset: string;
    alt: string;
    title?: string;
    description?: string;
    notes?: string;
    width?: ContainerWidth;
  };

  interface Props {
    graphics: Graphic[];
    /** Duration of the squash animation in milliseconds. */
    animationDuration?: number;
    /** When to swap the image during the animation (0–1). E.g. 0.8 = swap at 80% through. */
    animationSwap?: number;
    /**
     * Raw CSS string injected into the document head for this component instance.
     * Use the `.graphic` selector to target the image element.
     * Define any required @keyframes here too.
     *
     * @example
     * graphicCss="@keyframes bounce { to { translate: 0 -20px; } } .graphic { animation: bounce 0.5s ease alternate infinite; }"
     */
    graphicCss?: string;
  }

  let {
    graphics,
    animationDuration = 350,
    animationSwap = 0.8,
    graphicCss,
  }: Props = $props();

  let count = $state(1);
  let index = $state(0);
  let offset = $state(0);
  let progress = $state(0);

  let displayedIndex = $state(0);
  let imgEl = $state<HTMLImageElement | null>(null);

  // Inject graphicCss into the document head as a real style element.
  $effect(() => {
    if (!graphicCss) return;
    const style = document.createElement('style');
    style.textContent = graphicCss;
    document.head.appendChild(style);
    return () => style.remove();
  });

  // On index change: reset the CSS animation, then swap the graphic at the
  // configured point through the animation (animationSwap).
  $effect(() => {
    const newIndex = index; // track reactive dependency
    if (!imgEl) return;

    imgEl.style.animation = 'none';
    imgEl.offsetHeight; // force reflow to restart the animation
    imgEl.style.animation = '';

    const timeout = setTimeout(() => {
      displayedIndex = newIndex;
    }, animationDuration * animationSwap);

    return () => clearTimeout(timeout);
  });
</script>

<div class="photo-scroller">
  <ScrollerBase
    bind:count
    bind:index
    bind:offset
    bind:progress
    query="div.step-foreground-container"
  >
    {#snippet backgroundSnippet()}
      <img
        bind:this={imgEl}
        class="graphic"
        style="--animation-time: {animationDuration}ms"
        src={graphics[displayedIndex].src}
        srcset={graphics[displayedIndex].srcset}
        sizes="(min-width: 690px) 660px, calc(100vw - 30px)"
        alt={graphics[displayedIndex].alt}
      />
    {/snippet}

    {#snippet foregroundSnippet()}
      {#each graphics as graphic}
        <div class="step-foreground-container">
          {#if graphic.title}<h2>{graphic.title}</h2>{/if}
          {#if graphic.description}<p>{graphic.description}</p>{/if}
          {#if graphic.notes}<aside>{graphic.notes}</aside>{/if}
        </div>
      {/each}
    {/snippet}
  </ScrollerBase>
</div>

<style lang="scss">
  .graphic {
    display: block;
    object-fit: contain;
    transform-origin: bottom center;
    animation: squash var(--animation-time) linear(0, 0.417 25.5%, 0.867 49.4%, 1 57.7%, 0.925 65.1%, 0.908 68.6%, 0.902 72.2%, 0.916 78.2%, 0.988 92.1%, 1) forwards;
  }

  @keyframes squash {
    0% {
      scale: 1;
      translate: 0;
    }

    50% {
      scale: 0.8 1.2;
      translate: 0 -8px;
    }

    80% {
      scale: 1.4 0.6;
      translate: 0 -16px;
    }

    100% {
      scale: 1;
      translate: 0;
    }
  }

  .step-foreground-container {
    height: 50vh;
    padding: 1em;
    margin: 0 0 2em 0;
    max-width: var(--normal-column-width, 660px);
    position: relative;
  }
</style>
