import * as TWEEN from '@tweenjs/tween.js'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { amtMount, amtRemove } from '../models/center'
import { modelPlay, getClosetPoint } from '../utils'

import {
  bus, trackId, roamkey, sceneCheck,
  locatId, locating, locatPoints, listAppear
} from '@/views/3DModel/state'

import { POINT_START, POINT_END, POINT_SCANE } from '../points'

let env = {}

// 车辆追踪
const carTracking = (xCar) => {

  if (!sceneCheck('track')) {
    return
  }

  removeControls()

  const cars = env.getCars()
  const context = env.getContext()

  const preCar = cars.find(item => item.infos.id === trackId.value)


  if (preCar) {
    preCar.stopFollow('change')
  }

  const newCar = cars.find(item => item.infos.id === xCar.carId)

  if (newCar && !newCar.over_miles) {
    newCar.follow(context.camera)
    trackId.value = xCar.carId
  } else {
    context.camera.position.set(-25, 15, 0)
    context.camera.setLook(50, 10, 0)
    ElMessage.warning('车辆已驶离隧道！')
  }

}

// 段面漫游
const roamChange = ({ start, target, direct }) => {

  if (!sceneCheck('roam')) {
    return
  }

  removeControls()

  if (roamkey.value) {
    amtRemove(roamkey.value)
    roamkey.value = null
  }

  const context = env.getContext()
  const tunnnels = env.getTunnles()

  const tunnel = tunnnels[direct]

  const tween = new TWEEN.Tween({ rate: 0 })
    .easing(TWEEN.Easing.generatePow(2).InOut)
    .to({ rate: 1 }, 10000)
  
  tween.onUpdate(({ rate }) => {
    
    let progress = start + rate * (target - start);

    (progress > 1) && (progress = 1)

    const position = tunnel.roamCurve.getPointAt(progress)
    const direction = tunnel.roamCurve.getTangentAt(progress).add(position)
    position.y = position.y + 2
    direction.y = direction.y + 2
    context.camera.position.copy(position)
    context.camera.setLook(direction)
  })

  roamkey.value = amtMount(() => {
    tween.update()
  })

  tween.onComplete(() => {
    amtRemove(roamkey.value)
    roamkey.value = null
  })

  tween.start()

}

const cameraTravel = ( { start, target, call, duration, amt } = { duration: 2000, amt: 'InOut' }) => {

  let resolve = null
  const runningPms = new Promise(e => resolve = e)

  const tween = new TWEEN.Tween(start)
    .easing(TWEEN.Easing.generatePow(2).InOut)
    .to(target, duration)
  
  tween.onUpdate(e => {
    call(e)
  })

  const key = amtMount(() => {
    tween.update()
  })

  tween.onComplete(() => {
    amtRemove(key)
    resolve()
  })

  tween.start()
  
  return runningPms

}

// 添加事件标点
const addEventPoint = (tagPos) => {

  const context = env.getContext()
  const evtModel = env.getModel('event')

  // 前置清理
  locatPoints.value.forEach(ievt => {
    amtRemove(ievt.amtkey)
    context.scene.remove(ievt.model)
  })

  locatPoints.value = []

  // 添加点位
  const cloneModel = evtModel.scene.clone()

  cloneModel.scale.set(0.6, 0.6, 0.6)

  cloneModel.animations = evtModel.animations
  cloneModel.position.copy(tagPos)
  context.scene.add(cloneModel)

  const amtkey = modelPlay(cloneModel)

  locatPoints.value.push({
    amtkey, model: evtModel
  })

  /* const label = document.createElement('div')
  label.style.color = 'red'
  label.style.top = '-30px'
  label.innerHTML = '事件'
  const labelObject = new CSS2DObject(label)
  labelObject.position.set(0, 2, 0)

  cloneModel.add(labelObject) */

}

// 事件定位
const eventLocate = async (evt) => {

  if (!sceneCheck('locate')) {
    return
  }

  removeControls()

  locatId.value = evt.id
  locating.value = true

  const context = env.getContext()
  const tunnnels = env.getTunnles()

  const direct = evt.tunnelId === 'xl_tunnel_down' ? 1 : 0
  const tunnel = tunnnels[direct]

  const orgPos = context.camera.position
  const orgDirect = context.camera.look

  const progress = (direct ? (evt.milestone - tunnel.start) : (tunnel.end - evt.milestone)) / tunnel.miles
  const tagPos = tunnel.roamCurve.getPointAt(progress)

  if (!progress || progress > 1 || progress < 0) {
    return ElMessage.warning('数据错误！')
  }
  
  // 阶段1 视角进入入轨点
  const start = getClosetPoint(tunnel.roamCurve, tunnel.curves[1], orgPos)


  // 命中巡航点时，不执行旋转
  if (start.min > 1.5) {
    await cameraTravel({
      start: {
        point:  { ...orgPos },
        direct: { ...orgDirect }
      },
      target: {
        point:  { ...start.point, y: start.point.y + 60 },
        direct: { ...start.direct }
      },
      duration: 2000,
      call ({ point, direct, rate }) {
        context.camera.position.copy(new THREE.Vector3(point.x, point.y, point.z))
        context.camera.setLook(direct.x, direct.y, direct.z)
      },
      amt: 'In'
    })
  }

  // return

  // 阶段2 巡航

  const dps = Math.abs(progress - start.progress) // 位距百分比

  // 过滤重复点击
  if (dps > 0.001) {
    await cameraTravel({
      start: { rate: start.progress, x: 0 },
      target: { rate: progress, x: 1 },
      duration: 1000 * (dps / 0.1),
      call ({ rate, x }) {

        const xpoint = tunnel.roamCurve.getPointAt(rate)
        const xdirect = tunnel.curves[1].getPointAt(rate)
  
        context.camera.position.copy(new THREE.Vector3(xpoint.x, xpoint.y + 60, xpoint.z))
        context.camera.setLook(xdirect.x, xdirect.y, xdirect.z)

        if (x === 1) {
          createCube(xpoint)
          createCube(xdirect)
        }
        
  
      },
      amt: 'Out'
    })
  }

  

}

const createCube = (pos) => {
  const context = env.getContext()
  const geometry = new THREE.BoxGeometry(1, 1, 1)
  const material = new THREE.MeshBasicMaterial(0xffffff)
  const cube = new THREE.Mesh(geometry, material)

  cube.position.copy(pos)
  context.scene.add(cube)
}

// 添加控制器
const addControls = (target) => {
  const context = env.getContext()
  const controls = new OrbitControls(context.camera, context.renderer.domElement)
  controls.target = target
  context.controls = controls
}

// 删除控制器
const removeControls = () => {
  const context = env.getContext()
  if (context.controls) {
    context.controls.dispose()
    context.controls = null
  }
}

// 初始视角
const viewReset = (key) => {

  if (!sceneCheck('locate')) {
    return
  }

  removeControls()

  const context = env.getContext()

  const { pos, direct } = [POINT_SCANE, POINT_END, POINT_START][key]

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

  const look = context.camera.look
  addControls(new THREE.Vector3(look.x, look.y, look.z))

}

const useConnectCall = (provides) => {

  env = { ...provides }

  bus.on('3d-track', xCar => {
    carTracking(xCar)
  })

  bus.on('view-set', direct => {
    viewReset(direct)
  })

  bus.on('3d-roamming', configs => {
    roamChange(configs)
  })

  bus.on('3d-location', evt => {
    eventLocate(evt)
  })

}

const eventsClean = () => {

  bus.off('3d-track')
  bus.off('direction-set')
  bus.off('3d-roamming')
  bus.off('3d-location')

}

export {
  eventsClean,
  useConnectCall
}