<script lang="ts">
  import Dither from 'canvas-dither'
    import ErrorMessage from './ErrorMessage.svelte';
    import { onMount } from 'svelte';
  
  const DITHERING_ALGORITHMS = {
    atkinson: (imageData) => Dither.atkinson(imageData),
    bayer: (imageData) => Dither.bayer(imageData, 170),
    floydSteinberg: (imageData) => Dither.floydsteinberg(imageData),
    threshold: (imageData) => Dither.threshold(imageData, 2),
  }

  let canvas: HTMLCanvasElement;
  let offscreenCanvas: OffscreenCanvas;
  let ctx = $derived(canvas.getContext('2d'))
  let offscreenCtx: OffscreenCanvasRenderingContext2D;
  let imgObjectUrl = $state('')
  let errorMessage = $state('')

  onMount(() => {
   offscreenCanvas = new OffscreenCanvas(1000, 1000)
   offscreenCtx = offscreenCanvas.getContext('2d')
  })

  const doesCtxExist = () => {
    if (!ctx) {
      errorMessage = "Problem finding context for on-screen canvas"
      return false
    }

    return true;
  }

  const handleImageChange = async (event: any) => {
    const file = event.target.files[0];
    if (!file) {
      errorMessage = "No file selected, try again"
      return
    }

    imgObjectUrl = URL.createObjectURL(file);
    const image = await createImage(imgObjectUrl);

    // Change the sizes of canvases
    canvas.width = image.width
    canvas.height = image.height

    offscreenCanvas.height = image.height
    offscreenCanvas.width = image.width

    // Now draw the images 
    drawImage(image)
  }

  async function createImage(objectUrl: string):  Promise<HTMLImageElement> {
    return new Promise<HTMLImageElement>((resolve) => {
      const img = new Image();
      img.onload = () => {
        if (!ctx) return;
        resolve(img);
      }
      img.src = objectUrl;
    });
  }

  const clearCanvases = () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    offscreenCtx.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
  }

  const drawImage = (theImage: HTMLImageElement) => {
    if (!doesCtxExist()) return
    clearCanvases()
    ctx.drawImage(theImage, 0, 0, canvas.width, canvas.height);  
    offscreenCtx.drawImage(theImage, offscreenCanvas.width, offscreenCanvas.height)
  }

  function applyDitheringAlgorithm(algorithm: (imageData: ImageData) => ImageData) {
    return async () => {
      const freshImage = await createImage(imgObjectUrl);
      drawImage(freshImage)
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      const ditheredImageData = algorithm(imageData);
      ctx.putImageData(ditheredImageData, 0, 0);
    }
  }

  function saveImage() {
    if (!canvas) return;
    const link = document.createElement('a');
    link.download = 'dithered-image.png';
    link.href = canvas.toDataURL();
    link.click();
  }

</script>

<ErrorMessage message={errorMessage} />

<h2 class="page-dither__subtitle">1: Select a file</h2>

<form class="file-form">
  <label for="file"> Select a file<br />
    <input type="file" name="file" id="file" onchange={handleImageChange} accept="image/*" >
  </label>
</form>

<h2 class="page-dither__subtitle">2: Select a dithering algorithm</h2>

<ul class="algorithms">
  {#each Object.entries(DITHERING_ALGORITHMS) as [key, value]}
    <li class="algorithms__item">
      <button class="algorithms__button" onclick={applyDitheringAlgorithm(value)}>{key}</button>
    </li>
  {/each}
</ul>


<div class="canvas-container">
  <canvas id="photo-canvas" height="250" bind:this={canvas} onload={() => ctx = canvas.getContext('2d')}></canvas>
</div>


<h2 class="page-dither__subtitle">
  3: Save the file
</h2>
<button onclick={saveImage}>Save Image</button>


<style>
  .algorithms {
    list-style: none;
    display: grid;
    grid-template-columns: auto;
    gap: 4px;
  }

  .algorithms__button {
    box-shadow: 5px 2px blue;

      &:hover {
        background: var()
      }
  }

  .canvas-container {
    display: flex;
    width: auto;
    padding: 14px;
    height: min-content
  }

  #photo-canvas {
    width: 100%;
  }


</style>
