/* eslint-disable no-continue */
import { LocationInUnloadedChunkError, LocationOutOfWorldBoundariesError, system } from '@minecraft/server';
import WBVector3 from '../../util/wbVector3';
import HeadAttack from './headAttack';
import { LONG_RANGE_SQ } from '../witherbossSettings';
import createWitherWraithSkullHitEffect from '../../effects/witherWraithSkullHit';
export default class ShootHeadAttack extends HeadAttack {
    constructor(wither, headPos) {
        super(wither, headPos);
        // The time in ticks until the head will attack again
        this.attackCooldown = 0;
        // The time that the launched attack has been active
        this.launchedAttackTime = -1;
        this.resetAttackCooldown();
    }
    resumeHeadAttackMode() {
        // Not needed for this attack
    }
    tickHeadAttack() {
        // If an attack was launched, tick that attack (even if the head attack mode is paused)
        if (this.attackCooldown < 0) {
            this.tickLaunchedAttack();
        }
    }
    tickHeadAttackWhenActive() {
        // Update the properties for target yaw and pitch
        if (this.target && this.target.isValid()) {
            const headLocation = this.getApproxIdleHeadLocation(this.headPos);
            if (!headLocation) {
                return false;
            }
            const targetLocation = WBVector3.fromMC(this.target.location);
            const direction = targetLocation.subtract(headLocation).normalize().getRotation();
            const entity = this.wither.getEntity();
            if (entity) {
                entity.setProperty(this.headTargetYawPropertyName, direction.y);
                entity.setProperty(this.headTargetPitchPropertyName, direction.x);
            }
        }
        // If an attack was launched, do not do anything else
        if (this.attackCooldown < 0) {
            return true;
        }
        // Determine targets
        this.target = this.getNearestTarget(this.headPos, LONG_RANGE_SQ, 80);
        // Only start new attacks (and decrement cooldown) if the head has a target
        if (!this.target || !this.target.isValid()) {
            return false;
        }
        // If the cooldown is 0, start an attack
        if (this.attackCooldown === 0) {
            this.launchAttack(this.target);
        }
        // Reduce the cooldown
        this.attackCooldown--;
        return this.attackCooldown < 0;
    }
    pauseHeadAttackMode() {
        // Not needed for this attack
    }
    stopHeadAttack() {
        if (this.attackCooldown < 0) {
            this.stopLaunchedAttack(false, false);
        }
    }
    resetAttackCooldown() {
        const cooldown = Math.random()
            * (ShootHeadAttack.MAX_ATTACK_COOLDOWN - ShootHeadAttack.MIN_ATTACK_COOLDOWN)
            + ShootHeadAttack.MIN_ATTACK_COOLDOWN;
        this.attackCooldown = Math.floor(cooldown);
        this.launchedAttackTime = -1;
    }
    launchAttack(target) {
        // Play the animation
        const e = this.wither.getEntity();
        if (!e) {
            return;
        }
        e.playAnimation('animation.twb_wisu.wither_wraith.base_attack_right');
        // Start the attack at the right point in the animation
        system.runTimeout(() => {
            const entity = this.wither.getEntity();
            if (!entity) {
                return;
            }
            const headPos = this.getApproxIdleHeadLocation(this.headPos);
            if (!headPos) {
                return;
            }
            // Reset the attack time
            this.launchedAttackTime = 0;
            // Set the target
            this.launchedAttackTarget = target;
            // Spawn the projectile
            this.launchedAttackProjectile = entity.dimension.spawnEntity('twb_wisu:witherboss_skull_projectile', headPos);
            // Play the sound
            entity.dimension.playSound('twb_wisu:witherboss.attack.skull_projectile.shoot', headPos, { volume: 16 });
        }, ShootHeadAttack.LAUNCHED_ATTACK_ANIM_TIME);
    }
    tickLaunchedAttack() {
        // Do nothing if the attack is not yet started
        if (this.launchedAttackTime < 0) {
            return;
        }
        // Stop attack if the attack time is over or the projectile/target is invalid
        if (this.launchedAttackTime >= ShootHeadAttack.LAUNCHED_ATTACK_MAX_TIME
            || !this.launchedAttackProjectile
            || !this.launchedAttackTarget
            || !this.launchedAttackProjectile?.isValid()
            || !this.launchedAttackTarget?.isValid()) {
            this.stopLaunchedAttack(false, false);
            return;
        }
        // Move the projectile towards the target or in the last direction
        const projectileLocation = WBVector3.fromMC(this.launchedAttackProjectile.location);
        const targetLocation = WBVector3.fromMC(this.launchedAttackTarget.location);
        if (this.launchedAttackTime < ShootHeadAttack.LAUNCHED_ATTACK_MAX_TRACK_TIME) {
            const direction = targetLocation.subtract(projectileLocation).normalize();
            const addVector = direction.multiply(ShootHeadAttack.LAUNCHED_ATTACK_SPEED);
            this.launchedAttackProjectile.teleport(projectileLocation.add(addVector), {
                facingLocation: targetLocation
            });
        }
        else {
            const direction = WBVector3.fromMC(this.launchedAttackProjectile.getViewDirection());
            const addVector = direction.multiply(ShootHeadAttack.LAUNCHED_ATTACK_SPEED);
            const newLocation = projectileLocation.add(addVector);
            const facingLocation = newLocation.add(addVector);
            this.launchedAttackProjectile.teleport(newLocation, { facingLocation });
        }
        // Check if the projectile hit the target
        const distanceSquared = WBVector3.distanceSquared(projectileLocation, targetLocation);
        if (distanceSquared < 1) {
            this.stopLaunchedAttack(true, false);
            return;
        }
        // Check if the projectile hit a block
        try {
            const block = this.launchedAttackProjectile.dimension.getBlock(projectileLocation);
            const isSolid = !!block?.dimension.getBlockFromRay(block.location, { x: 0, y: 1, z: 0 }, { includePassableBlocks: false, maxDistance: 1 });
            if (block && isSolid) {
                this.stopLaunchedAttack(false, true);
                return;
            }
        }
        catch (e) {
            // Ignore block access exceptions
            // eslint-disable-next-line max-len
            if (!(e instanceof LocationInUnloadedChunkError || e instanceof LocationOutOfWorldBoundariesError)) {
                throw e;
            }
        }
        // Increment the attack time
        this.launchedAttackTime++;
    }
    stopLaunchedAttack(hitTarget, hitBlock) {
        // Check if projectile is valid
        if (!this.launchedAttackProjectile || !this.launchedAttackProjectile.isValid()) {
            return;
        }
        // If the attack hit the target, apply damage
        if (hitTarget) {
            this.launchedAttackTarget?.applyDamage(15, {
                damagingEntity: this.wither.getEntity(),
                damagingProjectile: this.launchedAttackProjectile
            });
            this.launchedAttackTarget?.addEffect('wither', 20 * 6);
        }
        // If the attack hit a block, spawn an explosion
        if (hitTarget || hitBlock) {
            this.launchedAttackProjectile?.dimension.createExplosion(this.launchedAttackProjectile.location, 1.5, {
                breaksBlocks: true,
                causesFire: false,
                source: this.launchedAttackProjectile
            });
        }
        // Spawn the particle & play sounds
        createWitherWraithSkullHitEffect({
            ...this.launchedAttackProjectile.location,
            dimension: this.launchedAttackProjectile.dimension
        });
        // Remove the projectile
        if (this.launchedAttackProjectile) {
            this.launchedAttackProjectile.remove();
        }
        // Reset the attack cooldown
        this.resetAttackCooldown();
    }
}
/// Static constants for the shoot head attack
ShootHeadAttack.MIN_ATTACK_COOLDOWN = 25;
ShootHeadAttack.MAX_ATTACK_COOLDOWN = 75;
ShootHeadAttack.LAUNCHED_ATTACK_MAX_TIME = 80;
ShootHeadAttack.LAUNCHED_ATTACK_MAX_TRACK_TIME = 50;
ShootHeadAttack.LAUNCHED_ATTACK_SPEED = 0.35;
ShootHeadAttack.LAUNCHED_ATTACK_ANIM_TIME = 8;
