import { Direction, MolangVariableMap } from "@minecraft/server";
import { directionToVector, flipDir, getADirection, offsetPos, offsetVec } from "../util/dirUtils";
import { hasAnyBlockAround, isBlockSolid, printAllStates, pushEntity, spawnParticleOnBlockFace } from "../util/miscUtils";
import { McBlocks } from "../reg/mcBlocks";
import { clamp, lengthSquared, random, vecDiff, vecMul, vecSum } from "../util/mthUtils";
import { SuppStates } from "../reg/suppStates";
import { hasWater } from "../util/blockProperties";
import { SuppParticles } from "../reg/suppParticles";
import { McParticles } from "../reg/mcParticles";
import { McStates } from "../reg/mcStates";
export const BellowsComponent = {
    onTick(event) {
        const block = event.block;
        if (!block.isValid())
            return;
        let power = block.getRedstonePower() | 0;
        if (power > 0 != block.permutation.getState(SuppStates.Powered)) {
            block.setPermutation(block.permutation.withState(SuppStates.Powered, power > 0));
        }
        if (power <= 0)
            return;
        const facing = flipDir(getADirection(block));
        let frontPos = offsetPos(block, facing);
        if (!frontPos)
            return;
        const airType = getInteractionType(frontPos, facing);
        if (airType == AirType.NONE)
            return;
        if (airType == AirType.LAVA || airType == AirType.WATER) {
            frontPos = offsetPos(block, facing, 2);
        }
        power = 1;
        blowParticles(block, facing, airType);
        pushAir(block, frontPos, facing, airType);
        if (airType == AirType.AIR) {
            refreshFire(4, facing, frontPos);
        }
        /*
        boolean blowing = Mth.sin(arg - 0.8f) > 0;
        if (tile.lastBlowing != blowing) {
            level.playSound(null, pos,
                blowing ? ModSounds.BELLOWS_BLOW.get() : ModSounds.BELLOWS_RETRACT.get(),
                SoundSource.BLOCKS, 0.1f,
                MthUtils.nextWeighted(level.random, 0.1f) + 0.85f + 0.6f * power / 15f);
        }*/
    }
};
var AirType;
(function (AirType) {
    AirType[AirType["NONE"] = 0] = "NONE";
    AirType[AirType["AIR"] = 1] = "AIR";
    AirType[AirType["WATER"] = 2] = "WATER";
    AirType[AirType["SUBMERGED"] = 3] = "SUBMERGED";
    AirType[AirType["SOAP"] = 4] = "SOAP";
    AirType[AirType["LAVA"] = 5] = "LAVA";
})(AirType || (AirType = {}));
function getInteractionType(frontPos, myDir) {
    if (frontPos.typeId == McBlocks.WetSponge) {
        if (hasAnyBlockAround(frontPos, myDir, "razz_sup:soap_block" /* SuppBlocks.SoapBlock */)) {
            return AirType.SOAP;
        }
        return AirType.WATER;
    }
    else if (hasWater(frontPos)) {
        return AirType.SUBMERGED;
    }
    else if (frontPos.typeId == "razz_sup:scorching_pumice" /* SuppBlocks.ScorchingPumice */) {
        return AirType.LAVA;
    }
    else if (isBlockSolid(frontPos)) {
        return AirType.NONE;
    }
    return AirType.AIR;
}
function pushAir(origin, front, dir, airType) {
    if (airType == AirType.SOAP || airType == AirType.SUBMERGED)
        return;
    const level = front.dimension;
    const step = directionToVector(dir);
    const range = airType == AirType.AIR ? 5 : 4;
    const opt = {
        includePassableBlocks: false,
        maxDistance: range,
    };
    const start = front;
    const furthest = level.getBlockFromRay(front.center(), step, opt);
    let maxDistSquared = range * range;
    let end;
    if (!furthest) {
        end = offsetVec(start, dir, range);
    }
    else {
        end = furthest.block;
        const hitLocation = vecSum(furthest.block.location, furthest.faceLocation);
        //distance from center
        const dist = vecDiff(start.center(), hitLocation);
        //select axis
        const axis = vecMul(dist, step);
        //get the axis with the highest magnitude
        maxDistSquared = lengthSquared(axis);
    }
    const diff = vecDiff(end, start);
    //iterate from start to end, get entities at the position
    //push them in the direction of dir
    const eOpt = {
        location: start,
        volume: diff,
        closest: 100,
        excludeFamilies: ["inanimate"]
    };
    const entities = level.getEntities(eOpt);
    // iterate over all entities and spawn flame particle on them
    for (const e of entities) {
        const pos = e.location;
        //verify distance
        const distFromBellow = vecDiff(pos, origin.center());
        const distSquared = lengthSquared(distFromBellow);
        if (distSquared > maxDistSquared)
            continue;
        if (airType == AirType.AIR) {
            const abovePos = { x: pos.x, y: pos.y + 0.6, z: pos.z };
            //TODO: fix range being bigger
            //e.dimension.spawnParticle("minecraft:blue_flame_particle", abovePos);
            const distanceFromCenter = vecDiff(start, pos);
            const mag = lengthSquared(distanceFromCenter);
            //apply knockback to entities
            let knockback = Math.max(0, 1 - mag / (range * range)) * 0.1;
            if (dir == Direction.Up) {
                knockback *= 0.5;
            }
            const knockbackVec = { x: step.x * knockback, y: step.y * knockback, z: step.z * knockback };
            pushEntity(e, knockbackVec);
        }
        else if (airType == AirType.LAVA) {
            e.setOnFire(5);
        }
        else if (airType == AirType.WATER) {
            e.extinguishFire();
        }
        //TODO: sounds
    }
}
function blowParticles(block, facing, type) {
    if (type == AirType.NONE)
        return;
    const direction = directionToVector(facing);
    const inFront = offsetPos(block, facing);
    if (!inFront)
        return;
    let speed = random(3.2, 4);
    const map = new MolangVariableMap();
    // Set velocity for the particle
    map.setVector3("variable.direction", direction);
    map.setFloat("variable.particle_initial_speed", speed);
    if (type == AirType.SOAP) {
        spawnParticleOnBlockFace(inFront, facing, SuppParticles.Suds, 2, 0.01, map);
    }
    else if (type == AirType.LAVA) {
        if (random() < 0.5) {
            spawnParticleOnBlockFace(inFront, facing, SuppParticles.Lava, 1, 0.01, map);
        }
        spawnParticleOnBlockFace(inFront, facing, SuppParticles.HotAir, 1, 0.01, map);
    }
    else if (type == AirType.WATER) {
        if (random() < 0.5) {
            spawnParticleOnBlockFace(inFront, facing, SuppParticles.Water, 1, 0.01, map);
        }
        spawnParticleOnBlockFace(inFront, facing, SuppParticles.WetAir, 1, 0.01, map);
    }
    else {
        const x = block.x + 0.5 + (Math.random() - 0.5) / 4 + direction.x * 0.5;
        const y = block.y + 0.5 + (Math.random() - 0.5) / 4 + direction.y * 0.5;
        const z = block.z + 0.5 + (Math.random() - 0.5) / 4 + direction.z * 0.5;
        const particlePos = { x, y, z };
        if (type === AirType.SUBMERGED) {
            block.dimension.spawnParticle(McParticles.Bubble, particlePos, map);
        }
        else {
            block.dimension.spawnParticle(SuppParticles.Air, particlePos, map);
        }
    }
}
function refreshFire(n, facing, frontPos) {
    if (Math.random() > 0.1)
        return;
    for (let i = 0; i < n; i++) {
        if (frontPos.typeId == McBlocks.Fire) {
            printAllStates(frontPos);
            const age = frontPos.permutation.getState(McStates.Age);
            if (age != 0) {
                frontPos.setPermutation(frontPos.permutation.withState(McStates.Age, clamp(age - 7, 0, 15)));
            }
        }
        frontPos = offsetPos(frontPos, facing);
    }
}
