import { SafetyCheckedEntity, Vector3, expandingCubeOperation, isSolid, waitForEvent, waitForTicks } from "../../utils/index";
import { Task } from "./task";
export class FindLowLightTask extends Task {
    start() {
        return super.start();
    }
    async performTask() {
        const lowLightLocation = await this.getLowLightLocation(this.npc.Entity, this.options);
        if (!lowLightLocation)
            return { outcome: "failed" };
        return { outcome: "succeeded", data: { lowLightLocation } };
    }
    async cleanupTask() {
    }
    async getLowLightLocation(entity, options) {
        var _a;
        // If light level is < 4, return location
        const lightLevelAtOrigin = entity.getProperty("light_level");
        const originBlock = entity.dimension.getBlock(entity.location);
        if (lightLevelAtOrigin < 4 && airOrSnow(originBlock) && ((_a = originBlock.above()) === null || _a === void 0 ? void 0 : _a.isAir) && canPlaceTorch(originBlock.below()))
            return entity.location;
        // Use expanding cube algorithm to scan
        let lightChecks = [];
        await expandingCubeOperation(this.npc.Entity.location.round(), 
        // Each layer spawn block checks, wait one tick, get light properties, despawn light checks
        async function (location) {
            var _a;
            const block = entity.dimension.getBlock(location);
            if (block && airOrSnow(block) && ((_a = block.above()) === null || _a === void 0 ? void 0 : _a.isAir) && isSolid(block.below()) && hasLineOfSight(new Vector3(entity.getHeadLocation()), location, entity.dimension)) {
                if (options.debugParticles)
                    entity.dimension.spawnParticle('minecraft:endrod', location);
                const lightCheck = new SafetyCheckedEntity(entity.dimension.spawnEntity(options.lightCheckEntity, location));
                waitForEvent(2, () => {
                    if (canPlaceTorch(block.below()))
                        lightChecks.push({ location: lightCheck.location, lightLevel: Number(lightCheck.getProperty('light_level')) });
                    lightCheck.remove();
                });
            }
            return false;
        }, 
        // If layer has light level < 4, return  location. Else continue scan
        async function () {
            await waitForTicks(3);
            return lightChecks.some(check => check.lightLevel < 4);
        }, {
            min: 1,
            max: 7
        });
        // After scanning full environment, fail if no block is less than 7 light
        if (lightChecks.every(check => check.lightLevel > 7))
            return;
        // Sort location list by light level, return lowest light
        return lightChecks.sort((checkA, checkB) => checkB.lightLevel - checkA.lightLevel).pop().location;
    }
}
function hasLineOfSight(origin, target, dimension) {
    const direction = target.subtract(origin).normalize();
    const block = dimension.getBlockFromRay(origin, direction, { includeLiquidBlocks: false, includePassableBlocks: false, maxDistance: origin.distance(target) });
    if (block) {
        return false;
    }
    else {
        return true;
    }
}
function airOrSnow(block) {
    if (!block)
        return false;
    return block.isAir || block.permutation.matches("minecraft:snow_layer");
}
function canPlaceTorch(block) {
    if (!block || (block === null || block === void 0 ? void 0 : block.isAir) || (block === null || block === void 0 ? void 0 : block.isLiquid))
        return false;
    const tags = [
        "stone",
        "wood",
        "dirt",
        "gravel",
        "sand",
    ];
    if (tags.some(tag => block.hasTag(tag)))
        return true;
    const whitelist = [
        "minecraft:tuff",
        "minecraft:deepslate",
        "minecraft:snow",
    ];
    if (whitelist.some(name => block.permutation.matches(name)))
        return true;
    return false;
}
