<template>
  <PlacedMapObject
    v-if="mapEditor.tool.value === 'pencil' && mapEditor.drawMode.value === 'map_object' && mapEditor.isPlacedMapObjectPreviewEnabled.value && mapEditor.selectedMapObject.value && previewPlacedMapObject"
    :tileMap
    :tileMapLayer
    :key="previewPlacedMapObject?.id"
    :placedMapObject="previewPlacedMapObject as PlacedMapObjectT"
  />
  <SelectedPlacedMapObjectComponent v-if="mapEditor.selectedPlacedObject.value" :key="mapEditor.selectedPlacedObject.value.id" :map :placedMapObject="mapEditor.selectedPlacedObject.value" @move="moveMapObject" @rotate="rotatePlacedMapObject" @delete="deletePlacedMapObject" />
  <PlacedMapObject v-for="placedMapObject in mapEditor.currentMap.value?.placedMapObjects" :tileMap :tileMapLayer :placedMapObject @pointerdown="clickPlacedMapObject(placedMapObject)" />
</template>

<script setup lang="ts">
import type { MapObject, Map as MapT, PlacedMapObject as PlacedMapObjectT } from '@/application/types'
import { uuidv4 } from '@/application/utilities'
import PlacedMapObject from '@/components/game/map/partials/PlacedMapObject.vue'
import SelectedPlacedMapObjectComponent from '@/components/gameMaster/mapEditor/partials/SelectedPlacedMapObject.vue'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { getTile } from '@/services/mapService'
import { useScene } from 'phavuer'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'

import Tilemap = Phaser.Tilemaps.Tilemap
import TilemapLayer = Phaser.Tilemaps.TilemapLayer

const scene = useScene()
const mapEditor = useMapEditorComposable()
const map = computed(() => mapEditor.currentMap.value!)

const emit = defineEmits<{ (e: 'update', map: MapT): void; (e: 'updateAndCommit', map: MapT): void; (e: 'pauseObjectTracking'): void; (e: 'resumeObjectTracking'): void }>()

defineExpose({ handlePointer })

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

const previewPosition = ref({ x: 0, y: 0 })
const previewPlacedMapObject = computed(() => ({
  id: mapEditor.selectedMapObject.value!.id,
  mapObject: mapEditor.selectedMapObject.value!.id,
  isRotated: false,
  positionX: previewPosition.value.x,
  positionY: previewPosition.value.y
}))

function updatePreviewPosition(pointer: Phaser.Input.Pointer) {
  const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
  if (!tile || (previewPosition.value.x === tile.x && previewPosition.value.y === tile.y)) return

  previewPosition.value = { x: tile.x, y: tile.y }
}

function pencil(pointer: Phaser.Input.Pointer, map: MapT) {
  emit('pauseObjectTracking')
  const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
  if (!tile) return

  // Check if object already exists on position
  const existingPlacedMapObject = findObjectByPointer(pointer, mapEditor.currentMap.value!)
  if (existingPlacedMapObject) return

  if (!mapEditor.selectedMapObject.value) return

  const newPlacedMapObject: PlacedMapObjectT = {
    id: uuidv4(),
    mapObject: mapEditor.selectedMapObject.value.id,
    isRotated: false,
    positionX: tile.x,
    positionY: tile.y
  }

  // Add new object to mapObjects
  mapEditor.selectedPlacedObject.value = newPlacedMapObject
  map.placedMapObjects.push(newPlacedMapObject)

  emit('update', map)
}

function eraser(pointer: Phaser.Input.Pointer, map: MapT) {
  emit('pauseObjectTracking')

  // Check if object already exists on position
  const existingPlacedMapObject = findObjectByPointer(pointer, map)
  if (!existingPlacedMapObject) return

  // Remove existing object
  map.placedMapObjects = map.placedMapObjects.filter((placedMapObject) => placedMapObject.id !== existingPlacedMapObject.id)

  emit('update', map)
}

function findObjectByPointer(pointer: Phaser.Input.Pointer, map: MapT): PlacedMapObjectT | undefined {
  const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
  if (!tile) return undefined

  return map.placedMapObjects.find((placedMapObject) => placedMapObject.positionX === tile.x && placedMapObject.positionY === tile.y)
}

function objectPicker(pointer: Phaser.Input.Pointer, map: MapT) {
  // Check if object already exists on position
  const existingPlacedMapObject = findObjectByPointer(pointer, map)
  if (!existingPlacedMapObject) return

  // Select the object
  mapEditor.setSelectedMapObject(existingPlacedMapObject.mapObject as MapObject)
}

function moveMapObject(id: string, map: MapT) {
  mapEditor.movingPlacedObject.value = map.placedMapObjects.find((object) => object.id === id) as PlacedMapObjectT

  emit('pauseObjectTracking')

  function handlePointerMove(pointer: Phaser.Input.Pointer) {
    if (!mapEditor.movingPlacedObject.value) return
    const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
    if (!tile) return

    mapEditor.movingPlacedObject.value.positionX = tile.x
    mapEditor.movingPlacedObject.value.positionY = tile.y
  }

  scene.input.on(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)
  scene.input.on(Phaser.Input.Events.POINTER_UP, handlePointerUp)

  function handlePointerUp(pointer: Phaser.Input.Pointer) {
    scene.input.off(Phaser.Input.Events.POINTER_MOVE, handlePointerMove)

    const tile = getTile(props.tileMap, pointer.worldX, pointer.worldY)
    if (!tile) return
    console.log(id)
    map.placedMapObjects.map((placed) => {
      if (placed.id === id) {
        placed.positionX = tile.x
        placed.positionY = tile.y
      }
    })

    mapEditor.movingPlacedObject.value = null

    scene.input.off(Phaser.Input.Events.POINTER_UP, handlePointerUp)
    emit('resumeObjectTracking')
    emit('updateAndCommit', map)
  }
}

function rotatePlacedMapObject(id: string, map: MapT) {
  map.placedMapObjects.map((placed) => {
    if (placed.id === id) {
      placed.isRotated = !placed.isRotated
    }
  })

  emit('updateAndCommit', map)
}

function deletePlacedMapObject(id: string, map: MapT) {
  map.placedMapObjects = map.placedMapObjects.filter((object) => object.id !== id)
  mapEditor.selectedPlacedObject.value = null
  emit('updateAndCommit', map)
}

function clickPlacedMapObject(placedMapObject: PlacedMapObjectT) {
  mapEditor.selectedPlacedObject.value = placedMapObject

  // If alt is pressed, select the object
  if (scene.input.activePointer.event.altKey) {
    mapEditor.setSelectedMapObject(placedMapObject.mapObject as MapObject)
  }
}

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

  // Check if alt is pressed, this means we are selecting the object
  if (pointer.event.altKey) return

  // Check if tool is pencil
  switch (mapEditor.tool.value) {
    case 'pencil':
      pencil(pointer, map)
      break
    case 'eraser':
      eraser(pointer, map)
      break
    case 'object picker':
      objectPicker(pointer, map)
      break
  }
}

onMounted(() => {
  scene.input.on(Phaser.Input.Events.POINTER_MOVE, updatePreviewPosition)
})

onUnmounted(() => {
  scene.input.off(Phaser.Input.Events.POINTER_MOVE, updatePreviewPosition)
})
</script>
