<script lang="ts">
  import { AI, bufToImageUrl } from "helpers";
  import { images as imageStore } from "stores";
  import OrbView from "./OrbView";
  import { onMount } from "svelte";
  import Toast from "components/Toast";

  export let image: Image;
  export let activeTool = "pan";
  export let brushRadius = 20;
  export let activeColor = "ff0000";
  export let layerOpacity = 50;

  const imageUrl = bufToImageUrl(image.data, image.type);

  let canvas: HTMLCanvasElement;
  let canvas2: HTMLCanvasElement;
  let canvas3D: HTMLCanvasElement;
  let wrapper: HTMLDivElement;
  let cx1: CanvasRenderingContext2D;
  let cx2: CanvasRenderingContext2D;

  let orb: ReturnType<typeof OrbView>;
  $: if (activeTool) {
    if (orb) {
      orb.setTool(activeTool);
    }
    if (activeTool === "clear") {
      cx1.clearRect(0, 0, image.width, image.height);
      cx2.clearRect(0, 0, image.width, image.height);
      saveToImage();
      activeTool = "brush";
    }
  }
  $: if (layerOpacity && orb) orb.setOpacity(layerOpacity);

  let mode = "2d";
  let isOriginal = true;

  let topLeftX = 0;
  let topLeftY = 0;
  let wrapperHeightRatio = 1;
  let wrapperWidth = 0;

  let xOffset = 0;

  $: if (xOffset !== undefined) {
    localStorage.setItem("xOffset", "" + xOffset);
  }
  if ("xOffset" in localStorage) {
    xOffset = parseInt(localStorage.getItem("xOffset"));
  }

  let isDown = false;
  let mx = 0;
  let my = 0;
  let downX, downOffset;

  let debugValue = 0;
  let debugY = 0;

  let isStrPressed = false;
  let isSpacePressed = false;
  let lastActiveTool;

  function scaleImageData(imageData, scale) {
    var newCanvas = document.createElement("canvas");
    newCanvas.width = imageData.width;
    newCanvas.height = imageData.height;

    newCanvas.getContext("2d").putImageData(imageData, 0, 0);

    // Second canvas, for scaling
    var scaleCanvas = document.createElement("canvas");
    scaleCanvas.width = imageData.width * scale;
    scaleCanvas.height = imageData.height * scale;

    var scaleCtx = scaleCanvas.getContext("2d");

    scaleCtx.scale(scale, scale);
    scaleCtx.drawImage(newCanvas, 0, 0);

    var scaledImageData = scaleCtx.getImageData(
      0,
      0,
      scaleCanvas.width * scale,
      scaleCanvas.height * scale
    );

    return scaledImageData;
  }

  const saveToImage = () => {
    const imageData = cx1.getImageData(0, 0, image.width, image.height);
    image.overlayData = imageData.data;
    imageStore.updateImage(image);
    cx2.putImageData(imageData, 0, 0);
  };

  function drawBrush() {
    const cx = isOriginal ? cx1 : cx2;
    cx.fillStyle = "#" + activeColor;
    cx.beginPath();

    const x =
      mx * wrapperHeightRatio -
      xOffset * wrapperHeightRatio +
      (isOriginal ? 0 : image.width * wrapperHeightRatio);
    const y = my * wrapperHeightRatio;

    cx.arc(x, y, brushRadius * wrapperHeightRatio, 0, 2 * Math.PI);
    cx.fill();
    cx.closePath();
  }

  let polygonPoints = [];
  let prePolygonImage = new Image(image.width, image.height);
  let lastPolyX;
  let lastPolyY;
  function drawPolygon() {
    const x = Math.floor(
      mx * wrapperHeightRatio - xOffset * wrapperHeightRatio
    );
    const y = Math.floor(my * wrapperHeightRatio);

    if (Math.abs(lastPolyX - x) + Math.abs(lastPolyY - y) < 2) return;

    cx1.clearRect(0, 0, image.width, image.height);
    cx1.drawImage(prePolygonImage, 0, 0, image.width, image.height);

    polygonPoints.push(x, y);

    cx1.beginPath();
    cx1.moveTo(polygonPoints[0], polygonPoints[1]);
    for (let i = 2; i < polygonPoints.length; i += 2) {
      cx1.lineTo(polygonPoints[i], polygonPoints[i + 1]);
    }
    cx1.fillStyle = "#" + activeColor;
    cx1.closePath();
    cx1.fill();

    lastPolyX = x;
    lastPolyY = y;
  }

  function savePrePolygon() {
    lastPolyX = undefined;
    lastPolyY = undefined;
    polygonPoints = [];
    prePolygonImage.src = canvas.toDataURL();
  }

  function switchMode(e: MouseEvent) {
    e.stopPropagation();
    e.stopImmediatePropagation();
    e.preventDefault();

    mode = mode === "2d" ? "3d" : "2d";

    if (mode === "3d") {
      if (!orb) orb = OrbView(image, cx1, canvas3D);
      orb.updateOverlay();
    }

    mode === "2d" ? orb.stop() : orb.start();
  }

  function handleMouseDown(e: MouseEvent) {
    if (e.button === 1) {
      lastActiveTool = activeTool;
      activeTool = "pan";
    }

    downX = e.clientX;
    downOffset = xOffset;
    isDown = true;
    if (activeTool === "brush") drawBrush();
    if (activeTool === "smooth_polygon") {
      savePrePolygon();
    }
  }

  function handleMouseUp(e) {
    isDown = false;
    if (lastActiveTool) {
      activeTool = lastActiveTool;
      lastActiveTool = undefined;
    }

    saveToImage();
  }

  function handleMouseMove(e) {
    mx = Math.floor(e.clientX - topLeftX);
    my = Math.floor(e.clientY - topLeftY);

    isOriginal = e.target.id === "cx1";

    //Caclulate y position of pixel
    const y = Math.floor(my * wrapperHeightRatio);
    debugY = y;
    const height = image.height;

    // KarlKilian Formel
    //debugValue = (2 * Math.sqrt(y * (image.height - y))) / image.height;

    // New new formel
    debugValue = Math.cos(
      (((360 / height ** 2) * y ** 2 + (-360 / height) * y + 90) / 360) *
        2 *
        Math.PI
    );

    if (isDown) {
      if (activeTool === "pan") {
        // TODO fix overflowiung
        xOffset =
          (downOffset + e.clientX - downX) % (image.width / wrapperHeightRatio);
      }

      if (activeTool === "erasor") {
        cx1.globalCompositeOperation = "destination-out";
        cx1.fillStyle = "#" + activeColor;
        cx1.beginPath();
        const mx = Math.floor(
          e.clientX * wrapperHeightRatio -
            topLeftX * wrapperHeightRatio -
            xOffset * wrapperHeightRatio
        );
        const my = Math.floor(
          e.clientY * wrapperHeightRatio - topLeftY * wrapperHeightRatio
        );
        cx1.arc(mx, my, brushRadius * wrapperHeightRatio, 0, 2 * Math.PI);
        cx1.fill();
        cx1.closePath();
      } else {
        cx1.globalCompositeOperation = "source-over";
      }

      if (activeTool === "smooth_polygon") drawPolygon();

      if (activeTool === "brush") drawBrush();
    }
  }

  function handleKeyDown(e) {
    if (e.keyCode === 69) activeTool = "erasor";
    if (e.keyCode === 66) activeTool = "brush";
    if (e.keyCode === 17) isStrPressed = true;
    //SPACE
    if (e.keyCode === 32) {
      isSpacePressed = true;
      if (!lastActiveTool) {
        lastActiveTool = activeTool;
        activeTool = "pan";
      }
    }
  }

  function handleKeyUp(e) {
    if (e.keyCode === 17) isStrPressed = false;
    //SPACE
    if (e.keyCode === 32) {
      activeTool = lastActiveTool;
      lastActiveTool = undefined;
    }
  }

  function handleResize() {
    if (!wrapper) return;
    const box = wrapper.getBoundingClientRect();
    topLeftX = Math.floor(box.x);
    topLeftY = Math.floor(box.y);
    wrapperHeightRatio = image.height / box.height;
    wrapperWidth = box.width;
  }

  let aiState = "AI";
  async function handleAi() {
    if (aiState !== "AI") return;

    if (
      !(await Toast.confirm(
        "For now this action will overwrite existing mask, continue?"
      ))
    ) {
      return;
    }

    aiState = "loading...";
    const res: any = await AI.analyze(image);
    aiState = "Finished";
    setTimeout(() => {
      aiState = "AI";
    }, 1000);

    console.log(res);

    var iData = new ImageData(res.segmentationMap, res.width, res.height);

    const scale = image.width / res.width;
    const s = scaleImageData(iData, scale);

    cx1.putImageData(s, 0, 0);
  }

  onMount(() => {
    canvas.width = image.width;
    canvas.height = image.height;

    canvas2.width = image.width;
    canvas2.height = image.height;

    cx1 = canvas.getContext("2d");
    cx2 = canvas2.getContext("2d");

    if (image.overlayData && image.overlayData.byteLength) {
      const imageData = new ImageData(
        new Uint8ClampedArray(image.overlayData),
        image.width,
        image.height
      );
      cx1.putImageData(imageData, 0, 0);
      cx2.putImageData(imageData, 0, 0);
    }

    handleResize();

    setTimeout(() => handleResize(), 500);
    setTimeout(() => handleResize(), 2000);
  });
</script>

<svelte:window
  on:keydown={handleKeyDown}
  on:keyup={handleKeyUp}
  on:resize={handleResize}
/>

<div
  on:mousedown={handleMouseDown}
  on:mouseup={handleMouseUp}
  on:mouseleave={handleMouseUp}
  on:mousemove={handleMouseMove}
  bind:this={wrapper}
  class={`wrapper tool-${activeTool} mode-${mode}`}
  class:is-down={isDown}
  style={`background-image: url(${imageUrl}); background-position: ${xOffset}px ${0}px`}
>
  <button id="mode" on:click={switchMode}>
    {mode}
  </button>

  <button id="ai" on:click={handleAi}>
    {aiState}
  </button>

  {#if activeTool === "brush" || activeTool === "erasor"}
    <div
      id="cursor"
      style={`width: ${brushRadius * 2}px; height: ${
        brushRadius * 2
      }px; background-color: #${activeColor}; top: ${my}px; left: ${mx}px`}
    />
  {/if}

  <canvas
    id="cx1"
    bind:this={canvas}
    class:visible={mode === "2d"}
    style={`transform: translateX(${xOffset}px); opacity: ${
      layerOpacity / 100
    };`}
  />

  <canvas
    id="cx2"
    bind:this={canvas2}
    class:visible={mode === "2d"}
    style={`transform: translateX(calc(${xOffset}px - ${
      xOffset > 0 ? 100 : -100
    }%)); opacity: ${(layerOpacity / 100) * 0.5};`}
  />

  <p>h:{image.height} | y:{debugY} | value:{debugValue}</p>

  <canvas class:visible={mode === "3d"} bind:this={canvas3D} />
</div>

<style>
  p {
    position: absolute;
    top: 0px;
    left: 0px;
    z-index: 1001;
  }

  #ai {
    position: absolute;
    top: 60px;
    right: 10px;
    z-index: 1001;
    cursor: pointer;
    pointer-events: all;
  }

  #mode {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 1001;
    cursor: pointer;
    pointer-events: all;
  }

  .tool-erasor > #cursor {
    background-color: transparent !important;
    border: solid medium black;
  }

  #cursor {
    position: absolute;
    pointer-events: none;
    opacity: 0.5;
    z-index: 99;
    border-radius: 100%;
    transform: translateX(-50%) translateY(-50%);
  }

  .wrapper.tool-pan {
    cursor: grab;
  }

  .wrapper.tool-pan.is-down {
    cursor: grabbing;
  }

  .wrapper.mode-3d {
    background-image: none !important;
  }

  .wrapper {
    position: relative;
    user-select: none;
    overflow: hidden;

    height: 100%;
    background-size: auto 100%;
  }
  canvas {
    position: absolute;
    display: none;
    height: 100%;
  }

  canvas.visible {
    display: block;
  }
</style>
