import { Vector } from "./../../../library/Minecraft.js";
import { locToString } from "../../util.js";
import { Jobs } from "./../../modules/jobs.js";
const offsets = [new Vector(-1, 0, 0), new Vector(1, 0, 0), new Vector(0, -1, 0), new Vector(0, 1, 0), new Vector(0, 0, -1), new Vector(0, 0, 1)];
function getChunkKey(pos) {
    return `${pos.x >> 4},${pos.z >> 4}`;
}
function parseCoordinates(key) {
    const [x, z] = key.split(",").map(Number);
    return { x, z };
}
function calculateDistance(point1, point2) {
    return (point1.x - point2.x) ** 2 + (point1.z - point2.z) ** 2;
}
function findClosestEntry(map, target) {
    let closestEntry;
    let closestDistance = Infinity;
    for (const [key, value] of map.entries()) {
        const coords = parseCoordinates(key);
        const distance = calculateDistance(coords, target);
        if (distance < closestDistance) {
            closestDistance = distance;
            closestEntry = [key, value];
        }
    }
    return closestEntry;
}
export function* floodFill(start, size, spread) {
    const initialCtx = {
        pos: Vector.ZERO,
        worldPos: Vector.from(start),
        nextBlock: yield* Jobs.loadBlock(start),
    };
    if (!spread({ ...initialCtx }, Vector.ZERO))
        return [];
    const dimension = Jobs.getRunner().dimension;
    const chunks = new Map();
    const queue = [[Vector.from(start), initialCtx]];
    const resultSet = new Set();
    const result = [];
    let currentChunk = getChunkKey(start);
    function addNeighbor(block, offset, ctx) {
        const neighbor = block.offset(offset.x, offset.y, offset.z);
        ctx.pos = neighbor.offset(-start.x, -start.y, -start.z);
        ctx.worldPos = neighbor;
        const chunkKey = getChunkKey(neighbor);
        if (chunkKey !== currentChunk) {
            if (!chunks.has(chunkKey))
                chunks.set(chunkKey, []);
            chunks.get(chunkKey).push([neighbor, ctx]);
        }
        else {
            queue.push([neighbor, ctx]);
        }
    }
    while (queue.length) {
        const [block, ctx] = queue.shift();
        const blockKey = locToString(block);
        if (!resultSet.has(blockKey) && Vector.sub(block, start).length <= size + 0.5) {
            resultSet.add(blockKey);
            result.push(block);
            for (const offset of offsets) {
                const nextBlockLoc = Vector.add(block, offset);
                const newCtx = { ...ctx, nextBlock: dimension.getBlock(nextBlockLoc) ?? (yield* Jobs.loadBlock(nextBlockLoc)) };
                try {
                    if (spread(newCtx, offset)) {
                        addNeighbor(block, offset, newCtx);
                        // system.run(() => dimension.spawnParticle("minecraft:basic_flame_particle", nextBlockLoc));
                    }
                }
                catch {
                    /* pass */
                }
            }
        }
        yield;
        if (!queue.length && chunks.size) {
            const [chunkKey, chunk] = findClosestEntry(chunks, parseCoordinates(currentChunk));
            if (!chunk)
                continue;
            currentChunk = chunkKey;
            queue.push(...chunk);
            chunks.delete(chunkKey);
        }
    }
    return result;
}
