const { Vec3 } = require('vec3')

module.exports = inject

const CARDINALS = {
  north: new Vec3(0, 0, -1),
  south: new Vec3(0, 0, 1),
  west: new Vec3(-1, 0, 0),
  east: new Vec3(1, 0, 0)
}

const FACING_MAP = {
  north: { west: 'right', east: 'left' },
  south: { west: 'left', east: 'right' },
  west: { north: 'left', south: 'right' },
  east: { north: 'right', south: 'left' }
}

function inject (bot) {
  const { instruments, blocks } = bot.registry

  // Stores how many players have currently open a container at a certain position
  const openCountByPos = {}

  function parseChestMetadata (chestBlock) {
    const chestTypes = ['single', 'right', 'left']

    return bot.supportFeature('doesntHaveChestType')
      ? { facing: Object.keys(CARDINALS)[chestBlock.metadata - 2] }
      : {
          waterlogged: !(chestBlock.metadata & 1),
          type: chestTypes[(chestBlock.metadata >> 1) % 3],
          facing: Object.keys(CARDINALS)[Math.floor(chestBlock.metadata / 6)]
        }
  }

  function getChestType (chestBlock) { // Returns 'single', 'right' or 'left'
    if (bot.supportFeature('doesntHaveChestType')) {
      const facing = parseChestMetadata(chestBlock).facing

      if (!facing) return 'single'

      // We have to check if the adjacent blocks in the perpendicular cardinals are the same type
      const perpendicularCardinals = Object.keys(FACING_MAP[facing])
      for (const cardinal of perpendicularCardinals) {
        const cardinalOffset = CARDINALS[cardinal]
        if (bot.blockAt(chestBlock.position.plus(cardinalOffset))?.type === chestBlock.type) {
          return FACING_MAP[cardinal][facing]
        }
      }

      return 'single'
    } else {
      return parseChestMetadata(chestBlock).type
    }
  }

  bot._client.on('block_action', (packet) => {
    const pt = new Vec3(packet.location.x, packet.location.y, packet.location.z)
    const block = bot.blockAt(pt)

    // Ignore on non-vanilla blocks
    if (block === null || !blocks[packet.blockId]) { return }

    const blockName = blocks[packet.blockId].name

    if (blockName === 'noteblock') { // Pre 1.13
      bot.emit('noteHeard', block, instruments[packet.byte1], packet.byte2)
    } else if (blockName === 'note_block') { // 1.13 onward
      bot.emit('noteHeard', block, instruments[Math.floor(block.metadata / 50)], Math.floor((block.metadata % 50) / 2))
    } else if (blockName === 'sticky_piston' || blockName === 'piston') {
      bot.emit('pistonMove', block, packet.byte1, packet.byte2)
    } else {
      let block2 = null

      if (blockName === 'chest' || blockName === 'trapped_chest') {
        const chestType = getChestType(block)
        if (chestType === 'right') {
          const index = Object.values(FACING_MAP[parseChestMetadata(block).facing]).indexOf('left')
          const cardinalBlock2 = Object.keys(FACING_MAP[parseChestMetadata(block).facing])[index]
          const block2Position = block.position.plus(CARDINALS[cardinalBlock2])
          block2 = bot.blockAt(block2Position)
        } else if (chestType === 'left') return // Omit left part of the chest so 'chestLidMove' doesn't emit twice when it's a double chest
      }

      // Emit 'chestLidMove' only if the number of players with the lid open changes
      if (openCountByPos[block.position] !== packet.byte2) {
        bot.emit('chestLidMove', block, packet.byte2, block2)

        if (packet.byte2 > 0) {
          openCountByPos[block.position] = packet.byte2
        } else {
          delete openCountByPos[block.position]
        }
      }
    }
  })

  bot._client.on('block_break_animation', (packet) => {
    const destroyStage = packet.destroyStage
    const pt = new Vec3(packet.location.x, packet.location.y, packet.location.z)
    const block = bot.blockAt(pt)
    const entity = bot.entities[packet.entityId]

    if (destroyStage < 0 || destroyStage > 9) {
      // http://wiki.vg/Protocol#Block_Break_Progress
      // "0-9 to set it, any other value to remove it"
      bot.emit('blockBreakProgressEnd', block, entity)
    } else {
      bot.emit('blockBreakProgressObserved', block, destroyStage, entity)
    }
  })
}
