<template>
  <div
    class="pointer-events-auto absolute top-12 left-2 z-20 flex flex-col rounded-lg bg-backdrop/30"
    @pointerdown.stop
    @pointermove.stop
    @pointerup.stop
    @wheel.stop
  >
    <div class="relative">
      <Button
        ref="menuTriggerRef"
        variant="textonly"
        size="icon"
        :aria-label="$t('menu.showMenu')"
        class="rounded-full"
        @click="toggleMenu"
      >
        <i class="icon-[lucide--menu] text-lg text-base-foreground" />
      </Button>

      <div
        v-show="isMenuOpen"
        ref="menuPanelRef"
        class="absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg"
      >
        <div class="flex flex-col">
          <Button
            v-for="category in availableCategories"
            :key="category"
            variant="textonly"
            :class="
              cn(
                'flex w-full items-center justify-start',
                activeCategory === category && 'bg-button-active-surface'
              )
            "
            @click="selectCategory(category)"
          >
            <i :class="getCategoryIcon(category)" />
            <span class="whitespace-nowrap text-base-foreground">{{
              $t(categoryLabels[category])
            }}</span>
          </Button>
        </div>
      </div>
    </div>
    <div v-show="activeCategory" class="rounded-lg bg-smoke-700/30">
      <SceneControls
        v-if="showSceneControls"
        v-model:show-grid="sceneConfig!.showGrid"
        v-model:background-color="sceneConfig!.backgroundColor"
        v-model:background-image="sceneConfig!.backgroundImage"
        v-model:background-render-mode="sceneConfig!.backgroundRenderMode"
        v-model:fov="cameraConfig!.fov"
        :hdri-active="
          !!lightConfig?.hdri?.hdriPath && !!lightConfig?.hdri?.enabled
        "
        @update-background-image="handleBackgroundImageUpdate"
      />

      <ModelControls
        v-if="showModelControls"
        v-model:material-mode="modelConfig!.materialMode"
        v-model:up-direction="modelConfig!.upDirection"
        v-model:show-skeleton="modelConfig!.showSkeleton"
        :material-modes="materialModes"
        :has-skeleton="hasSkeleton"
      />

      <CameraControls
        v-if="showCameraControls"
        v-model:camera-type="cameraConfig!.cameraType"
        v-model:fov="cameraConfig!.fov"
      />

      <div v-if="showLightControls" class="flex flex-col">
        <LightControls
          v-model:light-intensity="lightConfig!.intensity"
          v-model:material-mode="modelConfig!.materialMode"
          v-model:hdri-config="lightConfig!.hdri"
        />

        <HDRIControls
          v-model:hdri-config="lightConfig!.hdri"
          :has-background-image="!!sceneConfig?.backgroundImage"
          @update-hdri-file="handleHDRIFileUpdate"
        />
      </div>

      <ExportControls
        v-if="showExportControls"
        @export-model="handleExportModel"
      />

      <GizmoControls
        v-if="showGizmoControls"
        v-model:gizmo-config="modelConfig!.gizmo"
        @toggle-gizmo="handleToggleGizmo"
        @set-gizmo-mode="handleSetGizmoMode"
        @reset-gizmo-transform="handleResetGizmoTransform"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'

import CameraControls from '@/components/load3d/controls/CameraControls.vue'
import { useDismissableOverlay } from '@/composables/useDismissableOverlay'
import ExportControls from '@/components/load3d/controls/ExportControls.vue'
import GizmoControls from '@/components/load3d/controls/GizmoControls.vue'
import HDRIControls from '@/components/load3d/controls/HDRIControls.vue'
import LightControls from '@/components/load3d/controls/LightControls.vue'
import ModelControls from '@/components/load3d/controls/ModelControls.vue'
import SceneControls from '@/components/load3d/controls/SceneControls.vue'
import Button from '@/components/ui/button/Button.vue'
import type {
  CameraConfig,
  GizmoMode,
  LightConfig,
  MaterialMode,
  ModelConfig,
  SceneConfig
} from '@/extensions/core/load3d/interfaces'
import { cn } from '@comfyorg/tailwind-utils'

const {
  canUseGizmo = true,
  canUseLighting = true,
  canExport = true,
  materialModes = ['original', 'normal', 'wireframe'],
  hasSkeleton = false
} = defineProps<{
  canUseGizmo?: boolean
  canUseLighting?: boolean
  canExport?: boolean
  materialModes?: readonly MaterialMode[]
  hasSkeleton?: boolean
}>()

const sceneConfig = defineModel<SceneConfig>('sceneConfig')
const modelConfig = defineModel<ModelConfig>('modelConfig')
const cameraConfig = defineModel<CameraConfig>('cameraConfig')
const lightConfig = defineModel<LightConfig>('lightConfig')

const isMenuOpen = ref(false)
const menuPanelRef = ref<HTMLElement | null>(null)
const menuTriggerRef = ref<InstanceType<typeof Button> | null>(null)

useDismissableOverlay({
  isOpen: isMenuOpen,
  getOverlayEl: () => menuPanelRef.value,
  getTriggerEl: () => menuTriggerRef.value?.$el ?? null,
  onDismiss: () => {
    isMenuOpen.value = false
  }
})
const activeCategory = ref<string>('scene')
const categoryLabels: Record<string, string> = {
  scene: 'load3d.scene',
  model: 'load3d.model',
  camera: 'load3d.camera',
  light: 'load3d.light',
  gizmo: 'load3d.gizmo.label',
  export: 'load3d.export'
}

const availableCategories = computed(() => {
  const categories = ['scene', 'model', 'camera']
  if (canUseLighting) categories.push('light')
  if (canUseGizmo) categories.push('gizmo')
  if (canExport) categories.push('export')
  return categories
})

watch(
  availableCategories,
  (categories) => {
    if (!categories.includes(activeCategory.value)) {
      activeCategory.value = 'scene'
    }
  },
  { immediate: true }
)

const showSceneControls = computed(
  () => activeCategory.value === 'scene' && !!sceneConfig.value
)
const showModelControls = computed(
  () => activeCategory.value === 'model' && !!modelConfig.value
)
const showCameraControls = computed(
  () => activeCategory.value === 'camera' && !!cameraConfig.value
)
const showLightControls = computed(
  () =>
    canUseLighting &&
    activeCategory.value === 'light' &&
    !!lightConfig.value &&
    !!modelConfig.value
)
const showExportControls = computed(
  () => canExport && activeCategory.value === 'export'
)
const showGizmoControls = computed(
  () => canUseGizmo && activeCategory.value === 'gizmo' && !!modelConfig.value
)

const toggleMenu = () => {
  isMenuOpen.value = !isMenuOpen.value
}

const selectCategory = (category: string) => {
  activeCategory.value = category
  isMenuOpen.value = false
}

const categoryIcons = {
  scene: 'icon-[lucide--image]',
  model: 'icon-[lucide--box]',
  camera: 'icon-[lucide--camera]',
  light: 'icon-[lucide--sun]',
  gizmo: 'icon-[lucide--move-3d]',
  export: 'icon-[lucide--download]'
} as const

const getCategoryIcon = (category: string) => {
  const icon =
    category in categoryIcons
      ? categoryIcons[category as keyof typeof categoryIcons]
      : 'icon-[lucide--circle]'
  return cn(icon, 'text-lg text-base-foreground')
}

const emit = defineEmits<{
  (e: 'updateBackgroundImage', file: File | null): void
  (e: 'exportModel', format: string): void
  (e: 'updateHdriFile', file: File | null): void
  (e: 'toggleGizmo', enabled: boolean): void
  (e: 'setGizmoMode', mode: GizmoMode): void
  (e: 'resetGizmoTransform'): void
}>()

const handleBackgroundImageUpdate = (file: File | null) => {
  emit('updateBackgroundImage', file)
}

const handleExportModel = (format: string) => {
  emit('exportModel', format)
}

const handleHDRIFileUpdate = (file: File | null) => {
  emit('updateHdriFile', file)
}

const handleToggleGizmo = (enabled: boolean) => {
  emit('toggleGizmo', enabled)
}

const handleSetGizmoMode = (mode: GizmoMode) => {
  emit('setGizmoMode', mode)
}

const handleResetGizmoTransform = () => {
  emit('resetGizmoTransform')
}
</script>
