import * as mc from "@minecraft/server";

class FenceUpdate {
  constructor(sourceBlock, newState) {
    this.sourceBlock = sourceBlock;
    this.newState = newState;
    setState(this.sourceBlock, this.newState);
    this.id = `${Date.now()}_${sourceBlock.x}${sourceBlock.y}${sourceBlock.z}`;
    this.fences = [];
    this.ended = false;
    this.timer = 20;
    // mc.world.sendMessage(`Started FenceUpdate ${this.id}`);
  }

  updateFence(block) {
    if (!this.hasFence(block)) {
      let fence = new Fence(block);
      this.fences.push(fence);
      setState(block, this.newState);
      toggleNeighbors(block, this.id);
      this.timer = 20;
    }
  }

  hasFence(block) {
    let fence = new Fence(block);
    let fenceExists = false;
    for (let i = 0; i < this.fences.length; i++) {
      if (this.fences[i].id === fence.id) {
        fenceExists = true;
        break;
      }
    }
    return fenceExists;
  }

  destroy() {
    // mc.world.sendMessage(`Ended FenceUpdate ${this.id}. Updated ${this.fences.length} fences`);
    this.ended = true;
  }
}

class Fence {
  constructor(block) {
    this.block = block;
    this.id = `${block.x}${block.y}${block.z}`;
  }
}

class FenceUpdateManager {
  constructor() {
    this.fenceUpdates = [];
  }

  newUpdate(sourceBlock, newState) {
    let fu = new FenceUpdate(sourceBlock, newState);
    this.fenceUpdates.push(fu);
    return fu.id;
  }

  updateFence(block, update) {
    for (let i = this.fenceUpdates.length - 1; i >= 0; i--) {
      if (this.fenceUpdates[i].id === update) {
        this.fenceUpdates[i].updateFence(block);
      }
    }
  }

  update() {
    for (let i = this.fenceUpdates.length - 1; i >= 0; i--) {
      this.fenceUpdates[i].timer -= 1;
      if (this.fenceUpdates[i].timer === 0) this.fenceUpdates[i].destroy();
      if (this.fenceUpdates[i].ended) this.fenceUpdates.splice(i, 1)[0];
    }
  }
}

const FenceManager = new FenceUpdateManager();

export function registerComponent(event) {
  event.blockComponentRegistry.registerCustomComponent("hf_da:electric_fence", {
    onTick(e) {
      const { block } = e;
      if (block.isValid()) {
        if (block.permutation.getState("hf_da:is_on")) {
          const loc = block.location;
          loc.x = Math.floor(loc.x) + 0.5;
          loc.y = Math.floor(loc.y) + 0.5;
          loc.z = Math.floor(loc.z) + 0.5;
          block.dimension.spawnParticle("hf_da:electric_fence", loc);
          const entities = block.dimension.getEntities({ location: loc, maxDistance: 1, excludeTypes: ["minecraft:item", "minecraft:arrow", "minecraft:armor_stand"] });
          for (const entity of entities) {
            entity.applyDamage(2, { cause: mc.EntityDamageCause.contact });
          }
          block.dimension.playSound("hf_da:electric_fence.buzzing", loc, { volume: 0.5, pitch: 1.0 });
        }
      }
    },
  });
}

export function triggerToggle(event) {
  const { player, block, isFirstEvent, itemStack } = event;
  mc.system.run(() => {
    if (isFirstEvent && !itemStack && player && player.isValid() && player.typeId === "minecraft:player" && block && block.isValid() && block.typeId === "hf_da:electric_fence") {
      let blockId = `${block.x}${block.y}${block.z}`;
      if (player.getDynamicProperty("hf_da:last_fence_update") != blockId) {
        player.setDynamicProperty("hf_da:last_fence_update", blockId);
        toggleState(block);

        mc.system.runTimeout(() => {
          player.setDynamicProperty("hf_da:last_fence_update", null);
        }, 10);
      } else {
        let newState = block.permutation.getState("hf_da:is_on");
        let update = FenceManager.newUpdate(block, newState);
        toggleNeighbors(block, update);
      }
    }
  });
}

function toggleState(block) {
  mc.system.run(() => {
    block.setPermutation(
      mc.BlockPermutation.resolve(block.typeId, {
        "hf_da:is_on": !block.permutation.getState("hf_da:is_on"),
        "hf_da:north_connection": block.permutation.getState("hf_da:north_connection"),
        "hf_da:south_connection": block.permutation.getState("hf_da:south_connection"),
        "hf_da:east_connection": block.permutation.getState("hf_da:east_connection"),
        "hf_da:west_connection": block.permutation.getState("hf_da:west_connection"),
      })
    );
  });
}

function setState(block, state) {
  mc.system.run(() => {
    block.setPermutation(
      mc.BlockPermutation.resolve(block.typeId, {
        "hf_da:is_on": state,
        "hf_da:north_connection": block.permutation.getState("hf_da:north_connection"),
        "hf_da:south_connection": block.permutation.getState("hf_da:south_connection"),
        "hf_da:east_connection": block.permutation.getState("hf_da:east_connection"),
        "hf_da:west_connection": block.permutation.getState("hf_da:west_connection"),
      })
    );
  });
}

function toggleNeighbors(block, update) {
  const above = block.above();
  const below = block.below();
  const north = block.north();
  const east = block.east();
  const south = block.south();
  const west = block.west();

  const neighborsToUpdate = [];

  if (above.typeId === "hf_da:electric_fence") neighborsToUpdate.push(above);
  if (below.typeId === "hf_da:electric_fence") neighborsToUpdate.push(below);
  if (north.typeId === "hf_da:electric_fence") neighborsToUpdate.push(north);
  if (east.typeId === "hf_da:electric_fence") neighborsToUpdate.push(east);
  if (south.typeId === "hf_da:electric_fence") neighborsToUpdate.push(south);
  if (west.typeId === "hf_da:electric_fence") neighborsToUpdate.push(west);

  // mc.world.sendMessage(`Toggling ${neighborsToUpdate.length} neighbors`);
  for (let neighbor of neighborsToUpdate) {
    FenceManager.updateFence(neighbor, update);
  }
}

export function onBlockUpdate(event, isBroken) {
  electricFenceUpdate(event.block, isBroken);
}

export function electricFenceUpdate(block, isBroken) {
  const north = block.north();
  const east = block.east();
  const south = block.south();
  const west = block.west();

  const neighborsToUpdate = [];
  let northConnection = false;
  let eastConnection = false;
  let southConnection = false;
  let westConnection = false;

  if (north.typeId === "hf_da:electric_fence") {
    northConnection = true;
    if (!north.permutation.getState("hf_da:south_connection") || isBroken) neighborsToUpdate.push(north);
  } else if (!north.isAir && !north.isLiquid) {
    northConnection = true;
  } else {
    northConnection = false;
  }
  if (east.typeId === "hf_da:electric_fence") {
    eastConnection = true;
    if (!east.permutation.getState("hf_da:west_connection") || isBroken) neighborsToUpdate.push(east);
  } else if (!east.isAir && !east.isLiquid) {
    eastConnection = true;
  } else {
    eastConnection = false;
  }
  if (south.typeId === "hf_da:electric_fence") {
    southConnection = true;
    if (!south.permutation.getState("hf_da:north_connection") || isBroken) neighborsToUpdate.push(south);
  } else if (!south.isAir && !south.isLiquid) {
    southConnection = true;
  } else {
    southConnection = false;
  }
  if (west.typeId === "hf_da:electric_fence") {
    westConnection = true;
    if (!west.permutation.getState("hf_da:east_connection") || isBroken) neighborsToUpdate.push(west);
  } else if (!west.isAir && !west.isLiquid) {
    westConnection = true;
  } else {
    westConnection = false;
  }

  if (!isBroken) {
    if (block.typeId === "hf_da:electric_fence")
      block.setPermutation(
        mc.BlockPermutation.resolve(block.typeId, {
          "hf_da:is_on": block.permutation.getState("hf_da:is_on"),
          "hf_da:north_connection": northConnection,
          "hf_da:south_connection": southConnection,
          "hf_da:east_connection": eastConnection,
          "hf_da:west_connection": westConnection,
        })
      );

    for (let neighbor of neighborsToUpdate) {
      electricFenceUpdate(neighbor, false);
    }
  } else {
    mc.system.runTimeout(() => {
      for (let neighbor of neighborsToUpdate) {
        electricFenceUpdate(neighbor, false);
      }
    }, 1);
  }

  // mc.world.sendMessage(JSON.stringify(neighborsToUpdate));
}

export function tick1() {
  FenceManager.update();
}
