<template>
  <Image v-for="tile in mapEditor.currentMap.value?.mapEventTiles" v-bind="getImageProps(tile)" />
</template>

<script setup lang="ts">

import { MapEventTileType, type MapEventTile, type Map as MapT, type UUID } from '@/application/types'
import { uuidv4 } from '@/application/utilities'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { cloneArray, getTile, tileToWorldX, tileToWorldY } from '@/services/mapService'
import { Image } from 'phavuer'
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
}>()


// *** COMMAND STATE ***

let currentCommand: EventTileCommand | null = null

class EventTileCommand implements EditorCommand {
  public operation: 'draw' | 'erase' | 'clear' = 'draw'
  public type: 'event_tile' = 'event_tile'
  public affectedTiles: MapEventTile[] = []

  apply(elements: MapEventTile[]) {
    let tileVersion = cloneArray(elements) as MapEventTile[]
    if (this.operation === 'draw') {
      tileVersion = tileVersion.concat(this.affectedTiles)
    }
    else if (this.operation === 'erase') {
      tileVersion = tileVersion.filter((v) => !this.affectedTiles.includes(v))
    }
    else if (this.operation === 'clear') {
      tileVersion = []
    }
    return tileVersion
  }

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

function createCommandUpdate(tile?: MapEventTile, operation: 'draw' | 'erase' | 'clear') {
  if (!currentCommand) {
    currentCommand = new EventTileCommand(operation)
  }

  //If position is already in, do not proceed
  for (const priorTile of currentCommand.affectedTiles) {
    if (priorTile.positionX === tile.positionX && priorTile.positionY == tile.positionY) return
  }

  currentCommand.affectedTiles.push(tile)
}

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


// *** HANDLERS ***


function getImageProps(tile: MapEventTile) {
  return {
    x: tileToWorldX(props.tileMap, tile.positionX, tile.positionY),
    y: tileToWorldY(props.tileMap, tile.positionX, tile.positionY),
    texture: tile.type,
    depth: 999
  }
}

function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
  // Check if there is a tile
  const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
  if (!tile) return

  // Check if event tile already exists on position
  const existingEventTile = map.mapEventTiles.find((eventTile) => eventTile.positionX === tile.x && eventTile.positionY === tile.y)
  if (existingEventTile) return

  // If teleport, check if there is a selected map
  if (mapEditor.drawMode.value === 'teleport' && !mapEditor.teleportSettings.value.toMapId) return

  const newEventTile = {
    id: uuidv4() as UUID,
    mapId: map.id,
    map: map,
    type: mapEditor.drawMode.value === 'blocking tile' ? MapEventTileType.BLOCK : MapEventTileType.TELEPORT,
    positionX: tile.x,
    positionY: tile.y,
    teleport:
      mapEditor.drawMode.value === 'teleport'
        ? {
            toMap: mapEditor.teleportSettings.value.toMapId,
            toPositionX: mapEditor.teleportSettings.value.toPositionX,
            toPositionY: mapEditor.teleportSettings.value.toPositionY,
            toRotation: mapEditor.teleportSettings.value.toRotation
          }
        : undefined
  }

  createCommandUpdate(newEventTile, 'draw')

  map.mapEventTiles.push(newEventTile)
}

function erase(pointer: Phaser.Input.Pointer, map: MapT) {
  // Check if there is a tile
  const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
  if (!tile) return

  // Check if event tile already exists on position
  const existingEventTile = map.mapEventTiles.find((eventTile) => eventTile.positionX === tile.x && eventTile.positionY === tile.y)
  if (!existingEventTile) return

  if (mapEditor.drawMode.value !== existingEventTile.type.toLowerCase()) {
    if (mapEditor.drawMode.value === 'blocking tile' && existingEventTile.type === MapEventTileType.BLOCK)
      null //skip this case
    else return
  }

  createCommandUpdate(existingEventTile, 'erase')

  // Remove existing event tile
  map.mapEventTiles = map.mapEventTiles.filter((eventTile) => eventTile.id !== existingEventTile.id)
}

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

  if (pointer.event.altKey) return

  switch (mapEditor.tool.value) {
    case 'pencil':
      pencil(pointer, map)
      break
    case 'eraser':
      erase(pointer, map)
      break
  }
}

function clearTiles() {
  if (mapEditor.currentMap.value.mapEventTiles.length === 0) return
  createCommandUpdate(null, 'clear')
  finalizeCommand()
}

</script>
