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

const bookEntity = "tvf_randev:guide_book";
const entityBookQuery = { type: bookEntity };
export const bookItemId = "tvf_randev:guide_book_spawn_egg";

const back_animation_name = "tvf_randev:trigger_back_animation";
const back_event_name = "tvf_randev:guide_book_back";

const next_animation_name = "tvf_randev:trigger_forward_animation";
const next_event_name = "tvf_randev:guide_book_forward";

const wait_property_name = "tvf_randev:waiting_for_flip";

const timeToFlip = 5;

world.afterEvents.playerSpawn.subscribe((event) => {
  const { initialSpawn, player } = event;

  if (!initialSpawn) return;

  if (player.hasTag("tvf_randev:guide_book_given")) return;

  const book_item = new ItemStack(bookItemId, 1);
  player.dimension.spawnItem(book_item, player.location);

  player.addTag("tvf_randev:guide_book_given");
});

export function bookTick(dimension) {
  system.runInterval(() => {
    for (const book of dimension.overworld.getEntities(entityBookQuery)) {
      checkClicks(book);
    }
    for (const book of dimension.nether.getEntities(entityBookQuery)) {
      checkClicks(book);
    }
    for (const book of dimension.end.getEntities(entityBookQuery)) {
      checkClicks(book);
    }
  });
}

function checkClicks(book) {
  const isWaiting = book.getProperty(wait_property_name);

  if (isWaiting === undefined) {
    return;
  }

  if (isWaiting) {
    handleInteraction(book);
  }
}

function handleInteraction(book) {
  for (const player of book.dimension.getPlayers()) {
    let distance = vecDistance(book.location, player.location);

    if (distance > 4) {
      continue;
    }

    let point = add(player.location, player.getViewDirection());

    let fromPlayer = subtract(player.location, book.location);
    let toPoint = subtract(player.location, point);

    let sideToFlip = checkSide(fromPlayer, toPoint);

    if (sideToFlip == "left") {
      book.triggerEvent(back_animation_name);
      book.setProperty(wait_property_name, false);
      system.runTimeout(() => {
        book.triggerEvent(back_event_name);
      }, timeToFlip);
      continue;
    }

    book.triggerEvent(next_animation_name);
    book.setProperty(wait_property_name, false);
    system.runTimeout(() => {
      book.triggerEvent(next_event_name);
    }, timeToFlip);
    continue;
  }
}

function crossProduct(v, w) {
  return [v.y * w.z - v.z * w.y, v.z * w.x - v.x * w.z, v.x * w.y - v.y * w.x];
}

function checkSide(v, w) {
  var cross = crossProduct(v, w);
  var z = cross[1];
  if (z > 0) {
    return "left";
  } else if (z < 0) {
    return "right";
  } else {
    return "right";
  }
}

function vecDistance(vec1, vec2) {
  return Math.sqrt(
    Math.pow(vec1.x - vec2.x, 2) +
      Math.pow(vec1.y - vec2.y, 2) +
      Math.pow(vec1.z - vec2.z, 2)
  );
}

function add(a, b) {
  return {
    x: a.x + b.x,
    y: a.y + b.y,
    z: a.z + b.z,
  };
}
function subtract(a, b) {
  return {
    x: a.x - b.x,
    y: a.y - b.y,
    z: a.z - b.z,
  };
}
