<script context="module" lang="ts">
  let mouse = { x: 0, y: 0 };

  let visible = writable(false);

  const maxDistance = globalThis.innerWidth / 3;
  let frameId: number;
  function update() {
    frameId = requestAnimationFrame(update);
    for (const eye of eyes) {
      const dx = eye.x - mouse.x;
      const dy = eye.y - mouse.y;
      const angle = Math.atan2(dy, dx);

      const distance = Math.hypot(dx, dy);
      if (distance < maxDistance) {
        eye.rotation = angle * (180 / Math.PI) - 90;
        eye.distance = -Math.min(17, distance);
        eye.el.style.setProperty("--distance", `${eye.distance}px`);
        eye.el.style.setProperty(
          "--rotation",
          `${Math.floor(eye.rotation)}deg`,
        );
        eye.el.classList.add("active");
      } else {
        eye.el.classList.remove("active");
      }
    }
  }

  const eyes: {
    x: number;
    y: number;
    rotation: number;
    distance: number;
    el: HTMLDivElement;
    pupil: HTMLDivElement;
  }[] = [];

  function register(eye: HTMLDivElement) {
    const { x, y, width, height } = eye.getBoundingClientRect();
    eyes.push({
      x: x + width / 2,
      y: y + height / 2,
      rotation: 0,
      distance: 0,
      el: eye,
      pupil: eye.children[0] as HTMLDivElement,
    });
    if (frameId) cancelAnimationFrame(frameId);
    update();
  }

  function unregister(eye: HTMLDivElement) {
    const index = eyes.findIndex((e) => e.el === eye);
    eyes.splice(index, 1);
    if (eyes.length === 0) cancelAnimationFrame(frameId);
  }

  function handleMouseMove(ev: MouseEvent) {
    mouse.x = ev.clientX;
    mouse.y = ev.clientY;
  }
</script>

<script lang="ts">
  import { onMount } from "svelte";
  import { writable } from "svelte/store";
  import { scale } from "svelte/transition";

  let eye: HTMLDivElement;
  let _old_eye: HTMLDivElement;
  $: if (eye !== _old_eye) {
    if (_old_eye) {
      unregister(_old_eye);
    }
    if (eye) {
      register(eye);
    }
    _old_eye = eye;
  }

  onMount(() => {
    return () => {
      unregister(eye);
    };
  });
</script>

<svelte:window on:mousemove={handleMouseMove} />

<div
  class="googley-eyes"
  on:click={() => ($visible = !$visible)}
  role="img"
  aria-label="Toggle Googley Eyes"
  aria-hidden="true"
  on:keydown={(ev) => (ev.key === "Enter" ? ($visible = !$visible) : "")}>
  {#if $visible}
    <div class="eye" bind:this={eye} transition:scale>
      <div class="pupil"></div>
    </div>
  {/if}
</div>

<style>
  .googley-eyes {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    max-width: 50px;
    max-height: 50px;
    width: 10vw;
    height: 10vw;
    justify-content: center;
    align-items: center;
  }

  .eye {
    position: relative;
    overflow: hidden;
    max-width: 50px;
    max-height: 50px;
    width: 10vw;
    height: 10vw;
    border-radius: 50%;
    background-color: white;
    display: flex;
    justify-content: center;
    align-items: center;
    transform: rotate(var(--rotation));
    box-shadow: 0px 0px 30px #00000094;
    outline: solid 1px #737373;
  }

  .eye::before {
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background-color: rgba(200, 200, 200, 0.01);
    transform: rotate(calc(var(--rotation) * -1));
    box-shadow:
      5px 5px 10px #ffffff70 inset,
      1px 1px 4px #ffffff96 inset,
      -2px -2px 10px black inset,
      2px 2px 5px #00000078;
  }

  .googley-eyes > :global(.active > .pupil) {
    transform: translateY(var(--distance)) scale(0.7);
  }

  .pupil {
    position: absolute;
    width: 50%;
    height: 50%;
    border-radius: 50%;
    transition: transform cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s;
  }

  .pupil::after {
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background-color: black;
    box-shadow:
      -5px -5px 10px #ffffff70 inset,
      -1px -1px 4px #ffffff96 inset,
      2px 2px 2px black inset;
    transform: translate(-50%, -50%) rotate(calc(var(--rotation) * -1 + 180deg));
    top: 50%;
    left: 50%;
  }
</style>
