<template>
  <Controls v-if="tileMapLayer" :layer="tileMapLayer" :depth="0" />
</template>

<script setup lang="ts">
import Controls from '@/components/utilities/Controls.vue'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { cloneArray, createTileArray, getTile, placeTile, placeTiles } from '@/services/mapService'
import { onMounted, ref, watch } from 'vue'
import { type EditorCommand } from '@/components/gameMaster/mapEditor/Map.vue'

const mapEditor = useMapEditorComposable()

defineExpose({ handlePointer, finalizeCommand, clearTiles })

const emit = defineEmits(['createCommand'])

const props = defineProps<{
  tileMap: Phaser.Tilemaps.Tilemap
  tileMapLayer: Phaser.Tilemaps.TilemapLayer
}>()

// *** COMMAND STATE ***

let currentCommand: TileCommand | null = null

class TileCommand implements EditorCommand {
  public operation: 'draw' | 'erase' | 'clear' = 'draw'
  public type: 'tile' = 'tile'
  public tileName: string = 'blank_tile'
  public affectedTiles: number[][] = []

  apply(elements: string[][]) {
    let tileVersion
    if (this.operation === 'clear') {
      tileVersion = createTileArray(props.tileMapLayer.width, props.tileMapLayer.height, 'blank_tile')
    }
    else {
      tileVersion = cloneArray(elements) as string[][]
      for (const position of this.affectedTiles) {
        tileVersion[position[1]][position[0]] = this.tileName
      }
    }
    return tileVersion
  }

  constructor(operation: 'draw' | 'erase' | 'clear', tileName: string) {
    this.operation = operation
    this.tileName = tileName
  }
}

function createCommandUpdate(x: number, y: number, tileName: string, operation: 'draw' | 'erase' | 'clear') {
  if (!currentCommand) {
    currentCommand = new TileCommand(operation, tileName)
  }

  //If position is already in, do not proceed
  for (const vec of currentCommand.affectedTiles) {
    if (vec[0] === x && vec[1] === y) return
  }

  currentCommand.affectedTiles.push([x, y])
}

function finalizeCommand() {
  if (!currentCommand) return
  emit('createCommand', currentCommand)
  currentCommand = null
}


// *** HANDLERS ***


function draw(pointer: Phaser.Input.Pointer, tileName: string) {
  let map = mapEditor.currentMap.value
  if (!map) return

  // Check if there is a tile
  const tile = getTile(props.tileMapLayer, pointer.worldX, pointer.worldY)
  if (!tile) return

  // Place tile
  placeTile(props.tileMap, props.tileMapLayer, tile.x, tile.y, tileName)

  createCommandUpdate(tile.x, tile.y, tileName, tileName === 'blank_tile' ? 'erase': 'draw')

  // Adjust mapEditorStore.map.tiles
  map.tiles[tile.y][tile.x] = tileName
}

function paint(pointer: Phaser.Input.Pointer) {
  let map = mapEditor.currentMap.value
  if (!map) return

  // Set new tileArray with selected tile
  const tileArray = createTileArray(props.tileMap.width, props.tileMap.height, mapEditor.selectedTile.value)
  placeTiles(props.tileMap, props.tileMapLayer, tileArray)

  // Adjust mapEditorStore.map.tiles
  map.tiles = tileArray
}

// When alt is pressed, and the pointer is down, select the tile that the pointer is over
function tilePicker(pointer: Phaser.Input.Pointer) {
  let map = mapEditor.currentMap.value
  if (!map) return

  // Check if there is a tile
  const tile = getTile(props.tileMapLayer, pointer.worldX, pointer.worldY)
  if (!tile) return

  // Select the tile
  mapEditor.setSelectedTile(map.tiles[tile.y][tile.x])
}

function handlePointer(pointer: Phaser.Input.Pointer) {
  // Check if left mouse button is pressed
  if (!pointer.isDown && pointer.button === 0) return

  // Check if shift is not pressed, this means we are moving the camera
  if (pointer.event.shiftKey) return

  // Check if alt is pressed
  if (pointer.event.altKey) {
    tilePicker(pointer)
    return
  }

  // Check if draw mode is tile
  switch (mapEditor.tool.value) {
    case 'pencil':
      draw(pointer, mapEditor.selectedTile.value!)
      break
    case 'eraser':
      draw(pointer, 'blank_tile')
      break
    case 'paint':
      paint(pointer)
      break
  }
}

// *** LIFECYCLE ***

function clearTiles() {
  const tileArray = createTileArray(props.tileMap.width, props.tileMap.height, 'blank_tile')
  placeTiles(props.tileMap, props.tileMapLayer, tileArray)
  createCommandUpdate(0,0,"blank_tile",'clear')
  finalizeCommand()
}

onMounted(async () => {
  if (!mapEditor.currentMap.value) return
  const mapState = mapEditor.currentMap.value

  placeTiles(props.tileMap, props.tileMapLayer, mapState.tiles)
})
</script>
