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

export function getEntitiesArc(source, targetFamilies, distance, angle, inverse = false, customAngle = undefined) {
  let ret = [];
  const entities = union(
    {
      location: source.location,
      maxDistance: distance,
    },
    targetFamilies,
    source.dimension,
    source.id
  );

  const sourcePosition = vector.add(source.location, vector.up);

  for (let i = 0; i < entities.length; i++) {
    if (entities[i] === undefined) continue;
    //mc.world.sendMessage(`${entities[i].typeId}`)

    const targetPosition = vector.add(entities[i].location, vector.up);
    const dist = vector.subtract(targetPosition, sourcePosition);

    const obstruction = source.dimension.getBlockFromRay(sourcePosition, vector.normalize(dist), {
      maxDistance: vector.magnitude(dist),
      includeLiquidBlocks: true,
      includePassableBlocks: false,
    });

    if (obstruction !== undefined) {
      continue;
    }
    const atan = (Math.atan2(dist.x, dist.z) * 180) / Math.PI;
    let a = atan * -1;
    const yRot = customAngle === undefined ? source.getRotation().y : customAngle;

    const angleRange = angle / 2;
    a = normalizeAngle(a);
    const yRotNormalized = normalizeAngle(yRot);
    if (inverse ? isAngleInRange(a, yRotNormalized, angleRange) : !isAngleInRange(a, yRotNormalized, angleRange)) {
      continue;
    }

    ret.push(entities[i]);
  }
  return ret;
}

export function union(filter, families, dimension, casterId, checkForBlocks) {
  let ret = [];
  for (let i = 0; i < families.length; i++) {
    let currentFilter = filter;
    if (families[i].includes("!")) filter.excludeFamilies = [families[i].split("!")[1]];
    else filter.families = [families[i]];

    dimension.getEntities(currentFilter).forEach((element) => {
      if (casterId === undefined || element.id !== casterId) {
        if (!checkForBlocks) ret.push(element);
        else {
          const source = mc.world.getEntity(casterId);

          const sPos = source.getHeadLocation();
          const tPos = element.location;
          const dir = vector.subtract(sPos, tPos);

          const dist = Math.round(vector.distance(sPos, tPos));

          if (
            dimension.getBlockFromRay(sPos, dir, {
              maxDistance: dist,
              includePassableBlocks: false,
              includeLiquidBlocks: false,
            }) === undefined
          )
            ret.push(element);
        }
      }
    });
  }
  return ret;
}
/**
 *
 * @param {*} center
 * @param {mc.Block} blockTypeId
 * @param {Number} radius
 * @param {mc.Dimension} dimension
 * @returns
 */
export async function GetBlockInRadius(center, blockTypeId, radius, dimension) {
  const topRight = { x: center.x + radius, y: center.y + radius, z: center.z + radius };
  const bottomLeft = { x: center.x - radius, y: center.y - radius, z: center.z - radius };
  let blocksPos = [];

  for (let y = bottomLeft.y; y <= topRight.y; y++) {
    for (let x = bottomLeft.x; x <= topRight.x; x++) {
      for (let z = bottomLeft.z; z <= topRight.z; z++) {
        const location = { x: x, y: y, z: z };
        const isLocationInRadius = vector.distance(location, center) <= radius;

        if (isLocationInRadius) {
          let blockAtLocation;
          try {
            blockAtLocation = dimension.getBlock(location);
          } catch (e) {}

          if (blockAtLocation !== undefined && blockAtLocation.typeId === blockTypeId) {
            blocksPos.push(blockAtLocation.location);
          }
        }
      }
    }
  }

  return blocksPos;
}

function normalizeAngle(angle) {
  return ((angle + 180) % 360) - 180;
}

function angularDifference(angle1, angle2) {
  let diff = normalizeAngle(angle1 - angle2);
  return Math.abs(diff);
}

function isAngleInRange(angle, centerAngle, range) {
  const diff = angularDifference(angle, centerAngle);
  return diff <= range / 2;
}
export function getRandomQuantity(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
