import TrafficSocket from '@/views/traffic/utils/socket'
import { useAppStoreWithOut } from '@/store/modules/app'
import { awaitGet } from '@/utils/tunnel'
import { CAR_TYPES } from '@/config/dict'

const appStore = useAppStoreWithOut()

class Car {

  id // 车辆 ID
  type // 车型
  speed // 车速
  time // 瞬时记录
  plate // 车牌
  originalCar // 车辆信息
  mook // 模拟车流
  
  car_length // 车身长
  car_width // 车身宽

  distance = 0 // 行驶距离（米）
  x = 0 // 横向位置 px
  y // 纵向位置 px

  time_start = null // 起点时间
  time_pre = null // 前帧点
  time_now = null // 后帧点
  
  // switch_status = false // 切道状态；true 表示正在切换
  // switch_speed = 0.5 // 车道/每秒
  // switch_width = 0 // 切道距离
  // dy = [] // 剩余易道值
  // dr = 3
  
  // dy = 0 // 目标切换值
  dy = 0 // 瞬时切换值
  dyv = 0.5 // 切换速度：车道/秒
  tg_lane = null // 目标车道
  
  line // 行驶车道
  direction // 行驶方向

  lanes = [] // 车道行驶线
  ratex = 1 // 换算比
  animate_time = 1 // 动画间隔

  constructor (configs) {

    const {
      ratex, id, mook = false, type, speed, distance, line, direction, plate, originalCar,
      car_length, car_width, new_car = false, 
      lanes
    } = configs

    this.ratex = ratex
    this.id = id
    this.type = type
    this.speed = speed
    this.line = line
    this.direction = direction
    this.plate = plate
    this.car_length = car_length
    this.car_width = car_width
    this.mook = mook

    this.originalCar = originalCar
    
    this.lanes = lanes
    this.distance = distance
    this.x = distance * this.ratex * (direction ? 1 : -1)
    this.y = (lanes[line - 1] - car_width / 2) * (direction ? 1 : -1)
    this.time = (new Date()).getTime()

    this.style = {
      transform: `translate(${this.x}px, ${this.y}px)`
    }

  }
  
  // 自定义补间
  frameDefine () {
    
    if (this.dy.length) {
      this.y = this.y + this.dy[0]
      this.dy.shift()
    }

    if (this.new_car) {
      this.new_car = false
      this.style = {
        transform: `translate(${this.x}px, ${this.y}px)`
      }
    } else {
      this.style = {
        transform: `translate(${this.x}px, ${this.y}px)`,
        transitionProperty: 'all',
        transitionDuration: this.animate_time + 's',
        transitionTimingFunction: 'linear'
      }
    }
  }

  update (e) {

    const { type, speed, line, distance, originalCar } = e

    this.speed = speed
    this.type = type

    if (this.line !== line && !this.tg_lane) {
      this.tg_lane = line
    }

    this.originalCar = originalCar
    this.time = (new Date()).getTime()

  }

  run () {

    // 起始动画帧点
    if (!this.time_start) {
      this.time_start = (new Date()).getTime()
      this.time_pre = this.time_start
      this.style = {
        transform: `translate(${this.x}px, ${this.y}px)`
      }
    } else {
      const time_now = (new Date()).getTime()
      const duration = time_now - this.time_pre

      this.time_pre = time_now
      const x_speed = (this.speed * 1000 * this.ratex) / (3600 * 1000)
      this.x = this.x + (this.direction ? 1 : -1) * x_speed * duration
      // console.log(this.x)

      this.distance = this.distance + (this.speed * 1000) / (3600 * 1000) * duration

      let sign = 1
      
      if (this.tg_lane) {

        // const tg_width = Math.abs(this.lanes[this.tg_lane - 1] - this.lanes[this.line - 1])

        if (this.dy >= Math.abs(this.lanes[this.tg_lane - 1] - this.lanes[this.line - 1])) {
          this.line = this.tg_lane
          this.y = (this.lanes[this.line - 1] - this.car_width / 2) * (this.direction ? 1 : -1)
          this.tg_lane = null
          this.dy = 0
          return
        }

        // console.log(this.dy)

        const y_speed = (this.lanes[1] - this.lanes[0]) * this.dyv / 1000
        this.dy = this.dy + y_speed * duration
        sign = (this.tg_lane - this.line > 0 ? 1 : -1) * (this.direction ? 1 : -1)

      }

      this.style = {
        transform: `translate(${this.x}px, ${this.y + this.dy * sign}px)`,
        transitionDuration: '0s'
      }
    }

  }

}

class TrafficEngine {

  engine = null
  run_x = 0 // 运行计数
  scene = null // 模式
  direction = null // 隧道方向

  ratex = 1 // 换算比
  miles = 1800 // 隧道总长
  // section = 300 // 段面长
  section = 100
  start = 0
  end = 1800

  lanes = 2 // 车道数
  lane_w = 45 // 车道宽
  drive_lines = [] // 车道行驶线
  
  socket = null // 链接
  cars = [] // 路面车流

  mook_sign = null // 模拟器签名
  mook_store = [] // 车流模拟仓库
  mook_cache = [] // 重播缓存仓库
  mook_count = 0 // 当前模拟帧
  mook_loading = false // 数据状态

  constructor (secne = 'panorama', configs = {}) {

    Object.keys(configs).forEach(key => {
      this[key] = configs[key]
    })

    this.sceneChange(secne)

    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'visible') {
        this.cars = []
      }
    })

  }

  // 模式切换
  sceneChange (secne) {

    this.scene = secne

    if (secne === 'panorama') {
      this.ratex = (appStore.getDesign - 180) / this.miles
      this.lane_w = 45
    }

    if (secne === 'section') {
      this.ratex = appStore.getDesign / this.section
      this.lane_w = 125
    }

    this.cars = []

    this.drive_lines = new Array(this.lanes).fill(null).map((_, x) => {
      return (x + 0.5) * this.lane_w + (this.lane_w % 2 === 0 ? -0.5 : 0) // 1/2 车道宽为奇数时正好居中，为偶数时需要补 0.5px
    })

  }

  // 车辆数据格式化
  carFormate (cars) {

    return cars.map(item => {
      const { carId, roadNo, speed, carMilestone, vehicleType, carPlate } = item
      const { car_width, car_length } = CAR_TYPES[vehicleType]
      const distance = this.direction ? carMilestone - this.start : this.end - carMilestone

      return {
        id: carId,
        direction: this.direction, // 方向
        type: vehicleType, // 类型
        plate: carPlate, // 车牌
        speed, // 速度
        line: roadNo + 1, // 车道
        distance: distance < 0 ? 0 : distance, // 行驶距离
        carMilestone,
        car_width, // 车宽
        car_length, // 车长
        originalCar: JSON.parse(JSON.stringify(item)) // 车辆数据副本
      }
    })

  }

  // 更新车流
  carUpdate (frameCars) {

    const cars = frameCars.filter(xcar => xcar.carMilestone >= this.start && xcar.carMilestone <= this.end)

    const realCars = this.carFormate(cars)

    const currentMap = Object.fromEntries(realCars.map(item => {
      return [item.id, item]
    }))
    const oldCars = this.cars.filter(item => currentMap[item.id])
    const oldMap = Object.fromEntries(oldCars.map(item => {
      return [item.id, item]
    }))

    const newCars = realCars.filter(item => !oldMap[item.id]).map(item => {
      return new Car({
        ...item, 
        ratex: this.ratex,
        lanes: this.drive_lines,
        new_car: this.cars.length > 0
      })
    })

    oldCars.forEach(item => {
      item.update(currentMap[item.id])
    })

    this.cars = [...oldCars, ...newCars]
    this.run_x++

  }
  
  // 重播
  reMook () {

    this.cleanRoad()

    const run = () => {

      this.mook_sign = setInterval(() => {

        // 合法帧
        if (this.mook_count < this.mook_cache.length) {
          this.carUpdate(this.mook_cache[this.mook_count])
          this.mook_count++
          return
        }

        // 非法帧
        this.mook_sign && clearInterval(this.mook_sign)

        if (this.mook_loading) {
          const connected = awaitGet(() => this.mook_cache[this.mook_count]) // 续连状态
          if (connected) {
            run () // 继续执行
          }
        } else {
          clearInterval(this.mook_sign)
        }


      }, 250)

    }

    run()

  }

  // 虚实线换算
  lineFormate (lines) {

    return lines.map(({start, end, type}) => {
      
      return {
        x: start * this.ratex,
        y: this.lane_w,
        type,
        long: (end - start) * this.ratex
      }

    })

  }

  // 摄像机换算
  cameraFormate (cameras, direction) {

    return cameras.map(item => {

      return {
        x: item.distance * this.ratex,
        ...item
      }
    })

  }

  // 事件换算
  eventFormate (events) {
    return events.map(item => {
      return {
        id: item.id,
        x: item.distance * this.ratex,
        y: this.drive_lines[item.eventRoadNo % 2] + (this.scene === 'section' ? item.eventRoadNo * 7.5 : 0),
        // transform: `translate(${item.direction ? '-50%' : '50%'}, ${item.direction ? '50%' : '-50%'})`,
        ...item
      }
    })
  }

  // 设施换算
  facilitiesFormate (facilities) {

    return facilities.map(({ start, end, type, direction }) => {
      return {
        x: start * this.ratex,
        type,
        direction
      }
    })

  }

  // 停车带换算
  parkingFormate (packings) {

    return packings.map(({ start }) => {
      return { x: start * this.ratex }
    })

  }

  // 路标换算
  arrowFormate (arrows) {
    return arrows.map(({ start, lane }) => {
      return [{
        x: start * this.ratex,
        y: this.drive_lines[0] + (this.scene === 'section' ? lane * 7.5 : 0)
      }, {
        x: start * this.ratex,
        y: this.drive_lines[1] + (this.scene === 'section' ? lane * 7.5 : 0)
      }]
    }).flat()
  }

  // 缎面计算
  sectionsCompute () {

    const leftMiles = this.miles % this.section
    const completeSections = (this.miles - leftMiles) / this.section

    return (new Array(completeSections + (leftMiles > 0 ? 1 : 0))).fill(null).map((_, x) => {
      return {
        grow: x < completeSections ? 1 : leftMiles / this.section
      }
    })

  }

  
  // 模拟车流更新
  updateMooks (frames, loaded = false) {
    frames.forEach(item => this.mook_store.push(item))
    if (loaded) {
      this.mook_cache = JSON.parse(JSON.stringify(this.mook_store))
      this.mook_loading = false
    }
  }

  // 车流模拟
  /**
   * isAsync: 是否为连续请求数据
  */
  startMook (frames) {

    this.cleanRoad()

    if (frames) {
      this.mook_store = frames
    }
    
    const run = () => {

      this.mook_sign = setInterval(() => {

        if (this.mook_count < this.mook_store.length) {
          this.carUpdate(this.mook_store[this.mook_count])
        } else {
          clearInterval(this.mook_sign)
        }
        
      }, 250)

    }

    run()

    /* if (isAsync) { 
      this.mook_loading = true
    } */

    /* const run = () => {

      this.mook_sign = setInterval(() => {

        // 合法帧
        if (this.mook_count < this.mook_cache.length) {
          this.carUpdate(this.mook_store[this.mook_count])
          this.mook_count++
          return
        }

        // 非法帧
        if (this.mook_sign) {
          clearInterval(this.mook_sign)
        }

        if (this.mook_loading) {
          const connected = awaitGet(() => this.mook_store[this.mook_count]) // 续连状态
          if (connected) {
            run () // 继续执行
          }
        } else {
          // 执行完毕
          clearInterval(this.mook_sign)
        }

      }, 250)
    }

    run() */

  }

  // 实时车流
  startTraffic (selfCall) {

    this.cleanRoad()

    this.socket = new TrafficSocket(
      this.direction ? 'OUT_CAR' : 'IN_CAR'
    )

    this.socket.start(res => {
      selfCall && selfCall(res)
      this.carUpdate(res.vehicleInfoList)
    })

  }


  // 清理路面
  cleanRoad () {

    this.cars = []
    if (this.socket) {
      this.socket.close()
      this.socket = null
    }
    
    // this.mook_store = []
    this.mook_count = 0
    if (this.mook_sign) {
      clearInterval(this.mook_sign)
      this.mook_sign = null
    }
    
  }

  // 车流供给
  exportCars () {

    const cars = this.cars
      .filter(item => item.distance <= this.miles)
      .map(item => {

        item.run()

        return {
          id: item.id,
          mook: item.mook,
          type: item.type,
          style: item.style,
          direction: item.direction,
          plate: item.plate,
          distance: item.distance,
          progress: item.distance / this.miles,
          originalCar: item.originalCar
        }
      })

    

    return cars

  }

}


export default TrafficEngine