import * as TWEEN from '@tweenjs/tween.js'
import * as THREE from 'three'
import { amtMount, amtRemove } from './center'
import { modelPlay, build2DModel } from '../utils'
import { diceCode } from '@/utils'

import CarPlate from '../../Doms2d/CarPlate.vue'

// 车辆类型映射
const modelFlex = ['bus', 'car', 'track', 'tanker']

export default class Car {
  car
  wave
  direct = null // 隧道编码
  curves = null // 隧道引导线
  infos = null // 车辆基础信息
  dir_sign = 1 // 变道符
  over_miles = false // 驶出隧道
  context = null

  lanex // 车道
  distance = 0 // 行驶距离
  start = null // 隧道起
  end = null // 隧道止
  miles = 0 // 隧道长
  speed = 0 // 车速

  // long = 0 // 隧道长
  direct = null // 加偏符

  followed = false
  camera = null

  xSpeed = 0.02 // 速度

  lanes = [0, 3.5]
  turnning = false // 变道状态
  taglane = null // 目标车道
  // torque = 0.05 // 转向速度
  // tadd = 0 // 转向增量
  tsign = 0 // 转向符号
  tag_pgress = 0
  start_radian = 0 // 偏转初始弧度
  tag_radian = (Math.PI / 180) * 30 // 转向弧度

  car_direction = new THREE.Vector3() // 车辆方向

  car_plate = null // 2d 车辆名牌模型

  vm_instance = null // 车辆标签 vue 实例

  // static tips_type = 'plate' // 2d 车辆名牌类型

  // static type_focus = null // 2d 车辆铭牌选中类型
  //static focus_directs = [] // 选中车道

  static runningCall = {}

  static setRunningCall = (xcall, oldkey) => {
    const key = oldkey || diceCode()
    Car.runningCall[key] = xcall
    return key
  }

  constructor (context, configs) {

    this.car = configs.models[modelFlex[configs.infos.type - 1]].scene.clone()

    this.wave = configs.models.wave.scene.clone()
    this.wave.scale.set(2.4, 2.4, 2.4)
    this.wave.animations = configs.models.wave.animations

    this.direct = configs.infos.direct

    this.dir_sign = this.direct ? 1 : -1
    this.curves = configs.curves
    this.miles = configs.miles
    this.start = configs.start
    this.end = configs.end
    this.speed = configs.infos.speed * 1000 / 3600
    this.infos = configs.infos
    this.distance = this.direct ? this.infos.milestone - this.start : this.end - this.infos.milestone
    this.context = context

    this.lanex = configs.infos.lanex
    
    const progress = this.distance / this.miles
    const nowCurver = this.curves[this.lanex]
    const position = nowCurver.getPointAt(progress)
    const direction = nowCurver.getTangentAt(progress).add(position)
    
    // position.z = position.z + this.lanes[this.lanex - 1] * this.dir_sign
    // direction.z += 10

    this.car.position.copy(position)
    this.car.lookAt(direction)
    this.context.scene.add(this.car)

    this.addTips()

  }

  addTips () {

    const { instance, model } = build2DModel({
      vm: CarPlate,
      key: 'car',
      id: this.infos.id,
      contentStr: '.car-plate-doms'
    })

    this.car_plate = model
    this.vm_instance = instance

    this.car_plate.position.x -= 2

    this.car.add(model)

    this.vm_instance.update(this.infos)

    // const label = create2DModel('.carplate')
    // this.car_plate = label
    // this.car.add(label)

  }

  run (delay) {

    this.distance += delay * this.speed
    this.infos.milestone = parseInt(this.direct ? this.start + this.distance : this.end - this.distance)

    const progress = this.distance / this.miles
    const nowCurver = this.curves[this.lanex]
    
    // 行驶验证
    if (progress > 1) {
      this.over_miles = true
      if (this.camera) {
        this.stopFollow()
      }
      this.context.scene.remove(this.car)
      this.car.remove(this.car_plate)
      this.car_plate = null
      return
    }

    const position = nowCurver.getPointAt(progress)

    // 转向
    if (this.turnning) {

      // 转向超距
      /* const outTunnning = (this.tsign > 0 && (this.lanes[this.lane - 1] + this.tadd * this.tsign > this.lanes[this.taglane - 1])) ||
                          (this.tsign < 0 && (this.lanes[this.lane - 1] + this.tadd * this.tsign) < this.lanes[this.taglane - 1])

      if (outTunnning) {
        this.turnning = false
        this.lane = this.taglane
        this.turnning = false
        this.taglane = null
        this.tadd = 0
        this.tsign = 0
      } else {
        this.tadd += this.torque
      } */

      const otherCurver = this.curves[this.taglane]
      const _position = otherCurver.getPointAt(progress)

      const vx = (_position.x - position.x) * this.tag_pgress
      const vy = (_position.y - position.y) * this.tag_pgress
      const vz = (_position.z - position.z) * this.tag_pgress

      position.x = position.x + vx
      position.y = position.y + vy
      position.z = position.z + vz

      // 转向超距
      if (this.tag_pgress === 1) {
        this.lanex = this.taglane
        this.turnning = false
        this.taglane = null
        this.tag_pgress = 0
        this.start_radian = 0
      }
      
    }

    const direction = nowCurver.getTangentAt(progress).add(position)

    this.car.position.copy(position)
    this.car.lookAt(direction)

    // 添加转向角
    if (this.turnning) {
      const d_radian = this.tag_radian * Math.sin(this.tag_pgress * Math.PI) * this.tsign
      this.car.rotation.y = this.start_radian + d_radian
    }

    // 车牌更新
    this.vm_instance.update(this.infos)

  }

  // 基础信息更新（速度、车道、桩米号）
  update (newcar) {

    this.infos.speed = newcar.speed
    this.speed = this.infos.speed * 1000 / 3600

    if (this.lanex !== newcar.lanex) {
      this.laneChange(newcar.lanex)
    }

  }

  follow (camera) {

    this.followed = true
    this.camera = camera
   
    this.camera.position.set(0, 10, -15)
    this.camera.lookAt(0, 5, 0)
    this.car.add(camera)

    this.car.add(this.wave)
    const key = modelPlay(this.wave)
    
  }

  amtCreate ({ fromPos, tagPos, update, complete }) {

    const tween = new TWEEN.Tween(fromPos)
      .to(tagPos, 2000)
      .easing(TWEEN.Easing.Quadratic.InOut)

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

    tween.onUpdate(e => {
      update && update(e)
    })

    tween.onComplete(() => {
      amtRemove(key)
      complete && complete()
    })

    return tween

  }

  stopFollow (state) {

    const nowCurver = this.curves[this.lanex]

    // 删除前场景
    this.car.remove(this.camera)
    this.camera = null
    this.context.scene.remove(this.car)

    if (state === 'change') {

      const position = nowCurver.getPointAt(0.95)
      const direction = nowCurver.getTangentAt(0.95).add(position)

      position.y += 10
      direction.y += 10

      this.context.camera.position.copy(position)
      this.context.camera.setLook(direction)
      return

    } else {

      let progress = this.distance / this.miles 

      if (progress > 1) {
        progress = 1
      }

      if (progress < 0) {
        progress = 0
      }

      const position = nowCurver.getPointAt(progress)
      const direction = nowCurver.getTangentAt(progress).add(position)

      this.context.camera.position.copy(position)
      this.context.camera.setLook(direction)

      let tag_pos = nowCurver.getPointAt(0.95)
      let direct_pos = nowCurver.getTangentAt(0.95).add(tag_pos)

      if (state === 'stop') {
        tag_pos = new THREE.Vector3().copy(position)
        direct_pos = new THREE.Vector3().copy(direction)
      }

      tag_pos.y += 10
      direct_pos.y += 10

      const tween = this.amtCreate({
        fromPos: position,
        tagPos: tag_pos,
        update: (e) => {
          this.context.camera.position.copy(e)
        },
        complete: () => {
          this.followed = false
        }
      })

      tween.start()

    }

  }

  laneChange (target) {

    if (this.turnning) return

    this.turnning = true
    this.taglane = 1 - this.lanex
    this.tsign = this.taglane - this.lanex
    this.start_radian = this.car.rotation.y

    const tween = new TWEEN.Tween({ rate: 0 })
      .to({ rate: 1 }, 1000)
      .easing(TWEEN.Easing.Quadratic.InOut)
    const key = amtMount(() => {
      tween.update()
    })
    tween.onUpdate(e => {
      this.tag_pgress = e.rate
    })
    tween.onComplete(() => {
      amtRemove(key)
    })

    tween.start()
    
  }

}