<script lang='ts'>
  import { onMount } from 'svelte'

  import type { data } from '../../wailsjs/go/models.ts'
  import type { LoadedFile } from '../types/file'
  import type { PixelPosition } from '../types/shapes'
  import { BrushTool, EraserTool, FillTool, type Tool } from '../types/tools'
  import { Button, NumberInput, OverflowMenu, OverflowMenuItem, Slider } from 'carbon-components-svelte';
  import { ZoomIn, ZoomOut } from 'carbon-icons-svelte';

  export let file: LoadedFile
  export let animation: data.Animation
  export let frame: data.Frame
  export let layer: data.Layer
  export let refresh: {}

  export let checkerboardSize: number = 8

  let offsetX: number
  let offsetY: number
  let zoom: number = 1.0

  let mouseX: number = 0
  let mouseY: number = 0
  let mousePixelX: number = 0
  let mousePixelY: number = 0
  
  export let primaryColorIndex: number
  export let secondaryColorIndex: number
  export let brushSize: number

  let rootCanvas: HTMLCanvasElement
  let overlayCanvas: HTMLCanvasElement = document.createElement('canvas')
  let canvas: HTMLCanvasElement = document.createElement('canvas')

  let overlayDirty: boolean = true
  let canvasDirty: boolean = true
  
  let traversedPixels: Set<number> = new Set()
  function addTraversedPixels(pixels: PixelPosition[]) {
    for (let p of pixels) {
      traversedPixels.add(p.x+p.y*file.canvas.width)
    }
  }
  
  function zoomIn() {
    zoom *= 2
    if (zoom > 1) {
      zoom = Math.round(zoom)
    }

    capOffset()
    canvasDirty = true
    overlayDirty = true
  }
  function zoomOut() {
    zoom /= 2
    if (zoom > 1) {
      zoom = Math.round(zoom)
    }
    capOffset()
    canvasDirty = true
    overlayDirty = true
  }

  // check resizes canvases, etc.
  function check() {
    let ctx = rootCanvas.getContext('2d')
    if (!ctx) return
    let computedSize = getComputedStyle(rootCanvas)
    if (rootCanvas.width !== parseInt(computedSize.width) || rootCanvas.height !== parseInt(computedSize.height)) {
      rootCanvas.width = parseInt(computedSize.width)
      rootCanvas.height = parseInt(computedSize.height)
      canvas.width = rootCanvas.width
      canvas.height = rootCanvas.height
      overlayCanvas.width = rootCanvas.width
      overlayCanvas.height = rootCanvas.height
      overlayDirty = true
      canvasDirty = true
    }
    if (offsetX === undefined || offsetY === undefined) {
      // Adjust offset to center image on first LOAD.
      offsetX = rootCanvas.width/2 - file.canvas.width/2
      offsetY = rootCanvas.height/2 - file.canvas.height/2
    }
  }

  function draw() {
    if (!rootCanvas) return
    check()
    if (canvasDirty) {
      drawCanvas()
      canvasDirty = false
    }
    if (overlayDirty) {
      drawOverlay()
      overlayDirty = false
    }
    let ctx = rootCanvas.getContext('2d')
    if (!ctx) return
    ctx.clearRect(0, 0, rootCanvas.width, rootCanvas.height)
    ctx.drawImage(canvas, 0, 0)

    // Draw the actual canvas image.
    ctx.save()
    ctx.imageSmoothingEnabled = false
    ctx.scale(zoom, zoom)
    ctx.drawImage(file.canvas.canvas, offsetX, offsetY)
    ctx.restore()

    ctx.drawImage(overlayCanvas, 0, 0)
  }

  function drawCanvas() {
    let ctx = canvas.getContext('2d')
    if (!ctx) return

    ctx.fillStyle = '#111111'
    ctx.fillRect(0, 0, canvas.width, canvas.height)

    // Draw checkboard.
    ctx.save()
    ctx.imageSmoothingEnabled = false
    ctx.scale(zoom, zoom)
    {
      ctx.beginPath()
      ctx.fillStyle = '#888888'
      ctx.rect(offsetX, offsetY, file.canvas.width, file.canvas.height)
      ctx.fill()

      let rows = file.canvas.height / checkerboardSize
      let cols = file.canvas.width / checkerboardSize
      ctx.beginPath()
      ctx.fillStyle = '#444444'
      for (let r = 0; r < rows; r++) {
        for (let c = 0; c < cols; c++) {
          if (r % 2 === 0 && c % 2 === 1 || r % 2 === 1 && c % 2 === 0) {
            ctx.rect(
              offsetX+c * checkerboardSize,
              offsetY+r * checkerboardSize,
              checkerboardSize,
              checkerboardSize,
            )
          }
        }
      }
      ctx.fill()
    }
    ctx.restore()
  }

  function drawOverlay() {
    if (!overlayCanvas) return
    let ctx = overlayCanvas.getContext('2d')
    if (!ctx) return
    ctx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height)
    ctx.save()
    ctx.translate(0.5, 0.5)
    // Draw a cursor.
    {
      ctx.beginPath()
      ctx.strokeStyle = '#ff0000'
      ctx.lineWidth = 1

      if (zoom > 1) {
        ctx.rect(offsetX*zoom+mousePixelX*zoom, offsetY*zoom+mousePixelY*zoom, 1*zoom, 1*zoom)
      }
      if (zoom <= 1 || zoom > 4) {
        ctx.rect(mouseX, mouseY, 1, 1)
      }
      ctx.stroke()
    }
    ctx.restore()
  }

  function capOffset() {
    if (offsetX < -file.canvas.width+30) {
      offsetX = -file.canvas.width+30
    } else if (offsetX > canvas.width-30) {
      offsetX = canvas.width-30
    }
    if (offsetY < -file.canvas.height+30) {
      offsetY = -file.canvas.height+30
    } else if (offsetY > canvas.height-30) {
      offsetY = canvas.height-30
    }
  }
  
  export let currentTool: Tool
  
  function canvasMousedown(node) {
    let buttons: Set<number> = new Set()
    let x: number = 0
    let y: number = 0

    node.addEventListener('mouseenter', (e: MouseEvent) => {
      // hide cursor
      document.body.style.cursor = 'none'
    })
    node.addEventListener('mouseleave', (e: MouseEvent) => {
      // show cursor
      document.body.style.cursor = 'default'
    })

    node.addEventListener('mousedown', (e: MouseEvent) => {
      buttons.add(e.button)
      x = e.clientX
      y = e.clientY
      
      if (e.button === 0) {
        if (currentTool instanceof BrushTool) {
          currentTool.pointerDown({file, brushSize, colorIndex: primaryColorIndex}, {x: mousePixelX, y: mousePixelY, id: e.button })
        } else if (currentTool instanceof EraserTool) {
          currentTool.pointerDown({file, brushSize}, {x: mousePixelX, y: mousePixelY, id: e.button })
        } else if (currentTool instanceof FillTool) {
          currentTool.pointerDown({file, colorIndex: primaryColorIndex}, {x: mousePixelX, y: mousePixelY, id: e.button })
        } else {
          currentTool.pointerDown({file}, {x: mousePixelX, y: mousePixelY, id: e.button })
        }
      }
    })

    node.addEventListener('wheel', (e: WheelEvent) => {
      if (e.ctrlKey) {
        e.preventDefault()
        if (e.deltaY < 0) {
          zoomIn()
        } else if (e.deltaY > 0) {
          zoomOut()
        }
        return
      }
      if (e.deltaY < 0) {
        if (e.shiftKey) {
          offsetX--
        } else {
          offsetY--
        }
      } else if (e.deltaY > 0) {
        if (e.shiftKey) {
          offsetX++
        } else {
          offsetY++
        }
      }
      capOffset()
      canvasDirty = true
      overlayDirty = true
    })

    window.addEventListener('mousemove', (e: MouseEvent) => {
      if (!canvas) return
      // Get mouse position relative to canvas.
      {
        let rect = canvas.getBoundingClientRect()
        mouseX = e.offsetX - rect.left
        mouseY = e.offsetY - rect.top
        mousePixelX = Math.floor(mouseX / zoom - offsetX)
        mousePixelY = Math.floor(mouseY / zoom - offsetY)
        overlayDirty = true
      }

      if (buttons.size === 0) return
      let dx = e.clientX - x
      let dy = e.clientY - y
      x = e.clientX
      y = e.clientY

      if (buttons.has(0)) {
        if (currentTool.isActive()) {
          if (currentTool instanceof BrushTool) {
            currentTool.pointerMove({file, brushSize, colorIndex: primaryColorIndex}, {x: mousePixelX, y: mousePixelY, id: 0 })
          } else if (currentTool instanceof EraserTool) {
            currentTool.pointerMove({file, brushSize}, {x: mousePixelX, y: mousePixelY, id: 0 })
          } else if (currentTool instanceof FillTool) {
            currentTool.pointerMove({file, colorIndex: primaryColorIndex}, {x: mousePixelX, y: mousePixelY, id: 0 })
          } else {
            currentTool.pointerMove({file}, {x: mousePixelX, y: mousePixelY, id: 0 })
          }
        }
      }
      if (buttons.has(1)) {
        offsetX += dx / zoom
        offsetY += dy / zoom
        capOffset()
        canvasDirty = true
        overlayDirty = true
      }
      if (buttons.has(2)) {
        console.log('2')
      }
    })

    window.addEventListener('mouseup', (e: MouseEvent) => {
      if (buttons.size === 0) return

      if (e.button === 0) {
        if (currentTool.isActive()) {
          currentTool.pointerUp({file}, {x: mousePixelX, y: mousePixelY, id: 0 })
        }
      }

      buttons.delete(e.button)

    })
  }
  
  onMount(() => {
    let frameID: number = 0
    let frameDraw = () => {
      draw()
      frameID = window.requestAnimationFrame(frameDraw)
    }
    frameID = window.requestAnimationFrame(frameDraw)
    return () => {
      window.cancelAnimationFrame(frameID)
    }
  })
</script>

<main>
  <canvas bind:this={rootCanvas} use:canvasMousedown on:contextmenu={(e)=>e.preventDefault()}></canvas>
  <menu>
    <NumberInput
      min={0}
      max={10}
      step={1}
      bind:value={zoom}
      size="sm"
      hideSteppers
    />
    <Button
      on:click={zoomIn}
      kind="ghost"
      size="small"
      icon={ZoomIn}
      iconDescription="Zoom In"
      tooltipPosition="top"
      tooltipAlignment="end"
    />
    <Button
      on:click={zoomOut}
      kind="ghost"
      size="small"
      icon={ZoomOut}
      iconDescription="Zoom Out"
      tooltipPosition="top"
      tooltipAlignment="end"
    />
  </menu>
</main>

<style>
  main {
    width: 100%;
    height: 100%;
    overflow: hidden;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto;
  }
  canvas {
    width: 100%;
    height: 100%;
    image-rendering: pixelated;
  }
  menu {
    display: flex;
    flex-direction: row;
    justify-content: flex-end;
  }
  menu > .bx--form-item {
    flex: initial;
  }
</style>