
<template>

  <Controller v-bind="{ getCars, getContext }" />
  <div class="canvas" ref="canvas"></div>
  <Compatible />


</template>

<script lang='ts' setup>

import * as THREE from 'three'
import TrafficSocket from '@/views/traffic/utils/socket'

import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { FontLoader } from 'three/addons/loaders/FontLoader.js'
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'

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

import { useTunnelStoreWithOut } from '@/store/modules/tunnel'

import { MODELS } from './static'
import { modelLoader } from './utils'

import RoadlModel from './units/road'
import CarModel from './units/car'
import CameraModel from './units/camera'

import { amtMount, amtStart } from './center'

import {
  tracking,
  roamming,
  fosViews,
  tweenContext,
  goTracking,
  stopTrack,
  laneChange,
  tunAround,
  goRoamming,
  roamAct,
  roamChange,
  pointTest
} from './units/controller'

const app = getCurrentInstance()

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

const tunnelStore = useTunnelStoreWithOut()

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

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

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

const getContext = () => {
  return context
}

// 加载器
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 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 runningEngine = () => {

  // 尺寸自适应
  amtMount(autoSize)

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

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

  amtStart()

}

// 创建文字
const createText = async () => {

  const loader = new FontLoader()

  const font = await new Promise(resolve => {
    loader.load('fonts/optimer_bold.typeface.json', e => {
      resolve(e)
    })
  })
  
  const textGeo = new TextGeometry('中文', {
    font,
    size: 70,
    height: 5,
    curveSegments: 12,
    bevelEnabled: true,
    bevelThickness: 0.1,
    bevelSize: 0.1,
    bevelOffset: 0,
    bevelSegments: 5
  })

  textGeo.computeBoundingBox()

  const materials = [
    new THREE.MeshPhongMaterial( { color: 0xffffff, flatShading: true } ), // front
    new THREE.MeshPhongMaterial( { color: 0xffffff } ) // side
  ]

  const centerOffset = - 0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x)
  const textMesh = new THREE.Mesh(textGeo, materials[1])
  textMesh.position.z = centerOffset
  textMesh.position.y = 0
  textMesh.position.z = 0

  textMesh.rotation.y = - Math.PI / 2

  context.scene.add(textMesh)

}

// 场景创建
const createContext = () => {

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

  const { clientWidth: w, clientHeight: h } = canvas.value
  context.camera = new THREE.PerspectiveCamera(
    75,
    w / h,
    0.1,
    1000
  )

  context.camera.position.set(-10, 10, 0)
  context.scene.add(context.camera)
  context.camera.lookAt(20, 0, 0)

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

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

// 交互构建
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 createBaseEnvironment = async () => {

  // 辅助线
  // const axesHelper = new THREE.AxesHelper(100)

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

  await loadModels()

  // 道路模型
  await buildTunnels()

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

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



}

// 道路模型构建
const buildTunnels = async () => {

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

  await tunnelStore.waitTunnel()

  // 数据模型
  const getRoad = (x) => {

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

    const xRoad = new RoadlModel(
      context, models, {
        miles: tunnel.long,
        start: tunnel.start,
        end: tunnel.end,
        mode: 'online',
        direct: x
      }
    )

    // 摄像机配置

    tunnel.cameras.forEach(item => {
      elts.cameras.push(new CameraModel(context, {
        infos: {
          ...item,
          direct: x,
          progress: item.distance / tunnel.long
        },
        model: models.camera,
        curve: xRoad.curves[1 - x]
      }))
    })
    return xRoad
  }

  // 隧道
  elts.tunnels = [
    getRoad(0), getRoad(1)
  ]

  elts.tunnels[1].startFlows()

}

const sceneInit = () => {

  createContext()
  runningEngine()

}

onMounted(() => {

  sceneInit()
  createBaseEnvironment()

})

</script>

<style lang='scss' scoped>

.controls {
  position: absolute;
  right: 20px;
  top: 20px;
  z-index: 2;
  .ctrl-btn {
    width: 80px;
    height: 30px;
    border-radius: 4px;
    background: rgba($color: #FFFFFF, $alpha: 0.6);
    text-align: center;
    line-height: 30px;
    cursor: pointer;
    font-size: 13px;
    margin-top: 10px;
    transition-property: all;
    transition-duration: 0.3s;
    color: #616161;
    &:hover {
      background: rgba($color: #00DDFF, $alpha: 0.6);
      box-shadow: 0 0 12px #00DDFF;
      color: #f1f1f1;
    }
  }

  .sections {
    margin-top: 30px;
    .x-sec {
      height: 45px;
      width: 10px;
      border-radius: 5px;
      background: rgba($color: #FFFFFF, $alpha: 0.6);
      margin: 5px auto;
      transition-property: all;
      transition-duration: 0.3s;
      cursor: pointer;
      &:hover {
        background: rgba($color: #FFFFFF, $alpha: 1);
        box-shadow: 0 0 12px #FFFFFF;
      }
      &.act {
        background: rgba($color: #FFFFFF, $alpha: 1);
        box-shadow: 0 0 12px #FFFFFF;
      }
    }
  }
}

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

</style>