text: const { Client } = require('bedrock-protocol')
  const { BotProvider } = require('./BotProvider')
  
  const controlMap = {
    forward: ['KeyW', 'KeyZ'],
    back: 'KeyS',
    left: ['KeyA', 'KeyQ'],
    right: 'KeyD',
    sneak: 'ShiftLeft',
    jump: 'Space'
  }
  
  class ClientProvider extends BotProvider {
    downKeys = new Set()
  
    connect () {
      const client = new Client({ host: '127.0.0.1', version: '1.16.220', username: 'notch', offline: true, port: 19132, connectTimeout: 100000 })
  
      client.once('resource_packs_info', (packet) => {
        client.write('resource_pack_client_response', {
          response_status: 'completed',
          resourcepackids: []
        })
  
        client.once('resource_pack_stack', (stack) => {
          client.write('resource_pack_client_response', {
            response_status: 'completed',
            resourcepackids: []
          })
        })
  
        client.queue('client_cache_status', { enabled: false })
        client.queue('request_chunk_radius', { chunk_radius: 1 })
  
        this.heartbeat = setInterval(() => {
          client.queue('tick_sync', { request_time: BigInt(Date.now()), response_time: 0n })
        })
      })
  
      this.client = client
    }
  
    close () {
      this.client?.close()
    }
  
    listenToBot () {
      this.client.on('connect', () => {
        console.log('Bot has connected!')
      })
      this.client.on('start_game', packet => {
        this.updatePosition(packet.player_position)
        this.movements.init('server', packet.player_position, /* vel */ null, packet.rotation.z || 0, packet.rotation.x || 0, 0)
      })
  
      this.client.on('spawn', () => {
        this.movements.startPhys()
        // server allows client to render chunks & spawn in world
        this.emit('spawn', { position: this.lastPos, firstPerson: true })
  
        this.tickLoop = setInterval(() => {
          this.client.queue('tick_sync', { request_time: BigInt(Date.now()), response_time: 0n })
        })
      })
  
      this.client.on('level_chunk', packet => {
        this.handleChunk(packet)
      })
  
      this.client.on('move_player', packet => {
        if (packet.runtime_id === this.client.entityId) {
          this.movements.updatePosition(packet.position, packet.yaw, packet.pitch, packet.head_yaw, packet.tick)
        }
      })
  
      this.client.on('set_entity_motion', packet => {
        // if (packet.runtime_id === this.client.entityId) this.updatePosition(packet.position)
      })
  
      this.client.on('tick_sync', (packet) => {
        this.lastTick = packet.response_time
      })
    }
  
    onKeyDown = (evt) => {
      const code = evt.code
      for (const control in controlMap) {
        if (controlMap[control].includes(code)) {
          this.movements.setControlState(control, true)
          break
        }
        if (evt.ctrlKey) {
          this.movements.setControlState('sprint', true)
        }
      }
      this.downKeys.add(code)
    }
  
    onKeyUp = (evt) => {
      const code = evt.code
      if (code === 'ControlLeft' && this.downKeys.has('ControlLeft')) {
        this.movements.setControlState('sprint', false)
      }
      for (const control in controlMap) {
        if (controlMap[control].includes(code)) {
          this.movements.setControlState(control, false)
          break
        }
      }
      this.downKeys.delete(code)
    }
  }
  
  module.exports = { ClientProvider }
  