<template>

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

<script lang='ts' setup>

import * as THREE from 'three'

import { modelLoader } from './utils'
import { useTunnelStoreWithOut } from '@/store/modules/tunnel'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js'

import { MODELS } from './static'
import { newCamera, modelPlay } from './utils'
import { amtMount, amtStart, amtClean } from './models/center'

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

import { useConnectCall, controlsClean } from './controller'
import { useController } from './controller/_index'
import { POINT_START, POINT_END, POINT_SCANE } from './points'

import runTest from '../../test'

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

const canvas = ref(null)

let models = {} // 模型库

let interactClean = null // 交互清除 key

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

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

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

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

  getContext () {
    return context
  },

  getTunnles () {
    return elts.tunnels
  },

  getModel (key) {
    return models[key]
  },

  getCameras () {
    return elts.cameras
  }

}

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

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

  const res = await Promise.all(arr)

  // console.log(res)

  res.forEach((e, x) => {
    if (['bus', 'car', 'track', 'tanker'].includes(MODELS[x].key)) {

      if (MODELS[x].key === 'tanker') {
        e.scene.children[0].rotation.z = Math.PI / 2
        e.scene.children[0].position.copy(new THREE.Vector3(0, 0, 2))
      }

      if (MODELS[x].key === 'bus') {
        e.scene.children[0].rotation.z = Math.PI / 2
        e.scene.children[0].position.copy(new THREE.Vector3(0, 0, 0))
      }

      if (MODELS[x].key === 'car') {
        e.scene.children[0].rotation.y = - Math.PI / 2
        e.scene.children[0].position.copy(new THREE.Vector3(0, 0, 0))
      }

      if (MODELS[x].key === 'track') {
        e.scene.children[0].rotation.y = - Math.PI / 2
        e.scene.children[0].position.copy(new THREE.Vector3(0, 0, 0))
      }
      
    }
    
    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)

  interactClean = () => {
    window.removeEventListener('click', onCameraClick)
  }


}

// 隧道模型构建
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()

  // 开启事件流
  elts.tunnels[0].startEventFlows()

}

// 摄像机挂载
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[item.direct]
      })

    })

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

  })

}

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

  const scene = new THREE.Scene()
  scene.background = null

  const { clientWidth: w, clientHeight: h } = canvas.value
  const camera = newCamera(75,  w / h, 0.1, 10000)

  camera.position.copy(POINT_SCANE.pos)
  camera.setLook(POINT_SCANE.direct.x, POINT_SCANE.direct.y, POINT_SCANE.direct.z)
  scene.add(camera)

  const renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setClearAlpha(0)
  canvas.value.appendChild(renderer.domElement)

  const renderer2d = new CSS2DRenderer()
  renderer2d.domElement.style.position = 'absolute'
  renderer2d.domElement.style.top = 0
  canvas.value.appendChild(renderer2d.domElement)

  const controls = new OrbitControls(camera, renderer2d.domElement)
  controls.target = new THREE.Vector3(camera.look.x, camera.look.y, camera.look.z)

  context.scene = scene
  context.camera = camera
  context.renderer = renderer
  context.controls = controls
  context.renderer2d = renderer2d

}

// 隧道补光
const enviromentBright = () => {

  const GP_MILE = 100

  const { miles, roamCurve } = elts.tunnels[0]
  const nums = parseInt(miles / GP_MILE) || 0

  const points = new Array(nums - 1).fill(null).forEach((_, x) => {
    const progress = (x + 1) * GP_MILE / miles
    const xpt = roamCurve.getPointAt(progress)
    xpt.y += 30

    const light = new THREE.PointLight(0xffffff, 2, 0, 0)
    light.position.copy(xpt)
    context.scene.add(light)
  })


}

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

  // 环境光
  const enviroment = new THREE.AmbientLight(0xffffff, 1, 0, 0)
  context.scene.add(enviroment)

  // 平行光
  const light = new THREE.DirectionalLight(0xffffff, 2, 0, 0)
  light.position.set(0, 50, 0)
  context.scene.add(light)

  // 模型加载
  await loadModels()

  // 隧道构建
  await createTunnels()

  // 摄像机挂载
  mountCameras()

  // 初始化交互
  interactInit()

  // 隧道补光
  enviromentBright()

  // 测试
  runTest(context, models)

}

// 分辨率自适应
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.renderer2d.setSize(w, h)

    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)
  })

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

  // 启动
  amtStart()

}

// 3D 环境清理
const worldClean = () => {

  context.scene.clear()
  context.renderer.dispose()
  context.renderer.forceContextLoss()
  context.renderer.content = null

}


onMounted(async () => {

  creatContext()
  engineStart()

  await createEnviroment()

  // useConnectCall(provides)
  useController(provides)

})

onUnmounted(() => {

  amtClean()
  controlsClean()
  worldClean()
  interactClean && interactClean()
  elts.tunnels.forEach(xTunnel => {
    xTunnel.stopFlows()
    xTunnel.stopEventFlows()
  })

  models = {}
  interactClean = null

  elts.tunnels = []
  elts.cameras = []
  
  for (let key in context) {
    context[key] = null
  } 

})

</script>

<style lang='scss' scoped>

$Common_Url: '@/assets/images/3d/';

.canvas {
  z-index: 0;
  position: absolute;
  height: 100%;
  width: 100%;
  :deep(canvas) {
    height: 100%;
  }
}

</style>

<style lang="scss">

.world-3d {
  .el-table {
    &.def {
      .stripe-row {
       background: salmon;
      }
    }
  }
}

</style>