<script setup lang="ts">
import { watch, computed } from 'vue'
import * as THREE from 'three'
import { labelTypeRegistry, type LabelDataType } from '../config/sceneConfig'
import shuiliangIcon from '../assets/icon/shuiliang.png'
import yingliIcon from '../assets/icon/yingli.png'

const props = defineProps<{
  labelId: string
  name: string
  type: LabelDataType
  positionX: number
  positionY: number
  positionZ: number
  value: number
}>()

const iconMap: Record<string, string> = {
  'shuiliang.png': shuiliangIcon,
  'yingli.png': yingliIcon,
}

const displayConfig = computed(() => labelTypeRegistry[props.type])

let scene: THREE.Scene | null = null
let camera: THREE.PerspectiveCamera | null = null
let textSprite: THREE.Sprite | null = null
let spriteTexture: THREE.CanvasTexture | null = null
let tooltipSprite: THREE.Sprite | null = null
let tooltipTexture: THREE.CanvasTexture | null = null
let iconReady = false
let spriteCreated = false
let isHovered = false

const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')!

const ICON_W = 100
const ICON_H = 100
const CANVAS_SCALE = 2
canvas.width = ICON_W * CANVAS_SCALE
canvas.height = ICON_H * CANVAS_SCALE

const iconImg = new Image()
function loadIcon() {
  const iconPath = displayConfig.value.icon
  const resolvedIcon = iconMap[iconPath]
  if (resolvedIcon) {
    iconImg.src = resolvedIcon
  } else {
    iconImg.src = shuiliangIcon
  }
}
loadIcon()

iconImg.onload = () => {
  iconReady = true
  if (!spriteCreated) {
    ensureSprite()
  } else {
    redrawTexture()
  }
}

function redrawTexture() {
  ctx.setTransform(1, 0, 0, 1, 0, 0)
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  ctx.drawImage(iconImg, 0, 0, canvas.width, canvas.height)
  ctx.setTransform(1, 0, 0, 1, 0, 0)
  drawText()

  if (spriteTexture) {
    spriteTexture.needsUpdate = true
  }
}

function drawText() {
  const cfg = displayConfig.value
  ctx.scale(CANVAS_SCALE, CANVAS_SCALE)
  ctx.textAlign = 'left'
  ctx.textBaseline = 'top'
  ctx.font = 'bold 20px "Microsoft YaHei", Arial, sans-serif'
  ctx.fillStyle = '#ffffff'
  ctx.fillText(`${props.value.toFixed(cfg.decimalPlaces)}`, 37, 16)
  ctx.font = '12px "Microsoft YaHei", Arial, sans-serif'
  ctx.fillStyle = 'rgba(255,255,255,0.8)'
  ctx.fillText(cfg.label, 37, 39)
  ctx.setTransform(1, 0, 0, 1, 0, 0)
}

function ensureSprite() {
  if (!scene || spriteCreated) return

  if (!iconReady) {
    ctx.setTransform(1, 0, 0, 1, 0, 0)
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    ctx.setTransform(1, 0, 0, 1, 0, 0)
    drawText()
  } else {
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    ctx.drawImage(iconImg, 0, 0, canvas.width, canvas.height)
    drawText()
  }

  spriteTexture = new THREE.CanvasTexture(canvas)
  spriteTexture.colorSpace = THREE.SRGBColorSpace
  spriteTexture.needsUpdate = true

  const material = new THREE.SpriteMaterial({
    map: spriteTexture,
    transparent: true,
    depthTest: false,
    depthWrite: false,
    sizeAttenuation: true,
  })

  textSprite = new THREE.Sprite(material)
  textSprite.position.set(props.positionX, props.positionY + 2.5 / 2, props.positionZ)
  textSprite.scale.set(3.0, 2.5, 1)
  textSprite.renderOrder = 999
  textSprite.name = `label_${props.labelId}`
  scene.add(textSprite)

  createTooltip()

  spriteCreated = true
}

function createTooltip() {
  const tooltipCanvas = document.createElement('canvas')
  const tooltipCtx = tooltipCanvas.getContext('2d')!
  const padding = 12
  const fontSize = 16
  tooltipCtx.font = `bold ${fontSize}px "Microsoft YaHei", Arial, sans-serif`
  const textWidth = tooltipCtx.measureText(props.name).width
  const tw = textWidth + padding * 2
  const th = fontSize + padding * 2
  tooltipCanvas.width = tw * CANVAS_SCALE
  tooltipCanvas.height = th * CANVAS_SCALE

  tooltipCtx.scale(CANVAS_SCALE, CANVAS_SCALE)
  tooltipCtx.clearRect(0, 0, tw, th)

  const radius = 6
  tooltipCtx.beginPath()
  tooltipCtx.moveTo(radius, 0)
  tooltipCtx.lineTo(tw - radius, 0)
  tooltipCtx.quadraticCurveTo(tw, 0, tw, radius)
  tooltipCtx.lineTo(tw, th - radius)
  tooltipCtx.quadraticCurveTo(tw, th, tw - radius, th)
  tooltipCtx.lineTo(radius, th)
  tooltipCtx.quadraticCurveTo(0, th, 0, th - radius)
  tooltipCtx.lineTo(0, radius)
  tooltipCtx.quadraticCurveTo(0, 0, radius, 0)
  tooltipCtx.closePath()

  tooltipCtx.fillStyle = 'rgba(0, 0, 0, 0.85)'
  tooltipCtx.fill()
  tooltipCtx.strokeStyle = 'rgba(255, 255, 255, 0.3)'
  tooltipCtx.lineWidth = 1
  tooltipCtx.stroke()

  tooltipCtx.textAlign = 'center'
  tooltipCtx.textBaseline = 'middle'
  tooltipCtx.font = `bold ${fontSize}px "Microsoft YaHei", Arial, sans-serif`
  tooltipCtx.fillStyle = '#ffffff'
  tooltipCtx.fillText(props.name, tw / 2, th / 2)

  tooltipTexture = new THREE.CanvasTexture(tooltipCanvas)
  tooltipTexture.colorSpace = THREE.SRGBColorSpace
  tooltipTexture.needsUpdate = true

  const tooltipMaterial = new THREE.SpriteMaterial({
    map: tooltipTexture,
    transparent: true,
    depthTest: false,
    depthWrite: false,
    sizeAttenuation: true,
    opacity: 0,
  })

  tooltipSprite = new THREE.Sprite(tooltipMaterial)
  tooltipSprite.position.set(props.positionX, props.positionY + 2.5 / 2, props.positionZ)
  tooltipSprite.scale.set(0, 0, 1)
  tooltipSprite.renderOrder = 1000
  tooltipSprite.name = `tooltip_${props.labelId}`
  if (scene) scene.add(tooltipSprite)
}

function init(s: THREE.Scene, c: THREE.PerspectiveCamera) {
  scene = s
  camera = c
  if (iconReady) {
    ensureSprite()
  } else {
    iconImg.onload = () => {
      iconReady = true
      ensureSprite()
    }
  }
}

function tick() {
  if (!textSprite || !camera) return
  const dist = camera.position.distanceTo(textSprite.position)
  const refDist = 50.0
  let s = dist / refDist
  const minScale = 2.0
  const maxScale = 20.0
  s = Math.max(minScale, Math.min(maxScale, s))
  textSprite.scale.set(s * 3.0, s * 2.2, 1)
  textSprite.position.y = props.positionY + (s * 2.2) / 2

  if (tooltipSprite) {
    const tooltipScaleH = s * 1.0
    const img = tooltipSprite.material.map!.image as HTMLImageElement
    const tooltipScaleW = tooltipScaleH * (img.width / img.height) * (ICON_H / ICON_W)
    if (isHovered) {
      tooltipSprite.scale.set(tooltipScaleW, tooltipScaleH, 1)
      tooltipSprite.position.y = props.positionY + (s * 2.2) + tooltipScaleH / 2 + 0.3
    } else {
      tooltipSprite.scale.set(0, 0, 1)
    }
  }
}

function dispose() {
  if (textSprite && scene) {
    scene.remove(textSprite)
    textSprite.material.map?.dispose()
    textSprite.material.dispose()
    textSprite = null
  }
  if (tooltipSprite && scene) {
    scene.remove(tooltipSprite)
    tooltipSprite.material.map?.dispose()
    tooltipSprite.material.dispose()
    tooltipSprite = null
  }
  if (spriteTexture) {
    spriteTexture = null
  }
  if (tooltipTexture) {
    tooltipTexture = null
  }
  spriteCreated = false
  iconReady = false
  isHovered = false
}

watch(() => props.value, () => {
  if (spriteCreated) {
    redrawTexture()
  }
})

function setHovered(hovered: boolean) {
  isHovered = hovered
  if (tooltipSprite) {
    const mat = tooltipSprite.material as THREE.SpriteMaterial
    mat.opacity = hovered ? 1 : 0
  }
}

function getSprite(): THREE.Sprite | null {
  return textSprite
}

function getPosition(): { x: number; y: number; z: number } {
  return { x: props.positionX, y: props.positionY, z: props.positionZ }
}

defineExpose({ init, tick, dispose, setHovered, getSprite, getPosition })
</script>

<template>
</template>
