import { system, world } from "@minecraft/server";

system.beforeEvents.startup.subscribe(ini => {
  ini.blockComponentRegistry.registerCustomComponent("gm:structure_spawner", {onTick: (t, p) => {
    system.run(()=>{
      let block = t.block
      let loc = block.location
      let mode = p.params["spawn_type"]
      let playersInRange = t.dimension.getEntities({location: loc, maxDistance: 16, type: "minecraft:player"})
      if (block.permutation.getState("gm:file_name") !== "empty") {
        if (playersInRange.length > 0) {
          t.dimension.spawnParticle("minecraft:basic_flame_particle", loc)
          t.dimension.spawnParticle("minecraft:basic_smoke_particle", loc)
          console.log(world.getDynamicProperty(block.permutation.getState("gm:file_name")),block.permutation.getState("gm:file_name"),1,world.getDynamicPropertyIds(),1)
          let pool = JSON.parse(world.getDynamicProperty(block.permutation.getState("gm:file_name")))
          function* blockGetter(location) {
            for (let dx = -4; dx <= 4; dx++) {
              const maxDz = 4-Math.abs(dx);
              for (let dz = -maxDz; dz <= maxDz; dz++) {
                yield t.dimension.getBlock({x: (location.x+dx), y: location.y, z: (location.z+dz)})
              }
            }
          }
          function blockPopper(array,location) {
            let index = Math.random()*16
            for (let b of array) {
              let d = 1/(Math.abs(b.location.x-location.x)+Math.abs(b.location.z-location.z))
              index = index-d
              if (index > 0) {
                continue
              } else {
                return array.splice(array.indexOf(b,1))[0]
              }
            }
          }
          let blocks = Array.from(blockGetter(loc)).filter(b=>{b.location!=loc})
          const block1 = blockPopper(blocks, loc)
          const block2 = blockPopper(blocks, loc)
          const block3 = blockPopper(blocks, loc)
          const block4 = blockPopper(blocks, loc)
          const selectedBlocks = [block1,block2,block3,block4]
          if (mode === "not_set") {

          } else {
            if (mode === "monster") {
              let spawnCount = 0
              selectedBlocks.forEach(b=>{
                if (b?.isAir) {
                  if (b.getLightLevel() == 0) {
                    player.runCommand(`structure load ${structureGetter(pool)} ${b.location.x} ${b.location.y} ${b.location.z}`)
                    t.dimension.spawnParticle("minecraft:mob_block_spawn_emitter", loc)
                    spawnCount = spawnCount+1
                  }
                }
              })
              if (spawnCount === 0) {
                block.permutation.setPermutation(block.permutation.withState("gm:tick_type", "immediate"))
              } else {
                block.permutation.setPermutation(block.permutation.withState("gm:tick_type", "default"))
              }
            }
            if (mode === "animal") {
              let spawnCount = 0
              selectedBlocks.forEach(b=>{
                if (b?.isAir) {
                  player.runCommand(`structure load ${structureGetter(pool)} ${b.location.x} ${b.location.y} ${b.location.z}`)
                  t.dimension.spawnParticle("minecraft:mob_block_spawn_emitter", loc)
                  spawnCount = spawnCount+1
                }
              })
              if (spawnCount === 0) {
                block.permutation.setPermutation(block.permutation.withState("gm:tick_type", "immediate"))
              } else {
                block.permutation.setPermutation(block.permutation.withState("gm:tick_type", "default"))
              }
            }
            if (mode === "aquatic") {
              let spawnCount = 0
              function isWater(location) {
                const block = dimension.getBlock(location)
                return block.typeId === "minecraft:water" || block.typeId === "minecraft:flowing_water"
              }
              if (isWater({x:(loc.x-1),y:loc.y,z:loc.z}) || isWater({x:loc.x,y:(loc.y-1),z:loc.z}) || isWater({x:loc.x,y:loc.y,z:(loc.z-1)}) || isWater({x:(loc.x+1),y:loc.y,z:loc.z}) || isWater({x:loc.x,y:(loc.y+1),z:loc.z}) || isWater({x:loc.x,y:loc.y,z:(loc.z+1)})) {
                selectedBlocks.forEach(b=>{
                  if (b?.isAir) {
                    player.runCommand(`structure load ${structureGetter(pool)} ${b.location.x} ${b.location.y} ${b.location.z}`)
                    t.dimension.spawnParticle("minecraft:mob_block_spawn_emitter", loc)
                    spawnCount = spawnCount+1
                  }
                })
                if (spawnCount === 0) {
                  block.permutation.setPermutation(block.permutation.withState("gm:tick_type", "immediate"))
                } else {
                  block.permutation.setPermutation(block.permutation.withState("gm:tick_type", "default"))
                }
              }
            }
          }
        }
      }
    })
  }})
  ini.blockComponentRegistry.registerCustomComponent("gm:reward_xp", {onPlayerBreak: (b, p) => {
    b.dimension.spawnEntity("xp_orb", b.block.location)
  }})
  ini.itemComponentRegistry.registerCustomComponent("gm:blocking", {onUse: (u, p) => {
    let item = u.itemStack
    let player = u.source
    player.addTag("gm:blocking")
    player.dimension.getEntities({location: player.location, maxDistance: p.params?.radius??3}).forEach(e => {
      if (e.hasComponent("minecraft:projectile")) {
        e.remove()
      }
    })
    const cd = world.scoreboard.getObjective("cmd:countdown")
    cd.setScore(`block:${player.id}`, (p.params?.duration*20)??20)
    if (item.hasComponent("minecraft:cooldown")) {
      item.getComponent("minecraft:cooldown").startCooldown(player)
    }
  }})
  ini.customCommandRegistry.registerCommand({cheatsRequired: true, permissionLevel: 1, name: "gm:impulse", description: "Impulse on some entities.", mandatoryParameters: [{name: "victim", type: "EntitySelector"}, {name: "vecter", type: "Location"}]}, (executor, arg1, arg2) => {
    if (arg1.length < 1) {
      return {message: "commands.generic.entity.notFound", status: 1}
    } else {
      system.run(()=>{
        arg1.forEach(p => {
          world.getEntity(p.id).applyImpulse(arg2)
        })
      })
      return {message: "Successfully applied impulse", status: 0}
    }
  })
  ini.customCommandRegistry.registerCommand({cheatsRequired: true, permissionLevel: 1, name: "gm:punch", description: "Punch entities in circle.", mandatoryParameters: [{name: "victim", type: "EntitySelector"}, {name: "power", type: "Float"}], optionalParameters: [{name: "radius", type: "Float"}]}, (executor, arg1, arg2, arg3) => {
    if (arg1.length < 1) {
      return {message: "commands.generic.entity.notFound", status: 1}
    } else {
      system.run(()=>{
        let loc = {x:0,y:0,z:0}
        if (executor.sourceType == "Entity") {
          loc = executor.sourceEntity.location
        }
        if (executor.sourceType == "Block") {
          loc = executor.sourceBlock.location
        }
        if (executor.sourceType == "NPCDialogue") {
          loc = executor.initator.location
        }
        if (executor.sourceType == "Server") {
          return {message: "commands.generic.invalidOrigin", status: 1}
        }
        arg1.forEach(p => {
          let distance = Math.sqrt((loc.x-p.location.x)**2+(loc.y-p.location.y)**2+(loc.z-p.location.z)**2)
          if (arg3 && arg3 < distance) {
          } else {
            world.getEntity(p.id).applyImpulse({x:((p.location.x-loc.x)*arg2/Math.max(1,distance)),y:((p.location.y-loc.y)*arg2/Math.max(1,distance)),z:((p.location.z-loc.z)*arg2/Math.max(1,distance))})
          }
        })
      })
      return {message: "Successfully applied punch", status: 0}
    }
  })
});
system.runInterval(()=>{
  const cd = world.scoreboard.getObjective("cmd:countdown")
  cd.getParticipants().forEach(p => {
    if (p.displayName.startsWith("block:") && cd.getScore(p) === 0) {
      let player = world.getEntity(p.displayName.slice(6))
      player.removeTag("gm:blocking")
    }
  })
	let players = Array.from(world.getPlayers())
  players.forEach(player => {
    const h = player.getComponent("minecraft:player.hunger")
    const x = player.location.x
    const y = player.location.y
    const z = player.location.z
    h.setCurrentValue(20)
    if (x < 218 && y > 136 && z > 202 && x > 202 && z < 217) {
      if (cd.hasParticipant("stagetime1")) {
        const t = (2*Math.PI*(cd.getScore("stagetime1")-1110))/80
        const camX = Math.round((210.5+2*Math.cos(t))*100)/100
        const camZ = Math.round((209.5+2*Math.sin(t))*100)/100
        player.runCommand(`execute if score "stagetime1" "cmd:countdown" matches 1110..1190 run camera @a set minecraft:free pos ${camX} 139 ${camZ} facing 210.5 139 209.5`)
      }
      player.runCommand("function stages/stage1")
    }
    if (x > 490 && z > 478 && x < 540 && z < 528) {
      player.runCommand("function stages/chamber_stage1")
    }
    if (x > 126 && x < 175 && z > 1052 && z < 1107) {
      player.runCommand("function stages/chamber_stage2")
    }
    if (x > -81 && z > -50 && x < -48 && z < -16) {
      player.runCommand("function stages/chamber_stage3")
    }
    if (player.hasTag("ingame")) {
      player.runCommand("execute unless entity @s[hasitem={item=totem_of_undying}] run function states/failed")
    }
    if (player.hasTag("blacksoul")) {
      player.runCommand("function states/blacksoul")
    }
  })
});
world.afterEvents.itemUse.subscribe(t => {
  let item = t.itemStack
  let player = t.source
  if (item.typeId == "minecraft:snowball") {
    player.teleport({x:210,y:137,z:202})
    player.runCommand('give @s[hasitem={item=snowball,quantity=!1..}] snowball 1 0 {"minecraft:item_lock":{"mode":"lock_in_inventory"}}')
  }
});
world.afterEvents.entityHitEntity.subscribe(t => {
});
world.beforeEvents.playerInteractWithEntity.subscribe(t => {
})