<template>
  <div class="absolute border-0 border-l-2 border-solid border-gray-500 w-1/4 min-w-80 flex flex-col top-0 right-0 z-10 h-dvh bg-gray-800" v-if="isOpen">
    <div class="relative z-10 p-2.5 border-solid border-0 border-b border-gray-500 text-right">
      <h3 class="text-lg text-white">Tiles</h3>
    </div>
    <div class="overflow-hidden grow relative">
      <div class="absolute top-0 left-0 h-full w-full">
        <div class="relative z-10 h-full">
          <div class="h-full" v-if="!selectedGroup">
            <div class="flex pt-4 pl-4">
              <div class="w-full flex gap-1.5 flex-row">
                <div>
                  <label class="mb-1.5 font-titles hidden" for="search">Search...</label>
                  <input @mousedown.stop class="input-field" type="text" name="search" placeholder="Search" v-model="searchQuery" />
                </div>
              </div>
            </div>
            <div class="flex flex-col h-[calc(100%_-_170px)] p-4 pb-24">
              <div class="mb-4 flex flex-wrap gap-2">
                <button v-for="tag in uniqueTags" :key="tag" @click="toggleTag(tag)" class="btn-cyan" :class="{ 'opacity-50': !selectedTags.includes(tag) }">
                  {{ tag }}
                </button>
              </div>
              <div class="h-full flex-grow overflow-y-auto">
                <div class="grid grid-cols-4 gap-2 justify-items-center">
                  <div v-for="group in groupedTiles" :key="group.parent.id" class="flex flex-col items-center justify-center relative">
                    <img
                      class="max-w-full max-h-full border-2 border-solid cursor-pointer transition-all duration-300"
                      :src="`${config.server_endpoint}/textures/tiles/${group.parent.id}.png`"
                      :alt="group.parent.name"
                      @click="openGroup(group)"
                      @load="() => processTile(group.parent)"
                      :class="{
                    'border-cyan shadow-lg scale-105': isActiveTile(group.parent),
                    'border-transparent hover:border-gray-300': !isActiveTile(group.parent)
                  }"
                    />
                    <span class="text-xs mt-1">{{ getTileCategory(group.parent) }}</span>
                    <span v-if="group.children.length > 0" class="absolute top-0 right-0 bg-cyan text-white rounded-full w-5 h-5 flex items-center justify-center text-xs">
                  {{ group.children.length + 1 }}
                </span>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div v-else class="h-full overflow-auto">
            <div class="p-4">
              <button @click="closeGroup" class="btn-cyan mb-4">Back to All Tiles</button>
              <h4 class="text-lg mb-4">{{ selectedGroup.parent.name }} Group</h4>
              <div class="grid grid-cols-8 gap-2 justify-items-center">
                <div class="flex flex-col items-center justify-center">
                  <img
                    class="max-w-full max-h-full border-2 border-solid cursor-pointer transition-all duration-300"
                    :src="`${config.server_endpoint}/textures/tiles/${selectedGroup.parent.id}.png`"
                    :alt="selectedGroup.parent.name"
                    @click="selectTile(selectedGroup.parent.id)"
                    :class="{
                  'border-cyan shadow-lg scale-105': isActiveTile(selectedGroup.parent),
                  'border-transparent hover:border-gray-300': !isActiveTile(selectedGroup.parent)
                }"
                  />
                  <span class="text-xs mt-1">{{ getTileCategory(selectedGroup.parent) }}</span>
                </div>
                <div v-for="childTile in selectedGroup.children" :key="childTile.id" class="flex flex-col items-center justify-center">
                  <img
                    class="max-w-full max-h-full border-2 border-solid cursor-pointer transition-all duration-300"
                    :src="`${config.server_endpoint}/textures/tiles/${childTile.id}.png`"
                    :alt="childTile.name"
                    @click="selectTile(childTile.id)"
                    :class="{
                  'border-cyan shadow-lg scale-105': isActiveTile(childTile),
                  'border-transparent hover:border-gray-300': !isActiveTile(childTile)
                }"
                  />
                  <span class="text-xs mt-1">{{ getTileCategory(childTile) }}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import config from '@/application/config'
import type { Tile } from '@/application/types'
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
import { TileStorage } from '@/storage/storages'
import { liveQuery } from 'dexie'
import { computed, onMounted, onUnmounted, ref } from 'vue'

const isOpen = ref(false)
const tileStorage = new TileStorage()
const mapEditor = useMapEditorComposable()
const searchQuery = ref('')
const selectedTags = ref<string[]>([])
const tileCategories = ref<Map<string, string>>(new Map())
const selectedGroup = ref<{ parent: Tile; children: Tile[] } | null>(null)
const tiles = ref<Tile[]>([])

defineExpose({
  open: () => (isOpen.value = true),
  close: () => (isOpen.value = false),
  toggle: () => (isOpen.value = !isOpen.value)
})

const uniqueTags = computed(() => {
  const allTags = tiles.value.flatMap((tile) => tile.tags || [])
  return Array.from(new Set(allTags))
})

const groupedTiles = computed(() => {
  const groups: { parent: Tile; children: Tile[] }[] = []
  const filteredTiles = tiles.value.filter((tile) => {
    const matchesSearch = !searchQuery.value || tile.name.toLowerCase().includes(searchQuery.value.toLowerCase())
    const matchesTags = selectedTags.value.length === 0 || (tile.tags && selectedTags.value.some((tag) => tile.tags.includes(tag)))
    return matchesSearch && matchesTags
  })

  filteredTiles.forEach((tile) => {
    const parentGroup = groups.find((group) => areTilesRelated(group.parent, tile))
    if (parentGroup && parentGroup.parent.id !== tile.id) {
      parentGroup.children.push(tile)
    } else {
      groups.push({ parent: tile, children: [] })
    }
  })

  return groups
})

const tileColorData = ref<Map<string, { r: number; g: number; b: number }>>(new Map())
const tileEdgeData = ref<Map<string, number>>(new Map())

function areTilesRelated(tile1: Tile, tile2: Tile): boolean {
  const colorSimilarityThreshold = 30 // Adjust this value as needed
  const edgeComplexitySimilarityThreshold = 20 // Adjust this value as needed

  const color1 = tileColorData.value.get(tile1.id)
  const color2 = tileColorData.value.get(tile2.id)
  const edge1 = tileEdgeData.value.get(tile1.id)
  const edge2 = tileEdgeData.value.get(tile2.id)

  if (!color1 || !color2 || edge1 === undefined || edge2 === undefined) {
    return false
  }

  const colorDifference = Math.sqrt(Math.pow(color1.r - color2.r, 2) + Math.pow(color1.g - color2.g, 2) + Math.pow(color1.b - color2.b, 2))

  const edgeComplexityDifference = Math.abs(edge1 - edge2)

  const namePrefix1 = tile1.name.split('_')[0]
  const namePrefix2 = tile2.name.split('_')[0]

  return colorDifference <= colorSimilarityThreshold && edgeComplexityDifference <= edgeComplexitySimilarityThreshold && namePrefix1 === namePrefix2
}

const toggleTag = (tag: string) => {
  if (selectedTags.value.includes(tag)) {
    selectedTags.value = selectedTags.value.filter((t) => t !== tag)
  } else {
    selectedTags.value.push(tag)
  }
}

function processTile(tile: Tile) {
  const img = new Image()
  img.crossOrigin = 'Anonymous'
  img.onload = () => {
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    canvas.width = img.width
    canvas.height = img.height
    ctx!.drawImage(img, 0, 0, img.width, img.height)

    const imageData = ctx!.getImageData(0, 0, canvas.width, canvas.height)
    tileColorData.value.set(tile.id, getDominantColor(imageData))
    tileEdgeData.value.set(tile.id, getEdgeComplexity(imageData))
  }
  img.src = `${config.server_endpoint}/textures/tiles/${tile.id}.png`
}

function getDominantColor(imageData: ImageData) {
  let r = 0,
    g = 0,
    b = 0,
    total = 0

  for (let i = 0; i < imageData.data.length; i += 4) {
    if (imageData.data[i + 3] > 0) {
      // Only consider non-transparent pixels
      r += imageData.data[i]
      g += imageData.data[i + 1]
      b += imageData.data[i + 2]
      total++
    }
  }

  return {
    r: Math.round(r / total),
    g: Math.round(g / total),
    b: Math.round(b / total)
  }
}

function getEdgeComplexity(imageData: ImageData) {
  let edgePixels = 0
  for (let y = 0; y < imageData.height; y++) {
    for (let x = 0; x < imageData.width; x++) {
      const i = (y * imageData.width + x) * 4
      if (imageData.data[i + 3] > 0 && (x === 0 || y === 0 || x === imageData.width - 1 || y === imageData.height - 1 || imageData.data[i - 1] === 0 || imageData.data[i + 7] === 0)) {
        edgePixels++
      }
    }
  }
  return edgePixels
}

function getTileCategory(tile: Tile): string {
  return tileCategories.value.get(tile.id) || ''
}

function openGroup(group: { parent: Tile; children: Tile[] }) {
  selectedGroup.value = group
}

function closeGroup() {
  selectedGroup.value = null
}

function selectTile(tile: string) {
  mapEditor.setSelectedTile(tile)
}

function isActiveTile(tile: Tile): boolean {
  return mapEditor.selectedTile.value === tile.id
}

let subscription: any = null

onMounted(() => {
  subscription = liveQuery(() => tileStorage.liveQuery()).subscribe({
    next: (result) => {
      tiles.value = result
    },
    error: (error) => {
      console.error('Failed to fetch tiles:', error)
    }
  })
})

onUnmounted(() => {
  if (subscription) {
    subscription.unsubscribe()
  }
})
</script>