
import { world, system, ItemStack, EntityComponentTypes, EquipmentSlot, TicksPerSecond, ItemComponentTypes } from '@minecraft/server';

var shootingPlayers = [];

const projectileTypes = ['loth_sw:photon_thrower_projectile', 'loth_sw:electro_dagger_projectile', 'loth_sw:phase_blaster_projectile', 'loth_sw:static_chargers_projectile', 'loth_sw:plasma_pulse_projectile', 'loth_sw:flux_equalizer_projectile'];
var phase_blaster_power = 0
var static_chargers_power = 3
var plasma_pulse_power = 2
var flux_equalizer_power = 3

world.beforeEvents.worldInitialize.subscribe(event => {
  event.itemComponentRegistry.registerCustomComponent('loth_sw:axe', {
    onMineBlock: (eventData) => reduceDurability(eventData.source, eventData.itemStack, 1)
  });
})

system.afterEvents.scriptEventReceive.subscribe(event => {
  switch (event.id) {
    case 'loth_sw:mech_shoot':
      launchProjectile(event.sourceEntity, 'loth_sw:mech_projectile', 2.5, { x: 0, y: -1.5, z: 2 });
      break;
    case 'loth_sw:photon_thrower_pickup':
      const projectileEntity = event.sourceEntity;
      if (!projectileEntity) return;
      const player = projectileEntity.dimension.getPlayers({closest: 1, location: projectileEntity.location})[0];
      const newItem = new ItemStack('loth_sw:photon_thrower');
      newItem.getComponent(ItemComponentTypes.Durability).damage = projectileEntity.getProperty('prop:damage') ?? 0;
      player.dimension.spawnItem(newItem, player.location);
      player.playSound('armor.equip_iron');
      projectileEntity.remove();
      break;
    case 'loth_sw:electro_dagger_pickup':
      if (!event.sourceEntity) return;
      event.sourceEntity.dimension.spawnItem(new ItemStack('loth_sw:electro_dagger'), event.sourceEntity.location);
      break;
    case 'loth_sw:projectile_in_block_check':
      if (!event.sourceEntity) return;
      const block = event.sourceEntity.dimension.getBlock(event.sourceEntity.location);
      if (!block.isAir && !block.isLiquid) {
        event.sourceEntity.clearVelocity();
        event.sourceEntity.setProperty('prop:on_ground', true);
        const nearestAir = getNearestAirBlock(event.sourceEntity.location, event.sourceEntity.dimension);
        if (nearestAir){
          event.sourceEntity.teleport(nearestAir.location);
        }else {
          event.sourceEntity.teleport({ x: event.sourceEntity.location.x, y: event.sourceEntity.location.y + 1, z: event.sourceEntity.location.z });
        }
      }
      break;
    case 'loth_sw:projectile_hit_check': {
      if (!event.sourceEntity) return;
        const block = checkForBlocks(event.sourceEntity.location, event.sourceEntity.dimension);
        if (block) event.sourceEntity.triggerEvent('loth_sw:on_hit');
      break;
    }
    default:
      break;
  }
})

world.afterEvents.playerSpawn.subscribe(event => {
  const { player } = event;
  if (player.getTags().includes('loth_sw_intro')) return;
  player.removeTag('loth_sw_fly_hint');
  player.dimension.spawnItem(new ItemStack('loth_sw:guidebook'), player.location)
  player.addTag('loth_sw_intro')
})

world.afterEvents.projectileHitBlock.subscribe(event => {
  const projectile = event.projectile
  if (projectile.typeId.includes('loth_sw:weapon')) {
    system.runTimeout(() => {
      if (projectile.isValid()) projectile.remove();
    }, 3)

  }
})

world.afterEvents.projectileHitEntity.subscribe(event => {
  try {
    const projectile = event.projectile
    switch (projectile.typeId) {
      case 'loth_sw:photon_thrower_projectile': {
        const target = event.getEntityHit().entity;
        if (!target) return;
        var location = 0;
        var dim = 0;
        try {
          if (target.matches({ families: ['loth_sw_projectile'] })) return;
          const health = target.getComponent(EntityComponentTypes.Health);
          target.dimension.spawnParticle('loth_sw:lightning_hit', target.getHeadLocation());
          target.dimension.playSound('loth_sw.laser_zap', target.getHeadLocation());
          const shooter = event.source;
          if (shooter && health && health.currentValue == 0) {
            shooter.addEffect('regeneration', TicksPerSecond * 5, { amplifier: 1, showParticles: false })
          }
        } catch (error) {
          return;
        }
        return;
      }
      // case 'loth_sw:electro_dagger_projectile': {
      //   const target = event.getEntityHit().entity;
      //   if (!target) return
      //   if (!target.matches({ families: ['loth_sw_projectile'] })) return;
      //   target.dimension.spawnParticle('loth_sw:lightning_hit', target.getHeadLocation());
      //   target.dimension.playSound('loth_sw.laser_zap', target.getHeadLocation());
      //   event.projectile.remove();
      //   return;
      //   break;
      // }
      case 'loth_sw:phase_blaster_projectile': {
        const target = event.getEntityHit().entity;
        if (!target) return
        var location = 0;
        var dim = 0;
        try {
          if (target.matches({ families: ['loth_sw_projectile'] })) return;
          dim = target?.dimension;
          if (dim) {
            location = target.getHeadLocation();
            dim.spawnParticle('loth_sw:lightning_hit', location);
            dim.playSound('loth_sw.laser_zap', location);
            target.addEffect('slowness', TicksPerSecond * 2, { amplifier: 1, showParticles: true })
          }
        } catch (error) {
          return
        }
        return;
      }
      default:
        break;
    }
    if (projectile.typeId.includes('loth_sw')) {
      const target = event.getEntityHit().entity;
      if (!target) return;
      var dim = 0;
      var location = 0;
      try {
        if (target.matches({ families: ['loth_sw_projectile'] })) return;
        dim = target.dimension;
        location = target.getHeadLocation();
      } catch (error) {
        return
      }
      dim.spawnParticle('loth_sw:lightning_hit', location);
      dim.playSound('loth_sw.laser_zap', location);
    }
  } catch (error) {
    world.sendMessage(`${error}`);
    return;
  }
})

world.afterEvents.entityHitEntity.subscribe(event => {
  const player = event.damagingEntity;
  const playerEquipment = player.getComponent(EntityComponentTypes.Equippable);
  const equipHand = playerEquipment.getEquipment(EquipmentSlot.Mainhand);
  if (equipHand && (equipHand.typeId.includes('loth_sw:spectral_scythe') || equipHand.typeId.includes('loth_sw:static_slicer') || equipHand.typeId.includes('loth_sw:fusion_axe'))) {
    const target = event.hitEntity;
    target.dimension.spawnParticle('loth_sw:lightning_hit', target.getHeadLocation());
    target.dimension.playSound('loth_sw.laser_zap', target.getHeadLocation());
  }
}, { entityTypes: ['minecraft:player'] })

world.afterEvents.itemStartUse.subscribe(event => {
  if (!event.itemStack) return;
  switch (event.itemStack.type.id) {
    case 'loth_sw:photon_thrower': {
      const player = event.source;
      player.playAnimation('animation.loth_sw_player_raise_arm_high', {stopExpression: "!q.is_using_item", blendOutTime: 0.1});
      break;
    }
    case 'loth_sw:electro_dagger': {
      const player = event.source;
      player.playAnimation('animation.loth_sw_player_raise_arm_high', {stopExpression: "!q.is_using_item", blendOutTime: 0.1});
      break;
    }
    case 'loth_sw:phase_blaster': {
      const player = event.source;
      const hitEntities = player.getEntitiesFromViewDirection({ maxDistance: 3 })
      if (hitEntities.length > 0) phase_blaster_power = 1;
      else phase_blaster_power = 3;
      event.source.setDynamicProperty('phase_blaster_timer', 4);
      player.playAnimation('animation.loth_sw_player_raise_arm_phase_blaster', {stopExpression: "!q.is_using_item"});
      shootingPlayers.push(player);
      break;
    }
    case 'loth_sw:static_chargers': {
      const player = event.source;
      const hitEntities = player.getEntitiesFromViewDirection({ maxDistance: 3 })
      if (hitEntities.length > 0) static_chargers_power = 1;
      else static_chargers_power = 3;
      event.source.setDynamicProperty('static_chargers_timer', 2);
      player.playAnimation('animation.loth_sw_player_raise_arm_normal_both', {stopExpression: "!q.is_using_item"});
      shootingPlayers.push(player);
      break;
    }
    case 'loth_sw:plasma_pulse': {
      const player = event.source;
      const hitEntities = player.getEntitiesFromViewDirection({ maxDistance: 3 })
      if (hitEntities.length > 0) plasma_pulse_power = 1;
      else plasma_pulse_power = 2;
      event.source.setDynamicProperty('plasma_pulse_timer', 10);
      player.playAnimation('animation.loth_sw_player_raise_arm_plasma_pulse', {stopExpression: "!q.is_using_item"});
      shootingPlayers.push(player);
      break;
    }
    case 'loth_sw:flux_equalizer': {
      const player = event.source;
      const hitEntities = player.getEntitiesFromViewDirection({ maxDistance: 3 })
      if (hitEntities.length > 0) flux_equalizer_power = 1;
      else flux_equalizer_power = 3;
      event.source.setDynamicProperty('flux_equalizer_timer', 6);
      player.playAnimation('animation.loth_sw_player_raise_arm_flux_equalizer', {stopExpression: "!q.is_using_item"});
      shootingPlayers.push(player);
      break;
    }
    default:
      break;
  }
})

world.afterEvents.itemStopUse.subscribe(event => {
  if (!event.itemStack) return;
  switch (event.itemStack.type.id) {
    case 'loth_sw:phase_blaster': {
      event.source.setDynamicProperty('phase_blaster_timer', 0);
      shootingPlayers = shootingPlayers.filter(player => player.id != event.source.id);
      applyDurabilityDamage(event.source);
      break;
    }
    case 'loth_sw:static_chargers': {
      event.source.setDynamicProperty('static_chargers_timer', 0);
      shootingPlayers = shootingPlayers.filter(player => player.id != event.source.id);
      applyDurabilityDamage(event.source);
      break;
    }
    case 'loth_sw:plasma_pulse': {
      event.source.setDynamicProperty('plasma_pulse_timer', 0);
      shootingPlayers = shootingPlayers.filter(player => player.id != event.source.id);
      applyDurabilityDamage(event.source);
      break;
    }
    case 'loth_sw:flux_equalizer': {
      event.source.setDynamicProperty('flux_equalizer_timer', 0);
      shootingPlayers = shootingPlayers.filter(player => player.id != event.source.id);
      applyDurabilityDamage(event.source);
      break;
    }
    default:
      break;
  }
})

world.afterEvents.itemReleaseUse.subscribe(event => {
    if (!event.itemStack) return;
    switch (event.itemStack.type.id) {
      case 'loth_sw:photon_thrower': {
        const player = event.source;
        const playerEquippable = player.getComponent(EntityComponentTypes.Equippable);
        // if (200000 - event.useDuration < 10) break;
        const damage = event.itemStack.getComponent(ItemComponentTypes.Durability).damage;
        launchProjectile(player, 'loth_sw:photon_thrower_projectile', 1.5, { x: 0.1, y: 0, z: 1.7 }, damage + 5);
        if (event.itemStack.amount > 1) {
          event.itemStack.amount -= 1;
          playerEquippable.setEquipment(EquipmentSlot.Mainhand, event.itemStack);
        } else {
          playerEquippable.setEquipment(EquipmentSlot.Mainhand, undefined);
        }
        break;
      }
      case 'loth_sw:electro_dagger': {
        const player = event.source;
        const playerEquippable = player.getComponent(EntityComponentTypes.Equippable);
        // if (200000 - event.useDuration < 10) break;
        launchProjectile(player, 'loth_sw:electro_dagger_projectile', 1.3, { x: 0.1, y: 0, z: 1.7 });
        if (event.itemStack.amount > 1) {
          event.itemStack.amount -= 1;
          playerEquippable.setEquipment(EquipmentSlot.Mainhand,  event.itemStack);
        } else {
          playerEquippable.setEquipment(EquipmentSlot.Mainhand, undefined);
        }
        break;
      }
      default:
        break;
    }
})

function launchProjectile(player, identifier, power = 1.3, offset = { x: 0, y: 0, z: 0 }, damage = 0) {
  const playerRot = player.getRotation();
  const playerHead = player.getHeadLocation();
  const viewDir = player.getViewDirection();
  const velocity = { x: power, y: power, z: power };
  var rotRad = { ...playerRot };
  rotRad.y = (-playerRot.y + 360) % 360 / 180 * Math.PI;
  rotRad.x = (playerRot.x) % 360 / 180 * Math.PI;

  var forwardVector = {
    x: Math.cos(rotRad.x) * Math.sin(rotRad.y),
    y: -Math.sin(rotRad.x),
    z: Math.cos(rotRad.x) * Math.cos(rotRad.y)
  };
  var rightVector = {
    x: -Math.cos(rotRad.y),
    y: 0,
    z: Math.sin(rotRad.y),
  };
  var upVector = crossProduct(rightVector, forwardVector);
  
  upVector.x = upVector.x * offset.y;
  upVector.y = upVector.y * offset.y;
  upVector.z = upVector.z * offset.y;

  rightVector.x = rightVector.x * offset.x;
  rightVector.y = rightVector.y * offset.x;
  rightVector.z = rightVector.z * offset.x;

  forwardVector.x = forwardVector.x * offset.z;
  forwardVector.y = forwardVector.y * offset.z;
  forwardVector.z = forwardVector.z * offset.z;

  forwardVector.x = Math.round(( forwardVector.x + Number.EPSILON) * 100) / 100;
  forwardVector.y = Math.round(( forwardVector.y + Number.EPSILON) * 100) / 100;
  forwardVector.z = Math.round(( forwardVector.z + Number.EPSILON) * 100) / 100;

  upVector.x = Math.round(( upVector.x + Number.EPSILON) * 100) / 100;
  upVector.y = Math.round(( upVector.y + Number.EPSILON) * 100) / 100;
  upVector.z = Math.round(( upVector.z + Number.EPSILON) * 100) / 100;

  rightVector.x = Math.round(( rightVector.x + Number.EPSILON) * 100) / 100;
  rightVector.y = Math.round(( rightVector.y + Number.EPSILON) * 100) / 100;
  rightVector.z = Math.round(( rightVector.z + Number.EPSILON) * 100) / 100;
  

  const projectile = player.dimension.spawnEntity(identifier, {
    x: playerHead.x + forwardVector.x + upVector.x + rightVector.x,
    y: playerHead.y + forwardVector.y + upVector.y + rightVector.y + 0.1,
    z: playerHead.z + forwardVector.z + upVector.z + rightVector.z,
  });

  projectile.setProperty('prop:rot_y', playerRot.y);
  projectile.setProperty('prop:rot_x', playerRot.x);
  if (damage) projectile.setProperty('prop:damage', damage);

  const projectileComp = projectile.getComponent(EntityComponentTypes.Projectile);
  projectileComp.owner = player;
  projectileComp.stopOnHit = true;
  projectileComp?.shoot({ x: viewDir.x * velocity.x, y: viewDir.y * velocity.y, z: viewDir.z * velocity.z }, { owner: player });

  return projectile;
}

function loopitemUsers() {
  for (const player of shootingPlayers) {
    const phase_blaster_timer = player.getDynamicProperty('phase_blaster_timer');
    if (phase_blaster_timer) {
      if (phase_blaster_timer > 1) {
        if (phase_blaster_timer == 4) {
          launchProjectile(player, 'loth_sw:phase_blaster_projectile', phase_blaster_power, { x: 0.30, y: -0.15, z: 1.3 });
        }
        player.setDynamicProperty('phase_blaster_timer', phase_blaster_timer - 1);
      } else if (phase_blaster_timer == 1) {
        launchProjectile(player, 'loth_sw:phase_blaster_projectile', phase_blaster_power, { x: 0.30, y: -0.15, z: 1.3 });
        player.setDynamicProperty('phase_blaster_timer', 2);
      }
      continue
    }

    const static_chargers_timer = player.getDynamicProperty('static_chargers_timer');
    if (static_chargers_timer) {
      if (static_chargers_timer > 1) {
        launchProjectile(player, 'loth_sw:static_chargers_projectile', static_chargers_power, { x: 0.42, y: -0.2, z: 1.6 });
        player.setDynamicProperty('static_chargers_timer', static_chargers_timer - 1);
      } else if (static_chargers_timer == 1) {
        launchProjectile(player, 'loth_sw:static_chargers_projectile', static_chargers_power, { x: -0.38, y: -0.2, z: 1.6 });
        player.setDynamicProperty('static_chargers_timer', 2);
      }
      continue
    }

    const plasma_pulse_timer = player.getDynamicProperty('plasma_pulse_timer');
    if (plasma_pulse_timer) {
      if (plasma_pulse_timer > 1) {
        if (plasma_pulse_timer == 10) {
          launchProjectile(player, 'loth_sw:plasma_pulse_projectile', plasma_pulse_power, { x: 0.4, y: -0.2, z: 1.5 });
        }
        player.setDynamicProperty('plasma_pulse_timer', plasma_pulse_timer - 1);
      } else if (plasma_pulse_timer == 1) {
        player.setDynamicProperty('plasma_pulse_timer', 10);
      }
      continue
    }

    const flux_equalizer_timer = player.getDynamicProperty('flux_equalizer_timer');
    if (flux_equalizer_timer) {
      if (flux_equalizer_timer > 1) {
        if (flux_equalizer_timer == 6) {
          launchProjectile(player, 'loth_sw:flux_equalizer_projectile', flux_equalizer_power, { x: 0.25, y: -0.15, z: 1.7 });
        }
        player.setDynamicProperty('flux_equalizer_timer', flux_equalizer_timer - 1);
      } else if (flux_equalizer_timer == 1) {
        player.setDynamicProperty('flux_equalizer_timer', 6);
      }
      continue
    }
  }
}

function loopPlayers() {
  for (const player of world.getAllPlayers()) {
    if (player.getComponent(EntityComponentTypes.Riding)?.entityRidingOn?.typeId == 'loth_sw:mech_robot'){
      player.playAnimation("animation.loth_sw_mech_player_ride");
    }
    var megaCounter = 0;
    var ultraCounter = 0;
    const playerEquipment = player.getComponent(EntityComponentTypes.Equippable);
    const equipHead = playerEquipment.getEquipment(EquipmentSlot.Head);
    const equipChest = playerEquipment.getEquipment(EquipmentSlot.Chest);
    const equipLegs = playerEquipment.getEquipment(EquipmentSlot.Legs);
    const equipFeet = playerEquipment.getEquipment(EquipmentSlot.Feet);
    if (equipHead && equipHead.typeId.includes('loth_sw:helmet_mega_armor')) {
      megaCounter += 1;
      player.addEffect('night_vision', TicksPerSecond * 14, { amplifier: 2, showParticles: false });
    }
    if (equipChest && equipChest.typeId.includes('loth_sw:chestplate_mega_armor')) {
      megaCounter += 1;
      player.addEffect('health_boost', TicksPerSecond * 14, { amplifier: 4, showParticles: false });
    }
    if (equipLegs && equipLegs.typeId.includes('loth_sw:leggings_mega_armor')) {
      megaCounter += 1;
      player.addEffect('strength', TicksPerSecond * 14, { amplifier: 2, showParticles: false });
    }
    if (equipFeet && equipFeet.typeId.includes('loth_sw:boots_mega_armor')) {
      megaCounter += 1;
      if (player.isSneaking && player.isFalling) {
        player.addEffect('slow_falling', TicksPerSecond, { amplifier: 2, showParticles: false });
      }
    }
    if (megaCounter > 0) {
      player.addEffect('resistance', TicksPerSecond * 14, { amplifier: megaCounter, showParticles: false });
    }

    if (equipHead && equipHead.typeId.includes('loth_sw:helmet_ultra_armor')) ultraCounter += 1;
    if (equipChest && equipChest.typeId.includes('loth_sw:chestplate_ultra_armor')) {
      ultraCounter += 1;
      if (player.isSneaking) {
        const viewDir = player.getViewDirection();
        player.applyKnockback(viewDir.x, viewDir.z, 0.8, viewDir.y);
        player.addEffect('levitation', TicksPerSecond, { amplifier: 1, showParticles: false });
      }
    }
    if (equipLegs && equipLegs.typeId.includes('loth_sw:leggings_ultra_armor')) {
      ultraCounter += 1;
      player.addEffect('haste', TicksPerSecond * 14, { amplifier: 1, showParticles: false });
    }
    if (equipFeet && equipFeet.typeId.includes('loth_sw:boots_ultra_armor')) {
      ultraCounter += 1;
      player.addEffect('jump_boost', TicksPerSecond * 14, { amplifier: 3, showParticles: false });
    }
    if (ultraCounter > 3) {
      player.addEffect('resistance', TicksPerSecond * 14, { amplifier: 3, showParticles: false });
      player.addEffect('speed', TicksPerSecond * 14, { amplifier: 3, showParticles: false });
      if (player.matches({'excludeTags': ['loth_sw_fly_hint']})) {
        player.runCommand('titleraw @s actionbar {"rawtext": [{"translate": "loth_sw.hint.ultra_armor"}]}');
        player.addTag('loth_sw_fly_hint');
      }
    }
  }
}

function applyDurabilityDamage(player) {
  if (!player) return;
  const playerEquipment = player.getComponent(EntityComponentTypes.Equippable);
  const equipHand = playerEquipment.getEquipment(EquipmentSlot.Mainhand);
  const durabilityComponent = equipHand?.getComponent(ItemComponentTypes.Durability);
  if (!durabilityComponent) return;
  const { unbreakingLevel } = getRelevantEnchantments(equipHand);

  if (Math.random() < 1 / (unbreakingLevel + 1)) {
    const newDamage = durabilityComponent.damage + 1;
    if (newDamage >= durabilityComponent.maxDurability) {
      playerEquipment.setEquipment(EquipmentSlot.Mainhand, undefined);
      player.playSound("random.break");
    } else {
      durabilityComponent.damage = newDamage;
      playerEquipment.setEquipment(EquipmentSlot.Mainhand, equipHand);
    }
  }
}

function getRelevantEnchantments(item) {
  let unbreakingLevel = 0;

  try {
    const enchantableComponent = item.getComponent(ItemComponentTypes.Enchantable);
    if (enchantableComponent) {
      const enchantments = enchantableComponent.getEnchantments();
      for (const enchant of enchantments) {
        if (enchant.type.id === "unbreaking") {
          unbreakingLevel = enchant.level;
        }
      }
    }
  } catch (error) {
    console.warn("Error checking enchantments:", error);
  }
  return { unbreakingLevel };
}

function getNearestAirBlock(location, dimension) {
  for (let x = -1; x <= 1; x++) {
    for (let y = -1; y <= 1; y++) {
      for (let z = -1; z <= 1; z++) {
        const block = dimension.getBlock({ x: location.x + x, y: location.y + y, z: location.z + z });
        if (block.isAir || block.isLiquid) {
          return block;
        }
      }
    }
  }
  return undefined
}

function checkForBlocks(location, dimension) {
  for (let x = -1; x <= 1; x++) {
    for (let y = -1; y <= 1; y++) {
      for (let z = -1; z <= 1; z++) {
        const block = dimension.getBlock({ x: location.x + x, y: location.y + y, z: location.z + z });
        if (!block.isAir && !block.isLiquid) {
          return block;
        }
      }
    }
  }
  return undefined
}

//accepts an integer value and returns true/false for whether a tool should reduce durability on an item.
function shouldDamageItem(unbreakingLevel) {
  return 1 / (1 + (unbreakingLevel ?? 0)) >= Math.random();
}

//accepts an entity, the item stack, and the damage that will be applied to the item. Also handles breaking the item if it were to run out of durability.
function reduceDurability(entity, itemStack, damageAmount) {
  const durability = itemStack.getComponent("minecraft:durability");
  const inventory = entity.getComponent("minecraft:inventory").container;
  if(durability.damage + damageAmount >= durability.maxDurability) return (inventory.setItem(entity.selectedSlotIndex, undefined), entity.playSound('random.break'))
  return (itemStack.getComponent("minecraft:durability").damage += damageAmount, inventory.setItem(entity.selectedSlotIndex, itemStack));
}

function crossProduct(vectorA, vectorB) {
  const a = [vectorA.x, vectorA.y, vectorA.z];
  const b = [vectorB.x, vectorB.y, vectorB.z];
  return {x:a[1] * b[2] - a[2] * b[1], y: a[2] * b[0] - a[0] * b[2], z: a[0] * b[1] - a[1] * b[0]};
}


system.runInterval(loopitemUsers, 1);
system.runInterval(loopPlayers, 1);