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;
    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;
}
/**
 * 
 * @param {mc.Block} block 
 * @param {import("@minecraft/server").Vector3} localStart 
 * @param {import("@minecraft/server").Vector3} localEnd 
 * @returns 
 */
export function getVolumeFromLocalCoords(block, localStart, localEnd) {
  try {
    if (!block || block === undefined)  return new mc.BlockVolume({x: 0, y: 0, z: 0}, {x: 0, y: 0, z: 0});
  
    const facingOffsets = {
      north: { x: 0, z: -1, y: 0 },
      south: { x: 0, z: 1, y: 0 },
      east: { x: 1, z: 0, y: 0 },
      west: { x: -1, z: 0, y: 0 },
      up: { x: 0, z: 0, y: 1 },
      down: { x: 0, z: 0, y: -1 }
    };
  
    const directionMap = {
      0: 'down',
      1: 'up',
      2: 'north',
      3: 'south',
      4: 'west',
      5: 'east'
    };
  
    const facingDirection = block.permutation.getState('facing_direction');
    const directionKey = directionMap[facingDirection]; // Mapeo numérico a dirección
  
    const directionOffset = facingOffsets[directionKey];
  
    function localToWorld(localCoord) {
      if (!localCoord || localCoord === undefined) return block?.location;
      if (directionKey === 'up' || directionKey === 'down') {
        return {
          x: block?.location.x + localCoord.x,
          y: block?.location.y + localCoord.y * directionOffset.y,
          z: block?.location.z + localCoord.z
        };
      }
  
      return {
        x: block?.location.x + (localCoord.x * directionOffset.x - localCoord.z * directionOffset.z),
        y: block?.location.y + localCoord.y,
        z: block?.location.z + (localCoord.x * directionOffset.z + localCoord.z * directionOffset.x)
      };
    }
  
    const globalStart = localToWorld(localStart);
    const globalEnd = localToWorld(localEnd);
  
    return new mc.BlockVolume(globalStart, globalEnd);
  } catch {
    return new mc.BlockVolume({x: 0, y: 0, z: 0}, {x: 0, y: 0, z: 0});
  }
}

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;
}
