/**
 * 视锥能力（模拟 摄像头 视锥范围）
 */
class ConeStyle extends THING.Component {
  constructor() {
    super()
    // props 的第二种方式 - 直接定义对象
    this.props = {
      far: {
        type: 'number',
        value: 10,
        min: 2,
      },
      fov: {
        type: 'number',
        value: 60,
        min: 30,
        max: 120,
      },
      scanAuto: {
        type: 'boolean',
        value: true,
        description: '开启:显示视锥; 关闭:不显示视锥,但扫描到物体会显示',
      },
      scanCull: {
        type: 'boolean',
        value: false,
        description: '扫描到物体后,物体背朝视锥区域没有扫描线',
      },
      opacity: {
        type: 'slider',
        value: 0.1,
        min: 0,
        step: 0.02,
        max: 1,
      },
      color: {
        type: 'color',
        value: '#8EA9D8',
      },
      activeScan: {
        type: 'action',
      },
      passiveScan: {
        type: 'action',
      },
    }
  }

  onExport() {
    return this.props
  }

  onImport(param) {
    this.props = param
  }
  /**
   * 初始化参数
    / far,color,opacity,angles
   */
  _initParam() {
    const { props } = this
    this.color = props.color.value
    this.far = props.far.value
    this.fov = props.fov.value
    this.opacity = props.opacity.value
    this.scanAuto = props.scanAuto.value // 视锥长显
    this.scanCull = props.scanCull.value // 开启真实扫描遮挡，耗费性能，没有太大必要！建议false
  }
  onStart() {
    this.RENDER_ORDER_TAG = 'RENDER_ORDER_TAG'
    // 虚拟碰撞体集合
    this.nullBoxArr = []
    // 被动扫描
    this.scanAuto = false
    // 间隔扫描时间
    this.scanInterTime = 0.5
    const { object: o } = this
    o.waitForComplete().then(() => {
      try {
        // 摄像头镜头前，用了一个虚拟体定位坐标，这里获取一下这个坐标点
        const position =
          o.children.length > 0 ? o.children[0].position : o.position
        this._initParam()
        // 创建视锥
        this.probe = this._createProbe({
          far: this.far,
          fov: this.fov,
          color: this.color,
          opacity: this.opacity,
          position,
        })

        this.probe.waitForComplete().then(() => {
          // 创建多级碰撞体
          this._createAllNull(this.probe)
        })

        // 获取ColliderTrigger
        const ct = this.object.colliderTrigger
        // 添加事件
        ct.addEnterEvt(this.enter.bind(this))
        ct.addExitEvt(this.exit.bind(this))
        // 屏蔽视锥体自身碰撞，使用多级碰撞体
        this.object.collider.enable = false
        // 长显扫描，需要指定一个物体一个虚拟体，
        this._creatBackO(this.probe)
        // 是否启动了视锥扫描
        this.isWork = false
        // 开启扫描长显
        this.scanAuto && this.activeScan()
      } catch (error) {
        console.error(
          `预制件 : ${this.object.parent.name} 参数异常`,
          this.object
        )
      }
    })
  }
  onDestroy() {
    // this._stopProbe()
    if (this.probe) {
      this.probe.off('update', this.RENDER_ORDER_TAG)
      this.probe.destroy()
      this.probe = null
    }
    this.backObject && this.backObject.destroy()
    if (this.nullBoxArr && this.nullBoxArr.length > 0) {
      this.nullBoxArr.forEach((item) => item.destroy())
      this.nullBoxArr = null
    }
  }
  /**
   *  目前还不支持颜色，透明度
   */
  _createProbe({ far, opacity, color, aspect, fov, position } = {}) {
    let viewProbe = new THING.EXTEND.ViewProbe({
      innerScanning: true,
      scanningNum: [10, 20], // 后期可以自适应精度,
      innerPlaneColor: color, // 设置 plane 颜色
      innerPlaneOpacity: opacity, // 设置 plane 透明度
      outerColor: color, // 设置轮廓线颜色和plane一样
      parent: this.object,
    })

    viewProbe.fov = fov || 60
    viewProbe.far = far || 10
    viewProbe.angles = this.object.angles

    viewProbe.position = [position[0], position[1], position[2]]
    viewProbe.rotateX(90)
    viewProbe.aspect = aspect || 2
    viewProbe.on(
      'update',
      () => {
        viewProbe.renderOrder = -5
      },
      this.RENDER_ORDER_TAG
    )
    return viewProbe
  }

  _show(b) {
    this.probe && (this.probe.visible = b)
  }

  /**
   * 主动扫描
   */
  activeScan() {
    this.scanAuto = true
    this._stopProbe()
    this.interval = setInterval(() => {
      let arrR = [this.backObject]
      // 开启真实扫描遮挡，那就使用碰撞体数据扫描
      if (this.scanCull) {
        arrR = this._hasInvalidColliders()
          ? this._invalidEnterMap()
          : [this.backObject]
      }
      this._testInProbe(arrR)
    }, this.scanInterTime * 1000)
    this._show(true)
  }
  /**
   * 被动扫描
   */
  passiveScan() {
    this.scanAuto = false
    this._stopProbe()
  }

  /**
   * 创建多级碰撞体
   */
  _createAllNull(viewProbe) {
    const angles = viewProbe.angles
    const vec3 = THING.Math.vec3
    const f = vec3.normalize([], vec3.negate([], viewProbe.forward))
    const len = viewProbe.far
    // 分级数量，生成tl/2个碰撞体
    const tl = 12
    for (let i = 1; i < tl; i += 2) {
      // 1/n, 3/n ... 坐标刻度
      const z = (i / tl) * len
      // 2/n, 4/n ... 宽度刻度
      const y =
        2 * (z + len / tl) * Math.tan(((viewProbe.fov * Math.PI) / 180) * 0.5)
      const x = y * viewProbe.aspect
      const p = vec3.scaleAndAdd([], viewProbe.position, f, z)
      // z向碰撞缩小一点，避免这几个多级碰撞体相互碰撞
      const b = this._createNullBox(p, [x, y, ((2 * len) / tl) * 0.95], angles)
      b && this.nullBoxArr.push(b)
    }
  }
  _createNullBox(position, scale, angles) {
    const b = new THING.Box({
      parent: this.object,
      position,
      scale,
      angles,
      visible: false,
      // style: {
      //   opacity: 0.2,
      // },
    })
    b.waitForComplete().then(() => {
      const localBoundingBox = b.body.getLocalBoundingBox()
      b.collider.enable = true
      b.collider.mode = THING.ColliderType.Box
      b.collider.offset = localBoundingBox.center
      b.collider.halfSize = localBoundingBox.halfSize

      b.tags = this.object.tags
      const ct = this.object.colliderTrigger
      if (ct) {
        // 使用重新生成的多级碰撞体，挨个监听
        ct.addColliderEvt(b.collider)
      }
    })

    return b
  }

  /**
   * 启动 - loop - 每一秒监测一次 ; 没有碰撞对象，就停止监测
   */
  _startProbe() {
    this.interval = setInterval(() => {
      if (this.scanCull) {
        this._show(this._testInProbe(this._invalidEnterMap()))
      } else {
        this._testInProbe([this.backObject])
        this._show(true)
      }
    }, this.scanInterTime * 1000)
    this.isWork = true
  }

  // 单次监测，在视锥外?
  _testInProbe(arr) {
    if (!this.probe) return false
    try {
      this.probe.start(arr, true, false, true)
      const result = this.probe.test(arr, true, false, true)
      return result.length > 0
    } catch (error) {
      return false
    }
  }
  /**
   * 主动扫描的对象（摄像头背后，只是呈现扫描动画效果，不需要真正扫描）
   */
  _creatBackO(viewProbe) {
    const vec3 = THING.Math.vec3
    const f = vec3.normalize([], vec3.negate([], viewProbe.forward))
    const position = vec3.scaleAndAdd([], viewProbe.position, f, 1)
    this.backObject = new THING.Plane({
      scale: [0.1, 0.1, 0.1],
      position,
      visible: false,
    })
    this.backObject.collider.enable = false
  }
  _stopProbe() {
    this.probe.stop()
    this.interval && clearInterval(this.interval)
    this.isWork = false
    this._show(false)
  }
  // 是否有碰撞还没离开
  _hasInvalidColliders() {
    return this._invalidEnterMap().length > 0
  }

  // 有效的碰撞体对象
  _invalidEnterMap() {
    try {
      return this.object.colliderTrigger.colliderObjects.filter((obj) => {
        return this._tagsCheck(obj)
      })
    } catch (error) {
      return false
    }
  }

  // 人，车才可以
  _tagsCheck(obj) {
    if (Array.isArray(obj.tags)) {
      return obj.tags.includes('Human')
    } else {
      return obj.tags === 'Human'
    }
  }
  enter(e) {
    if (!this._tagsCheck(e.object)) return
    // 长显视锥，不需要走这里
    if (this.scanAuto) return
    // 是否启动视锥监测
    if (this.isWork) return
    this._startProbe()
  }
  exit(e) {
    if (!this._tagsCheck(e.object)) return
    if (this.scanAuto) return
    // 碰撞体离开，需要监测一些有效碰撞对象，是否还有
    if (this._hasInvalidColliders()) {
      // 单次监测
      if (this.scanCull)
        this._show(this._testInProbe(Array.from(this._invalidEnterMap())))
    } else {
      //离开时，没有碰撞体了。那说明视锥体中没有对象了
      this._stopProbe()
    }
  }
}
