/* eslint-disable no-param-reassign */
/* eslint-disable no-continue */
import { system } from '@minecraft/server';
import WBVector3 from '../../util/wbVector3';
/**
 * Creates a shockwave at the given location
 * @param dimension The dimension to create the shockwave in
 * @param location The location to create the shockwave at
 * @param shockWaveOptions The options for the shockwave
 * @example
 * createLocationShockWave(world.getDimension(MinecraftDimensionTypes.overworld),
 *   new Vector(0, 0, 0), {
 *    particles: ["twb_wisu:gas_cloud"],
 *    shockWaveSize: 7.5,
 *    shockWaveSpeed: 0.75,
 *    canJumpOver: true
 * })
 */
export function createLocationShockWave(dimension, location, shockWaveOptions) {
    const { entity = undefined, particles = [], size = 10, speed = 0.5, startRange = 0, stepSize = 0.25, damage = 1, damageOptions = undefined, knockback = 2, knockbackRangeFunction = (kb, _) => [kb, 0.25], canRepeatEntityHit = true, canJumpOver = false, entityHitFunction = () => true, edgeParticles = [], edgeParticlesHeight = 1, edgeParticlesStepSize = 4 * Math.PI, edgeParticlesMinRange = 3 } = shockWaveOptions;
    // Spawn the shockwave particles
    particles.forEach((particle) => {
        dimension.spawnParticle(particle, location);
    });
    const hitEntities = new Set();
    for (let r = startRange; r < size; r += stepSize) {
        system.runTimeout(() => {
            // Get the entities that are in range of the shockwave
            const entities = dimension.getEntities({
                location,
                minDistance: r,
                maxDistance: r + stepSize
            });
            // Apply effects to the entities that are hit
            const modifiedKnockback = knockbackRangeFunction(knockback, r);
            for (const hitEntity of entities) {
                if (hitEntity === entity
                    || (canJumpOver && !hitEntity.isOnGround)
                    || (!canRepeatEntityHit && hitEntities.has(hitEntity))) {
                    continue;
                }
                // Add the entity to the hit entities list
                if (!canRepeatEntityHit) {
                    hitEntities.add(hitEntity);
                }
                // Check if the entity can be hit
                if (!entityHitFunction(hitEntity)) {
                    continue;
                }
                // Damage the entity
                if (damage > 0) {
                    hitEntity.applyDamage(damage, damageOptions);
                }
                // Knockback the entity (not every entity can be knocked back, therefore the try-catch)
                if (knockback > 0) {
                    try {
                        const direction = WBVector3.fromMC(hitEntity.location).subtract(location).normalize();
                        hitEntity.applyKnockback(direction.x, direction.z, modifiedKnockback[0], modifiedKnockback[1]);
                    }
                    catch (_) { /* empty */ }
                }
            }
            // Spawn the edge particles
            if (edgeParticles.length === 0 || r < edgeParticlesMinRange) {
                return;
            }
            const edgeParticlesCount = Math.ceil((2 * Math.PI * r) / edgeParticlesStepSize);
            const theta = (2 * Math.PI) / edgeParticlesCount;
            const offset = Math.random() * theta;
            for (let i = 0; i < edgeParticlesCount; i++) {
                const x = r * Math.cos(i * theta + offset);
                const z = r * Math.sin(i * theta + offset);
                edgeParticles.forEach((particle) => {
                    dimension.spawnParticle(particle, new WBVector3(x, edgeParticlesHeight, z).add(location));
                });
            }
        }, r / speed);
    }
}
export function createEntityShockWave(origin, shockWaveOptions) {
    if (shockWaveOptions.entity === undefined) {
        shockWaveOptions.entity = origin;
    }
    createLocationShockWave(origin.dimension, origin.location, shockWaveOptions);
}
