<script lang="ts">
  import { onMount, onDestroy } from 'svelte';

  interface Props {
    analyserNode?: AnalyserNode | null;
  }

  let { analyserNode }: Props = $props();

  let container: HTMLDivElement;
  let canvas: HTMLCanvasElement;
  let canvasContext: CanvasRenderingContext2D | null = null;
  let animationFrameId: number | null = null;
  let dataArray: Uint8Array | null = null;
  let width = $state(800);
  let height = $state(600);

  $effect(() => {
    if (animationFrameId !== null) {
      cancelAnimationFrame(animationFrameId);
      animationFrameId = null;
    }

    if (analyserNode && canvasContext) {
      setupSpectrogram(analyserNode);
    }
  });

  function setupSpectrogram(node: AnalyserNode) {
    try {
      const bufferLength = node.frequencyBinCount;
      dataArray = new Uint8Array(bufferLength);
      startDrawing(node);
    } catch (error) {
      console.error('Failed to setup spectrogram:', error);
    }
  }

  function startDrawing(node: AnalyserNode) {
    if (!canvasContext || !dataArray) return;

    const draw = () => {
      if (!canvasContext || !dataArray || !node || !canvas) return;

      animationFrameId = requestAnimationFrame(draw);

      node.getByteFrequencyData(dataArray);

      const imageData = canvasContext.getImageData(1, 0, canvas.width - 1, canvas.height);
      canvasContext.putImageData(imageData, 0, 0);

      const barWidth = 1;
      const x = canvas.width - barWidth;

      for (let i = 0; i < dataArray.length; i++) {
        const value = dataArray[i];
        const percent = value / 255;
        const y = canvas.height - (i / dataArray.length) * canvas.height;
        const height = (canvas.height / dataArray.length);

        const hue = (1 - percent) * 240;
        const saturation = 100;
        const lightness = percent * 50;

        canvasContext.fillStyle = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
        canvasContext.fillRect(x, y - height, barWidth, height);
      }
    };

    draw();
  }

  function updateSize() {
    if (container && canvas) {
      const rect = container.getBoundingClientRect();
      width = Math.floor(rect.width);
      height = Math.floor(rect.height);
    }
  }

  onMount(() => {
    if (canvas) {
      canvasContext = canvas.getContext('2d');
      if (canvasContext) {
        canvasContext.fillStyle = '#000';
        canvasContext.fillRect(0, 0, canvas.width, canvas.height);
      }
    }

    updateSize();

    const resizeObserver = new ResizeObserver(() => {
      updateSize();
    });

    if (container) {
      resizeObserver.observe(container);
    }

    return () => {
      resizeObserver.disconnect();
    };
  });

  onDestroy(() => {
    if (animationFrameId !== null) {
      cancelAnimationFrame(animationFrameId);
    }
  });
</script>

<div class="spectrogram" bind:this={container}>
  <canvas bind:this={canvas} {width} {height}></canvas>
</div>

<style>
  .spectrogram {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #0a0a0a;
    overflow: hidden;
  }

  canvas {
    display: block;
    width: 100%;
    height: 100%;
    background-color: #0a0a0a;
  }
</style>
