import { system, BlockPermutation } from "@minecraft/server";
import { get_randomWithWeight } from "./time_events";

const collision_blocks = [
  "minecraft:grass_block",
  "minecraft:podzol",
  "minecraft:mycelium",
  "minecraft:sand",
  "minecraft:gravel",
  "minecraft:red_sand",
  "minecraft:sandstone",
  "minecraft:red_sandstone",
  "minecraft:dirt",
  "minecraft:stone",
  "minecraft:coarse_dirt",
  "minecraft:granite",
  "minecraft:diorite",
  "minecraft:andesite",
  "minecraft:tuff",
  "minecraft:deepslate",
  "minecraft:bedrock",
];
const structure_list = [
  {
    id: "tvf_randev/meteor_big_1",
    w: 10,
    bound: 0,
  },
  {
    id: "tvf_randev/meteor_big_2",
    w: 10,
    bound: 0,
  },
  {
    id: "tvf_randev/meteor_medium_1",
    w: 30,
    bound: 0,
  },
  {
    id: "tvf_randev/meteor_medium_2",
    w: 30,
    bound: 0,
  },
];
function meteor_MAIN(dimension) {
  system.runInterval(() => {
    dimension
      .getEntities({
        type: "tvf_randev:meteor",
      })
      .forEach((ent) => {
        let promise = collide(ent);
        promise.then(
          () => {
            playerCameraShake(ent, 50, 0.5, 2);
            place_structure(ent);
            ent.kill();
          },
          (error) => {
            if (error == "unloaded") ent.clearVelocity();
            else follow_path(ent);
          }
        );
      });
  }, 1);
}
function follow_path(meteor) {
  try {
    const dimension = meteor.dimension;
    const a = meteor.location;
    const followForce = 0.0005;
    let path;
    path = {
      x: meteor.getProperty("property:path_x"),
      y: meteor.getProperty("property:path_y"),
      z: meteor.getProperty("property:path_z"),
    };
    if (path.x == 0.0 && path.y == 0.0 && path.z == 0.0) {
      //initilize
      const nearPlayer = dimension.getPlayers({
        location: a,
        closest: 1,
      });
      if (nearPlayer.length == 0) return 0;
      let mod;
      if (Math.floor(Math.random() * 2) == 1) mod = 1;
      else mod = -1;
      let mod_1;
      if (Math.floor(Math.random() * 2) == 1) mod_1 = 1;
      else mod_1 = -1;
      let bX =
        nearPlayer[0].location.x + mod * (Math.floor(Math.random() * 20) + 40);
      let bZ =
        nearPlayer[0].location.z +
        mod_1 * (Math.floor(Math.random() * 20) + 40);
      let bY = dimension.getTopmostBlock({
        x: bX,
        z: bZ,
      }).y;
      const vector = {
        x: bX - a.x,
        y: bY - a.y,
        z: bZ - a.z,
      };
      const vector_length = Math.sqrt(
        vector.x * vector.x + vector.y * vector.y + vector.z * vector.z
      );
      const vector_norm = {
        x: (vector.x * 1000) / vector_length,
        y: (vector.y * 1000) / vector_length,
        z: (vector.z * 1000) / vector_length,
      };
      meteor.setProperty("property:path_x", vector_norm.x);
      meteor.setProperty("property:path_y", vector_norm.y);
      meteor.setProperty("property:path_z", vector_norm.z);
    }
    meteor.clearVelocity();
    meteor.applyImpulse({
      x: path.x * followForce,
      y: path.y * followForce,
      z: path.z * followForce,
    });
    playerCameraShake(meteor, 50, 0.05, 0.2);
  } catch {}
}
function playerCameraShake(meteorite, distance, duration, force) {
  meteorite.dimension
    .getEntities({
      type: "minecraft:player",
      location: meteorite.location,
      maxDistance: distance,
    })
    .forEach((player) => {
      player.runCommand(`/camerashake add @s ${force} ${duration} positional`);
    });
}
function place_structure(ent) {
  const blockPerm = BlockPermutation.resolve("minecraft:smooth_basalt");
  const fire = BlockPermutation.resolve("minecraft:fire");
  const magma = BlockPermutation.resolve("minecraft:magma");
  const center = ent.location;
  const maxBlockBreak = 200;
  let waitMax = 1000;
  const structureName = get_randomWithWeight(structure_list);
  function structureRotation() {
    const random = Math.random();
    if (random <= 0.25) return "0_degrees";
    else if (random > 0.25 && random <= 0.5) return "90_degrees";
    else if (random > 0.5 && random <= 0.75) return "180_degrees";
    else if (random > 0.75 && random <= 1.0) return "270_degrees";
  }
  ent.runCommand(
    `/structure load "${structureName}" ~-12 ~-7 ~-12 ${structureRotation()} none false true false `
  );
  ent.clearVelocity();
  system.runTimeout(() => {
    afterPlacingStructure(ent);
  }, 5);

  function afterPlacingStructure(ent) {
    const dimension = ent.dimension;
    dimension.createExplosion(ent.location, 10, {
      breaksBlocks: false,
    });
    system.runJob(
      (function* () {
        let i = 0;
        let temp = 0;
        for (let blockCounter = 0; blockCounter < maxBlockBreak; ) {
          if (blockCounter == temp) i++;
          else i = 0;
          if (i > waitMax) {
            break;
          }
          temp = blockCounter;
          const random_r = Math.random() * 15;
          const random_phi = Math.random() * 360;
          const location_cyl = cyl2decart(random_r, random_phi, 0);
          for (let dy = 0; dy < 8; dy++) {
            let location = {
              x: center.x + location_cyl.x,
              y: center.y - dy,
              z: center.z + location_cyl.z,
            };
            const block = dimension.getBlock(location);
            if (block == undefined) continue;
            if (block.isAir) continue;
            if (!block.above(1).isAir) continue;
            if (
              block.typeId == "minecaft:smooth_basalt" ||
              block.typeId == "tvf_randev:meteorite_stone" ||
              block.typeId == "tvf_randev:meteorite_ore" ||
              block.typeId == "minecraft:ancient_debris" ||
              block.typeId == "minecraft:lava" ||
              block.typeId == "minecraft:flowing_lava" ||
              block.typeId == "minecraft:mob_spawner" ||
              block.typeId == "minecraft:fire" ||
              block.typeId == "minecraft:magma"
            )
              continue;
            if (Math.floor(Math.random() * 7) == 1) {
              block.setPermutation(magma);
              dimension.getBlock(block.above(1).location).setPermutation(fire);
            } else block.setPermutation(blockPerm);
            blockCounter++;
            if (blockCounter > maxBlockBreak) break;
            yield;
          }
        }
      })()
    );
  }
}
function cyl2decart(r, phi, h) {
  const outputPoint = {
    x: r * Math.cos(phi),
    y: h,
    z: r * Math.sin(phi),
  };
  return outputPoint;
}
function collide(meteor) {
  return new Promise((resolve, reject) => {
    const currPos = meteor.location;
    const block = meteor.dimension.getBlock(currPos);
    if (!block) {
      reject(new Error("unloaded"));
      return;
    }
    if (collision_blocks.includes(block.typeId)) {
      resolve(true);
      return;
    } else {
      reject(new Error("not_collide"));
      return;
    }
  });
}
export { meteor_MAIN };
