import { world, EntityComponentTypes, EquipmentSlot, ItemStack, ItemLockMode, system, TicksPerSecond } from "@minecraft/server";
import { openMainMenu } from "./gui";
import { blocks, mobs, morphs } from "./data";
import { centerBlockMorph, runOnAttackAbilities, runOnInteractAbilities, runPassiveAbilities, stopCenterBlockMorph } from "./abilities";
import { failMorphPlayerHasHelmet, showMorphAbility, showMorphGenericAbility, showTeleportPrediction, successMorphUnlocked, successTransform } from "./player_communication";
import { getEntityId, isMobMorph } from "./util";
world.beforeEvents.worldInitialize.subscribe((event) => {
  event.itemComponentRegistry.registerCustomComponent("squaredreams_bam:morph_from_block", {
    onUseOn: detectBlockMorph
  });
  event.itemComponentRegistry.registerCustomComponent("squaredreams_bam:unlock_from_block", {
    onUseOn: unlockBlockMorph
  });
});
world.afterEvents.itemUse.subscribe((event) => {
  const player = event.source;
  const itemStack = event.itemStack;
  const morph = getMorph(player);
  if (itemStack.typeId === "squaredreams_bam:morph_bracelet" && player.isSneaking) {
    openMainMenu(player);
    unlockEntityMorph(player);
  }
  if (itemStack.typeId === "squaredreams_bam:morph_bracelet" && !player.isSneaking) {
    runOnInteractAbilities(player, itemStack, morph);
  }
  if (itemStack.typeId === "squaredreams_bam:morph_wand" && !player.isSneaking) {
    runOnInteractAbilities(player, itemStack, morph);
  }
});
world.beforeEvents.playerInteractWithEntity.subscribe((event) => {
  const { player, target, itemStack } = event;
  if (!player.isSneaking)
    return;
  if (itemStack === void 0 || itemStack.typeId !== "squaredreams_bam:morph_wand")
    return;
  const cancelEventMobs = ["minecraft:allay", "minecraft:villager_v2", "minecraft:horse", "minecraft:mule", "minecraft:camel", "minecraft:donkey", "minecraft:skeleton_horse", "minecraft:zombie_horse", "minecraft:wandering_trader"];
  if (cancelEventMobs.includes(target.typeId)) {
    event.cancel = true;
  }
  system.run(() => attemptMorphFromId(player, getEntityId(target), mobs));
});
world.afterEvents.entityHitEntity.subscribe((event) => {
  const player = event.damagingEntity;
  const entity = event.hitEntity;
  const morph = getMorph(player);
  if (player.typeId !== "minecraft:player" || morph.id === "player")
    return;
  runOnAttackAbilities(player, morph, entity);
});
world.afterEvents.playerBreakBlock.subscribe((event) => {
  const { player, brokenBlockPermutation } = event;
  checkUnlockMorph(player, brokenBlockPermutation.type.id);
});
world.afterEvents.entityDie.subscribe((event) => {
  const player = event.damageSource.damagingEntity;
  const entity = event.deadEntity;
  if (entity.typeId === "minecraft:player") {
    transform(entity, mobs[0]);
  }
  if (player === void 0 || player.typeId !== "minecraft:player")
    return;
  checkUnlockMorph(player, getEntityId(entity));
});
world.afterEvents.playerSpawn.subscribe((event) => {
  const { initialSpawn, player } = event;
  player.onScreenDisplay.setActionBar("");
  if (!initialSpawn)
    return;
  if (!player?.getDynamicProperty("squaredreams_bam:init")) {
    const manager = world.structureManager;
    const structure = manager.get("squaredreams_bam:addon_book");
    if (structure == void 0)
      return;
    manager.place(structure, player?.dimension, player?.location);
    player?.setDynamicProperty("squaredreams_bam:init", true);
  }
});
system.runInterval(() => {
  const players = world.getAllPlayers();
  for (const player of players) {
    const currentMorph = getMorph(player);
    setPlayerVisibility(player, currentMorph.id === "player");
    setPlayerBaby(player, currentMorph.id === "player" && getMorphBaby(player));
    if (getMorphFromHelmet(player) != currentMorph && player.getDynamicProperty("squaredreams_bam:block_frozen") === false) {
      startMorph(player, currentMorph);
    }
    checkBlockHiding(player);
    if (!player.isSprinting)
      setSpeed(player, currentMorph);
    runPassiveAbilities(player, currentMorph);
    showTeleportPrediction(player, ["enderman", "shulker"]);
  }
}, 1);
function transform(player, newMorph) {
  const oldMorph = getMorph(player);
  const equippableComponent = player.getComponent(EntityComponentTypes.Equippable);
  const helmet = equippableComponent.getEquipment(EquipmentSlot.Head);
  if (helmet != void 0 && !helmet?.typeId.includes("squaredreams_bam:morph."))
    return failMorphPlayerHasHelmet(player);
  stopMorph(player, oldMorph);
  startMorph(player, newMorph);
  successTransform(player, oldMorph, newMorph);
}
function startMorph(player, morph) {
  player.setDynamicProperty("squaredreams_bam:morph", morph.id);
  unlockMorph(player, morph);
  setPlayerVisibility(player, morph.id == "player");
  setMorphHelmet(player, morph);
  setPermenantEffects(player, morph);
  setSpeed(player, morph);
  system.runTimeout(() => {
    if (morph.has_ability)
      return showMorphAbility(player, morph);
    if (morph.can_fly)
      return showMorphGenericAbility(player, "fly");
    if (morph.variants.length > 0)
      return showMorphGenericAbility(player, "variant");
  }, 2 * TicksPerSecond);
}
function stopMorph(player, morph) {
  stopPermenantEffects(player, morph);
  resetSpeed(player);
}
function getMorph(player) {
  return morphs?.find((x) => x.id == player.getDynamicProperty("squaredreams_bam:morph")) ?? morphs[0];
}
function unlockEntityMorph(player) {
  const entities = player.getEntitiesFromViewDirection({ maxDistance: 5 });
  if (entities.length > 0) {
    checkUnlockMorph(player, getEntityId(entities[0].entity));
  }
}
function detectBlockMorph(event) {
  const player = event.source;
  if (!player.isSneaking)
    return false;
  return attemptMorphFromId(player, event.block.typeId, blocks);
}
function unlockBlockMorph(event) {
  const player = event.source;
  if (!player.isSneaking)
    return;
  checkUnlockMorph(player, event.block.typeId);
}
function attemptMorphFromId(player, id, validMorphs = morphs) {
  const oldMorph = getMorph(player);
  const newMorph = validMorphs.find((x) => `minecraft:${x.id}` === id);
  if (newMorph !== void 0 && newMorph.id !== oldMorph.id) {
    transform(player, newMorph);
    return true;
  }
  if (id === "minecraft:slime") {
    transform(player, blocks.find((x) => x.id === "slime_block"));
  }
  return false;
}
function unlockMorph(player, morph) {
  if (!getMorphUnlocked(player, morph)) {
    successMorphUnlocked(player, morph);
  }
  player.setDynamicProperty(`squaredreams_bam:unlock.${morph.id}`, true);
}
function lockMorph(player, morph) {
  player.setDynamicProperty(`squaredreams_bam:unlock.${morph.id}`, false);
}
function checkUnlockMorph(player, id) {
  const morph = morphs.find((x) => `minecraft:${x.id}` === id);
  if (morph === void 0)
    return;
  unlockMorph(player, morph);
}
function cycleMorph(player) {
  const currentMorph = getMorph(player);
  const morphIndex = morphs.findIndex((x) => x === currentMorph);
  transform(player, morphs[morphIndex + 1]);
}
function setMorphHelmet(player, morph) {
  const equippableComponent = player.getComponent(EntityComponentTypes.Equippable);
  if (equippableComponent.getEquipment(EquipmentSlot.Head)?.typeId == "squaredreams_bam:morph.hidden" && player.getDynamicProperty("squaredreams_bam:block_frozen") != false)
    return;
  if (morph.id == "player") {
    equippableComponent.setEquipment(EquipmentSlot.Head, new ItemStack("minecraft:air"));
    if (getMorphBaby(player)) {
      setPlayerBaby(player, true);
    } else {
      setPlayerBaby(player, false);
    }
    return;
  }
  var helmetId = `squaredreams_bam:morph.${morph.id}`;
  if (morph.variants.length > 0)
    helmetId += "." + morph.variants[getMorphVariant(player)];
  if (getMorphBaby(player) && !blocks.includes(morph))
    helmetId += ".baby";
  const newMorphHelmet = new ItemStack(helmetId);
  newMorphHelmet.lockMode = ItemLockMode.slot;
  newMorphHelmet.keepOnDeath = true;
  equippableComponent.setEquipment(EquipmentSlot.Head, newMorphHelmet);
  return;
}
function getMorphFromHelmet(player) {
  const equippableComponent = player.getComponent(EntityComponentTypes.Equippable);
  const helmet = equippableComponent.getEquipment(EquipmentSlot.Head);
  if (helmet !== void 0 && helmet.typeId.includes("squaredreams_bam:morph.")) {
    const helmetId = helmet.typeId.split(".")[1];
    return morphs?.find((x) => x.id === helmetId) ?? morphs[0];
  }
  return morphs[0];
}
function getMorphVariant(player) {
  const morph = getMorph(player);
  return player.getDynamicProperty(`squaredreams_bam:variant.${morph.id}`) ?? 0;
}
function getMorphBaby(player) {
  return player.getDynamicProperty(`squaredreams_bam:toggle.baby`) ?? false;
}
function setPermenantEffects(player, morph) {
  for (const effect of morph.effects)
    player.addEffect(effect.split("/")[0], 2e7, { showParticles: false, amplifier: Number(effect.split("/")[1] ?? 0) });
  if (morph.can_fly)
    player.addEffect("slow_falling", 2e7, { showParticles: false });
  return;
}
function stopPermenantEffects(player, morph) {
  for (const effect of morph.effects)
    player.removeEffect(effect.split("/")[0]);
  if (morph.can_fly)
    player.removeEffect("slow_falling");
  return;
}
function setSpeed(player, morph) {
  const groundMovement = player.getComponent(EntityComponentTypes.Movement);
  const underwaterMovment = player.getComponent(EntityComponentTypes.UnderwaterMovement);
  const lavaMovement = player.getComponent(EntityComponentTypes.LavaMovement);
  const landSpeed = morph.speed["land"] ?? 0.1;
  const underwaterSpeed = morph.speed["underwater"] ?? underwaterMovment.defaultValue;
  const lavaSpeed = morph.speed["lava"] ?? lavaMovement.defaultValue;
  groundMovement.setCurrentValue(landSpeed);
  underwaterMovment.setCurrentValue(underwaterSpeed);
  lavaMovement.setCurrentValue(lavaSpeed);
}
function resetSpeed(player) {
  const groundMovement = player.getComponent(EntityComponentTypes.Movement);
  const underwaterMovment = player.getComponent(EntityComponentTypes.UnderwaterMovement);
  const lavaMovement = player.getComponent(EntityComponentTypes.LavaMovement);
  underwaterMovment.resetToDefaultValue();
  lavaMovement.resetToDefaultValue();
  groundMovement.setCurrentValue(0.1);
}
function checkBlockHiding(player) {
  const blockFrozen = player.getDynamicProperty("squaredreams_bam:block_frozen");
  if (blockFrozen == false || blockFrozen == void 0)
    return;
  const equippableComponent = player.getComponent(EntityComponentTypes.Equippable);
  if (equippableComponent.getEquipment(EquipmentSlot.Head)?.typeId != "squaredreams_bam:morph.hidden") {
    const newMorphHelmet = new ItemStack("squaredreams_bam:morph.hidden");
    newMorphHelmet.lockMode = ItemLockMode.slot;
    newMorphHelmet.keepOnDeath = true;
    equippableComponent.setEquipment(EquipmentSlot.Head, newMorphHelmet);
  }
  const blockFrozenLoc = blockFrozen.split("/");
  const fakeBlockVector = { x: Number(blockFrozenLoc[0]), y: Number(blockFrozenLoc[1]), z: Number(blockFrozenLoc[2]) };
  const morph = getMorph(player);
  if (player.location.x != Number(blockFrozenLoc[0]) || player.location.y != Number(blockFrozenLoc[1]) || player.location.z != Number(blockFrozenLoc[2])) {
    return stopCenterBlockMorph(player, fakeBlockVector);
  }
  if (isMobMorph(morph)) {
    return stopCenterBlockMorph(player, fakeBlockVector);
  }
  const fakeBlock = player.dimension.getBlock(fakeBlockVector);
  if (fakeBlock == void 0)
    return;
  if (morph != getFakeBlockMorph(fakeBlock)) {
    centerBlockMorph(player);
  }
}
function setPlayerVisibility(player, bool) {
  if (bool) {
    player.playAnimation("animation.squaredreams_bam.player.player_visible", { nextState: "null", controller: `controller.animation.squaredreams_bam.player_visibility`, stopExpression: "0" });
  } else {
    player.playAnimation("animation.squaredreams_bam.player.player_invisible", { nextState: "null", controller: `controller.animation.squaredreams_bam.player_visibility`, stopExpression: "0" });
  }
}
function setPlayerBaby(player, bool) {
  if (bool) {
    player.playAnimation("animation.squaredreams_bam.player.baby", { nextState: "null", controller: `controller.animation.squaredreams_bam.player_baby`, stopExpression: "0" });
  } else {
    player.playAnimation("animation.squaredreams_bam.player.player_visible", { nextState: "null", controller: `controller.animation.squaredreams_bam.player_baby`, stopExpression: "0" });
  }
}
function getMorphUnlocked(player, morph) {
  if (getAllMorphsUnlocked(player)) {
    return true;
  } else {
    return player.getDynamicProperty(`squaredreams_bam:unlock.${morph.id}`) ?? false;
  }
}
function getAllMorphsUnlocked(player) {
  return player.getDynamicProperty("squaredreams_bam:toggle.unlock_all") ?? false;
}
function getFakeBlockMorph(block) {
  if (block.typeId != "squaredreams_bam:fake_block")
    return void 0;
  const textureBit0 = block.permutation.getState("squaredreams_bam:texture_bit_0");
  const textureBit1 = block.permutation.getState("squaredreams_bam:texture_bit_1");
  const textureBit2 = block.permutation.getState("squaredreams_bam:texture_bit_2");
  const textureBit = textureBit2 * 256 + textureBit1 * 16 + textureBit0;
  return blocks[textureBit];
}
export {
  getMorph,
  getMorphBaby,
  getMorphUnlocked,
  getMorphVariant,
  lockMorph,
  setMorphHelmet,
  transform
};
