<script lang="ts">
  import LogoIcon from './LogoIcon.svelte'
  import { onMount } from 'svelte'

  let container: HTMLElement | null = $state(null)

  let POSSIBLE_ICONS = $state([
    'java', 'python', 'kotlin', 
    'spring', 'fastapi', 
    'android', 'chrome', 
    'svelte', 'react', 'html5', 'css3', 'javascript', 
    'nodejs',
    'docker', 'nginx', 'amazonwebservices', 
    'mongodb', 'sqlite', 'postgresql', 'redis', 
    'playwright', 'junit', 
    'git', 'githubactions',
    'archlinux', 'ubuntu', 'debian', 'fedora', 'linux', 'windows11', 
    'raspberrypi', 
  ])

  class Phys {
    x: Number
    y: Number
    xspeed: Number
    yspeed: Number
    icon: String
    constructor(x, y, xspeed, yspeed, icon) {
      this.x = x
      this.y = y
      this.xspeed = xspeed
      this.yspeed = yspeed
      this.icon = icon
    }
  }

  let items: Array<Phys> = $state([])
  let scroll = 0;
  let lastScroll = 0;

  let MAX_Y = 0;
  let MAX_X = 0;
  const DIAMETER = 48;
  const GRAVITY = 0.03;
  const FRICTION = 0.005;
  const TERMINAL_VELOCITY = 5;
  const FRAMERATE = 1/30;

  $effect(()=>{
    if (container != null) {
      const rect = container.getBoundingClientRect()
      MAX_Y = rect.height - DIAMETER
      MAX_X = rect.width - DIAMETER
      spawnTimed()
      container = null
    }
  })

  onMount(()=>{
    const interval = setInterval(() => {
      physics()
		}, FRAMERATE);
    const onscroll = ()=>{scroll = window.scrollY}
    window.addEventListener('scroll', onscroll)
		return () => {
      clearInterval(interval);
      window.removeEventListener('scroll', onscroll)
    };
  })

  function spawnTimed() {
    const icon = POSSIBLE_ICONS.pop()
    if (!icon) return;

    items.push({x: Math.random() * 200 + MAX_X/2 - 100, y: -DIAMETER, xspeed: 0.0, yspeed: 0.0, icon: icon})
    setTimeout(spawnTimed, Math.random()*300)
  }

  function respawn(item) {
    items.splice(items.findIndex(i=>i.icon == item.icon), 1)
    POSSIBLE_ICONS.push(item.icon)
    spawnTimed()
  }

  function physics() {
    for (const i of items) {

      // on floor, or gravity
      const grounded = (i.y + i.yspeed) > MAX_Y
      if (grounded) {
        i.y = MAX_Y
        if (i.yspeed > 0) i.yspeed = - Math.abs(i.yspeed / 2)
      } else {
        i.yspeed += GRAVITY
      }

      if (lastScroll != scroll) {
        i.yspeed -= (scroll - lastScroll) / 100
      }

      // horizontal friction
      // TODO: improve sudden stopping
      const friction = grounded? FRICTION *5 : FRICTION
      if (i.xspeed > -friction && i.xspeed < friction) {
        i.xspeed = 0
      } else if (i.xspeed >= friction) {
        i.xspeed -= friction
      } else if (i.xspeed <= -friction) {
        i.xspeed += friction
      }

      // hit walls
      if (i.x + i.xspeed > MAX_X) {
        i.x = MAX_X
        i.xspeed = -Math.abs(i.xspeed/2)
      } else if (i.x - i.xspeed < 0) {
        i.x = 0
        i.xspeed = Math.abs(i.xspeed/2)
      }
      
      // clamp speed to terminal velocity
      i.xspeed = Math.max(-TERMINAL_VELOCITY, Math.min(TERMINAL_VELOCITY, i.xspeed))
      i.yspeed = Math.max(-TERMINAL_VELOCITY, Math.min(TERMINAL_VELOCITY, i.yspeed))

      // be moved by mouse
    }

    for (let i = 0; i < items.length; i++) {
      const a = items[i];

      for (let j = i + 1; j < items.length; j++) {
        const b = items[j];

        const dx = b.x - a.x;
        const dy = b.y - a.y;
        const dist = Math.hypot(dx, dy);

        if (dist < DIAMETER) {
          const overlap = DIAMETER - dist;

          // normalize collision vector
          const nx = dx / dist;
          const ny = dy / dist;

          // avoid overlap
          const correction = overlap / 2;
          a.x -= nx * correction;
          a.y -= ny * correction;
          b.x += nx * correction;
          b.y += ny * correction;

          // calc speed
          const dvx = a.xspeed - b.xspeed;
          const dvy = a.yspeed - b.yspeed;
          const dot = dvx * nx + dvy * ny;

          a.xspeed -= dot * nx;
          a.yspeed -= dot * ny;
          b.xspeed += dot * nx;
          b.yspeed += dot * ny;
        }
      }
    }

    for (const i of items) {
      // move
      i.y += i.yspeed
      i.x += i.xspeed
    }
    lastScroll = scroll
  }
  
</script>

<div 
  bind:this={container} 
  style="width: 100%; height: 100%" 
  class="absolute middle center">

  {#each items as item (item.icon)}
    <LogoIcon icon={item.icon} y={item.y} x={item.x} onclick={()=>respawn(item)}/>
  {/each}
</div>