<template>

  <!-- <MapScale /> -->

  <div class="canvas" ref="canvas"></div>

  <Controller v-bind="provides" />

  <Compatible />

</template>

<script lang='ts' setup>

// import MapScale from '@/views/3DModel/components/SpaceCtrl/index.vue'

import * as THREE from 'three'

import { modelLoader } from './utils'
import { useTunnelStoreWithOut } from '@/store/modules/tunnel'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'

import { MODELS } from './static'
import { amtMount, amtStart } from './models/center'

import TunnelModel from './models/tunnel'
import CameraModel from './models/camera'

import Controller from './components/controller.vue'
import Compatible from './components/compatible.vue'

const app = getCurrentInstance()
const tunnelStore = useTunnelStoreWithOut()

const canvas = ref(null)
const models = {} // 模型库

const context = {
  scene: null, // 主场景
  camera: null, // 摄像机
  renderer: null, // 渲染器
  controls: null, // 控制器
  raycaster: null, // 投射器
  mouse: null // 鼠标
}

const elts = {
  tunnels: [], // 隧道
  cameras: [] // 隧道摄像机
}

// 非联动性数据供给
const provides = {

  getCars () {
    return elts.tunnels.map(item => {
      return item.cars
    }).flat()
  },

  getContext () {
    return context
  },

  getTunnles () {
    return elts.tunnels
  }

}

// 模型加载
const loadModels = async () => {

  const arr = MODELS.map(item => {
    return modelLoader(item.url)
  })

  const res = await Promise.all(arr)

  res.forEach((e, x) => {
    models[MODELS[x].key] = e
  })

}

// 交互构建
const createInteract = (targets, glbkey, call) => {

  if (!context.raycaster) {
    context.raycaster = new THREE.Raycaster()
    context.mouse = new THREE.Vector2()
  }

  const getTarget = (el) => {
    if (el.type === 'Group' && el.userData.type === glbkey) {
      return el.userData
    } else if (el.parent) {
      return getTarget(el.parent)
    } else {
      return false
    }
  }

  const onClick = (e) => {
    context.mouse.x = (e.clientX / window.innerWidth) * 2 - 1
    context.mouse.y = (e.clientY / window.innerHeight) * -1 * 2 + 1
    context.raycaster.setFromCamera(context.mouse, context.camera)

    const intersects = context.raycaster.intersectObjects(targets, true)

    if (intersects.length) {
      const xCamera = getTarget(intersects[0].object)
      xCamera && call(xCamera)
    }
  }

  return onClick

}

// 交互初始化（集）
const interactInit = () => {


  // 摄像机交互
  const onCameraClick = createInteract(
    elts.cameras.map(item => item.camera),
    'glb-camera',
    (res) => {
      app?.proxy?.$openVideo(res.infos)
    }
  )

  window.addEventListener('click', onCameraClick, false)

}

// 隧道模型构建
const createTunnels = async () => {

  // 视觉
  context.scene.add(models.tunnel.scene)

  await tunnelStore.waitTunnel()

  // 数据
  const getTunnel = (x) => {

    const tunnel = [
      tunnelStore.getUpTunnel,
      tunnelStore.getDownTunnel
    ][x]

    // 隧道模型构建
    return new TunnelModel(
      context, models, {
        miles: tunnel.long,
        start: tunnel.start,
        end: tunnel.end,
        mode: 'online',
        direct: x
      }
    )

  }

  elts.tunnels = [
    getTunnel(0), getTunnel(1)
  ]

  // 开启车流
  elts.tunnels[0].startFlows()

}

// 摄像机挂载
const mountCameras = () => {

  elts.tunnels.forEach((item, x) => {

    const tunnel = [
      tunnelStore.getUpTunnel,
      tunnelStore.getDownTunnel
    ][x]

    const cameras = tunnel.cameras.map(icamera => {

      return new CameraModel(context, {
        infos: {
          ...icamera,
          direct: item.direct,
          progress: icamera.distance / tunnel.long
        },
        model: models.camera,
        curve: item.curves[1 - item.direct]
      })

    })

    elts.cameras = [elts.cameras, cameras].flat()

  })

}

// 上下文环境构建
const creatContext = () => {

  const scene = new THREE.Scene()
  scene.background = new THREE.Color('rgba(31, 80, 128, 1)')

  const { clientWidth: w, clientHeight: h } = canvas.value
  const camera =  new THREE.PerspectiveCamera(
    75,
    w / h,
    0.1,
    1000
  )
  camera.position.set(-10, 10, 0)
  camera.lookAt(20, 0, 0)
  scene.add(camera)

  const renderer = new THREE.WebGLRenderer()
  canvas.value.appendChild(renderer.domElement)

  // const controls = new OrbitControls(camera, renderer.domElement)
  // controls.target = new THREE.Vector3(100, 10, 0)

  context.scene = scene
  context.camera = camera
  context.renderer = renderer
  // context.controls = controls

}

// 3d 基础环境构建
const createEnviroment = async () => {

  // 点光源
  const light = new THREE.PointLight(0xffffff, 5, 0, 0)
  light.position.set(-50, 50, -50)
  context.scene.add(light)

  // 模型加载
  await loadModels()

  // 隧道构建
  await createTunnels()

  // 摄像机挂载
  mountCameras()

  // 初始化交互
  interactInit()
  

}

// 分辨率自适应
const autoSize = () => {
  const { clientWidth: w, clientHeight: h } = canvas.value
  const { clientWidth: ow, clientHeight: oh } = context.renderer.domElement

  if (w !== ow || h !== oh) {
    context.renderer.setSize(w, h, false)
    context.camera.aspect = w / h
    context.camera.updateProjectionMatrix()
  }
}

// 执行帧挂载集
const engineStart = () => {

  // 尺寸自适应挂载
  amtMount(autoSize)

  // 控制器挂载
  amtMount(() => {
    context.controls && context.controls.update()
  })

  // 场景更新挂载
  amtMount(() => {
    context.renderer.render(context.scene, context.camera)
  })

  // 启动
  amtStart()

}

onMounted(async () => {

  creatContext()
  engineStart()
  createEnviroment()

})

</script>

<style lang='scss' scoped>

.canvas {
  width: 100vw;
  height: 100vh;
  :deep(canvas) {
    height: 100%;
  }
}

</style>