import { BlockPermutation, MolangVariableMap, Player } from "@minecraft/server";
import { clamp, lengthSquared, random, rotateAroundAxis, vecCross, vecEqual, vecNormalize } from "../util/mthUtils";
import { SuppParticles } from "../reg/suppParticles";
import { isBlockSolid, shrinkIfInSurvival } from "../util/miscUtils";
import { configs } from "../configs";
import { getPlacementPosition } from "../util/placementUtils";
import { isReplaceable } from "../util/blockProperties";
import { SuppStates } from "../reg/suppStates";
import { getLayers } from "../layersManager";
import { getHorizontalViewDir } from "../util/dirUtils";
import { McStates } from "../reg/mcStates";
function canPlaceConfetti(player) {
    return player.isSneaking && configs.placeableConfetti.getValue();
}
export const ConfettiPopperComponent = {
    onUse(event) {
        const player = event.source;
        const start = player.getHeadLocation();
        const facing = player.getViewDirection();
        const item = event.itemStack;
        if (!canPlaceConfetti(player)) {
            spawnConfettiParticles(facing, start, player.dimension);
            shrinkIfInSurvival(player, item, 1);
            player.dimension.playSound("firework.blast", player.location, { volume: 0.5, pitch: 1.2 });
            //TODO:proper sound here. new custom one as othe one was gifted
        }
    },
    onUseOn(event) {
        const player = event.source;
        if (player instanceof Player && canPlaceConfetti(player)) {
            const pos = getPlacementPosition(event.block, event.blockFace, b => {
                return (b.typeId == "razz_sup:confetti_litter" /* SuppBlocks.ConfettiLitter */ ? (getLayers(b) < 4) : isReplaceable(b)) &&
                    isBlockSolid(b.below());
            });
            if (pos) {
                const dir = getHorizontalViewDir(player);
                const existing = pos.typeId == "razz_sup:confetti_litter" /* SuppBlocks.ConfettiLitter */;
                let perm = existing ?
                    pos.permutation.withState(SuppStates.LayerCount, getLayers(pos) + 1) :
                    BlockPermutation.resolve("razz_sup:confetti_litter" /* SuppBlocks.ConfettiLitter */)
                        .withState(McStates.HorizontalDirection, dir);
                //perm = perm.withState("minecraft:cardinal_direction", dir);
                pos.dimension.setBlockPermutation(pos.location, perm);
                pos.dimension.playSound("place.azalea_leaves", pos.center(), { volume: 1, pitch: 1 });
                shrinkIfInSurvival(player, event.itemStack);
            }
        }
    }
};
export function spawnConfettiParticles(facing, start, dimension, mag = 1) {
    const map = new MolangVariableMap();
    for (let i = 0; i < configs.confettiAmount.getValue(); i++) {
        const v = randomVectorInCone(facing, 0.35, 0.7);
        const speed = random(9, 16) * mag;
        map.setVector3("variable.direction", v);
        map.setFloat("variable.particle_initial_speed", speed);
        dimension.spawnParticle(Math.random() < 0.12 ? SuppParticles.Streamer :
            SuppParticles.Confetti, start, map);
    }
}
/**
 * Returns a random normalized vector within a cone around the given direction.
 * @param direction A normalized direction vector.
 * @param angle Cone angle in radians.
 * @param bias Bias factor for the random distribution (default is 1, 0 = all center).
 */
export function randomVectorInCone(direction, angle, bias = 1) {
    const [aaa, dir] = vecNormalize(direction); // Just in case it's not normalized
    // Random spherical coordinates within cone
    const u = Math.random();
    const v = Math.random();
    const theta = 2 * Math.PI * u;
    const biasedV = Math.pow(v, bias);
    const phi = Math.acos(1 - biasedV * (1 - Math.cos(angle))); // Uniform within cone
    const sinPhi = Math.sin(phi);
    // Local vector (cone points along +Z)
    const local = {
        x: sinPhi * Math.cos(theta),
        y: sinPhi * Math.sin(theta),
        z: Math.cos(phi)
    };
    // If direction is (0,0,1), no rotation needed
    if (vecEqual(dir, { x: 0, y: 0, z: 1 }))
        return local;
    // Compute rotation axis and angle from +Z to direction
    const axis = vecCross({ x: 0, y: 0, z: 1 }, dir);
    const axisLenSq = lengthSquared(axis);
    if (axisLenSq === 0) {
        // Direction is either (0,0,1) or (0,0,-1)
        return dir.z > 0 ? local : { x: -local.x, y: -local.y, z: -local.z };
    }
    const [aa, axisNorm] = vecNormalize(axis);
    const dot = dir.z; // dot({0,0,1}, dir)
    const angleToDir = Math.acos(clamp(dot, -1, 1));
    return rotateAroundAxis(local, axisNorm, angleToDir);
}
