package net.minecraft.world.entity.projectile;

import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.Packet;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import java.util.Iterator;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.Difficulty;
import java.util.List;
import net.minecraft.util.Mth;
import com.google.common.collect.Lists;
import net.minecraft.core.Position;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import net.minecraft.core.BlockPos;
import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Entity;

public class ShulkerBullet extends Entity {
    private LivingEntity owner;
    private Entity finalTarget;
    @Nullable
    private Direction currentMoveDirection;
    private int flightSteps;
    private double targetDeltaX;
    private double targetDeltaY;
    private double targetDeltaZ;
    @Nullable
    private UUID ownerId;
    private BlockPos lastKnownOwnerPos;
    @Nullable
    private UUID targetId;
    private BlockPos lastKnownTargetPos;
    
    public ShulkerBullet(final EntityType<? extends ShulkerBullet> aku, final Level bjw) {
        super(aku, bjw);
        this.noPhysics = true;
    }
    
    public ShulkerBullet(final Level bjw, final double double2, final double double3, final double double4, final double double5, final double double6, final double double7) {
        this(EntityType.SHULKER_BULLET, bjw);
        this.moveTo(double2, double3, double4, this.yRot, this.xRot);
        this.setDeltaMovement(double5, double6, double7);
    }
    
    public ShulkerBullet(final Level bjw, final LivingEntity akz, final Entity akq, final Direction.Axis a) {
        this(EntityType.SHULKER_BULLET, bjw);
        this.owner = akz;
        final BlockPos fk6 = new BlockPos(akz);
        final double double7 = fk6.getX() + 0.5;
        final double double8 = fk6.getY() + 0.5;
        final double double9 = fk6.getZ() + 0.5;
        this.moveTo(double7, double8, double9, this.yRot, this.xRot);
        this.finalTarget = akq;
        this.currentMoveDirection = Direction.UP;
        this.selectNextMoveDirection(a);
    }
    
    @Override
    public SoundSource getSoundSource() {
        return SoundSource.HOSTILE;
    }
    
    @Override
    protected void addAdditionalSaveData(final CompoundTag jt) {
        if (this.owner != null) {
            final BlockPos fk3 = new BlockPos(this.owner);
            final CompoundTag jt2 = NbtUtils.createUUIDTag(this.owner.getUUID());
            jt2.putInt("X", fk3.getX());
            jt2.putInt("Y", fk3.getY());
            jt2.putInt("Z", fk3.getZ());
            jt.put("Owner", jt2);
        }
        if (this.finalTarget != null) {
            final BlockPos fk3 = new BlockPos(this.finalTarget);
            final CompoundTag jt2 = NbtUtils.createUUIDTag(this.finalTarget.getUUID());
            jt2.putInt("X", fk3.getX());
            jt2.putInt("Y", fk3.getY());
            jt2.putInt("Z", fk3.getZ());
            jt.put("Target", jt2);
        }
        if (this.currentMoveDirection != null) {
            jt.putInt("Dir", this.currentMoveDirection.get3DDataValue());
        }
        jt.putInt("Steps", this.flightSteps);
        jt.putDouble("TXD", this.targetDeltaX);
        jt.putDouble("TYD", this.targetDeltaY);
        jt.putDouble("TZD", this.targetDeltaZ);
    }
    
    @Override
    protected void readAdditionalSaveData(final CompoundTag jt) {
        this.flightSteps = jt.getInt("Steps");
        this.targetDeltaX = jt.getDouble("TXD");
        this.targetDeltaY = jt.getDouble("TYD");
        this.targetDeltaZ = jt.getDouble("TZD");
        if (jt.contains("Dir", 99)) {
            this.currentMoveDirection = Direction.from3DDataValue(jt.getInt("Dir"));
        }
        if (jt.contains("Owner", 10)) {
            final CompoundTag jt2 = jt.getCompound("Owner");
            this.ownerId = NbtUtils.loadUUIDTag(jt2);
            this.lastKnownOwnerPos = new BlockPos(jt2.getInt("X"), jt2.getInt("Y"), jt2.getInt("Z"));
        }
        if (jt.contains("Target", 10)) {
            final CompoundTag jt2 = jt.getCompound("Target");
            this.targetId = NbtUtils.loadUUIDTag(jt2);
            this.lastKnownTargetPos = new BlockPos(jt2.getInt("X"), jt2.getInt("Y"), jt2.getInt("Z"));
        }
    }
    
    @Override
    protected void defineSynchedData() {
    }
    
    private void setMoveDirection(@Nullable final Direction fp) {
        this.currentMoveDirection = fp;
    }
    
    private void selectNextMoveDirection(@Nullable final Direction.Axis a) {
        double double4 = 0.5;
        BlockPos fk3;
        if (this.finalTarget == null) {
            fk3 = new BlockPos(this).below();
        }
        else {
            double4 = this.finalTarget.getBbHeight() * 0.5;
            fk3 = new BlockPos(this.finalTarget.getX(), this.finalTarget.getY() + double4, this.finalTarget.getZ());
        }
        double double5 = fk3.getX() + 0.5;
        double double6 = fk3.getY() + double4;
        double double7 = fk3.getZ() + 0.5;
        Direction fp12 = null;
        if (!fk3.closerThan(this.position(), 2.0)) {
            final BlockPos fk4 = new BlockPos(this);
            final List<Direction> list14 = Lists.newArrayList();
            if (a != Direction.Axis.X) {
                if (fk4.getX() < fk3.getX() && this.level.isEmptyBlock(fk4.east())) {
                    list14.add(Direction.EAST);
                }
                else if (fk4.getX() > fk3.getX() && this.level.isEmptyBlock(fk4.west())) {
                    list14.add(Direction.WEST);
                }
            }
            if (a != Direction.Axis.Y) {
                if (fk4.getY() < fk3.getY() && this.level.isEmptyBlock(fk4.above())) {
                    list14.add(Direction.UP);
                }
                else if (fk4.getY() > fk3.getY() && this.level.isEmptyBlock(fk4.below())) {
                    list14.add(Direction.DOWN);
                }
            }
            if (a != Direction.Axis.Z) {
                if (fk4.getZ() < fk3.getZ() && this.level.isEmptyBlock(fk4.south())) {
                    list14.add(Direction.SOUTH);
                }
                else if (fk4.getZ() > fk3.getZ() && this.level.isEmptyBlock(fk4.north())) {
                    list14.add(Direction.NORTH);
                }
            }
            fp12 = Direction.getRandomFace(this.random);
            if (list14.isEmpty()) {
                for (int integer15 = 5; !this.level.isEmptyBlock(fk4.relative(fp12)) && integer15 > 0; fp12 = Direction.getRandomFace(this.random), --integer15) {}
            }
            else {
                fp12 = list14.get(this.random.nextInt(list14.size()));
            }
            double5 = this.getX() + fp12.getStepX();
            double6 = this.getY() + fp12.getStepY();
            double7 = this.getZ() + fp12.getStepZ();
        }
        this.setMoveDirection(fp12);
        final double double8 = double5 - this.getX();
        final double double9 = double6 - this.getY();
        final double double10 = double7 - this.getZ();
        final double double11 = Mth.sqrt(double8 * double8 + double9 * double9 + double10 * double10);
        if (double11 == 0.0) {
            this.targetDeltaX = 0.0;
            this.targetDeltaY = 0.0;
            this.targetDeltaZ = 0.0;
        }
        else {
            this.targetDeltaX = double8 / double11 * 0.15;
            this.targetDeltaY = double9 / double11 * 0.15;
            this.targetDeltaZ = double10 / double11 * 0.15;
        }
        this.hasImpulse = true;
        this.flightSteps = 10 + this.random.nextInt(5) * 10;
    }
    
    @Override
    public void checkDespawn() {
        if (this.level.getDifficulty() == Difficulty.PEACEFUL) {
            this.remove();
        }
    }
    
    @Override
    public void tick() {
        super.tick();
        if (!this.level.isClientSide) {
            if (this.finalTarget == null && this.targetId != null) {
                final List<LivingEntity> list2 = this.level.<LivingEntity>getEntitiesOfClass(LivingEntity.class, new AABB(this.lastKnownTargetPos.offset(-2, -2, -2), this.lastKnownTargetPos.offset(2, 2, 2)));
                for (final LivingEntity akz4 : list2) {
                    if (akz4.getUUID().equals(this.targetId)) {
                        this.finalTarget = akz4;
                        break;
                    }
                }
                this.targetId = null;
            }
            if (this.owner == null && this.ownerId != null) {
                final List<LivingEntity> list2 = this.level.<LivingEntity>getEntitiesOfClass(LivingEntity.class, new AABB(this.lastKnownOwnerPos.offset(-2, -2, -2), this.lastKnownOwnerPos.offset(2, 2, 2)));
                for (final LivingEntity akz4 : list2) {
                    if (akz4.getUUID().equals(this.ownerId)) {
                        this.owner = akz4;
                        break;
                    }
                }
                this.ownerId = null;
            }
            if (this.finalTarget != null && this.finalTarget.isAlive() && (!(this.finalTarget instanceof Player) || !((Player)this.finalTarget).isSpectator())) {
                this.targetDeltaX = Mth.clamp(this.targetDeltaX * 1.025, -1.0, 1.0);
                this.targetDeltaY = Mth.clamp(this.targetDeltaY * 1.025, -1.0, 1.0);
                this.targetDeltaZ = Mth.clamp(this.targetDeltaZ * 1.025, -1.0, 1.0);
                final Vec3 cvl2 = this.getDeltaMovement();
                this.setDeltaMovement(cvl2.add((this.targetDeltaX - cvl2.x) * 0.2, (this.targetDeltaY - cvl2.y) * 0.2, (this.targetDeltaZ - cvl2.z) * 0.2));
            }
            else if (!this.isNoGravity()) {
                this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.04, 0.0));
            }
            final HitResult cvi2 = ProjectileUtil.forwardsRaycast(this, true, false, this.owner, ClipContext.Block.COLLIDER);
            if (cvi2.getType() != HitResult.Type.MISS) {
                this.onHit(cvi2);
            }
        }
        final Vec3 cvl2 = this.getDeltaMovement();
        this.setPos(this.getX() + cvl2.x, this.getY() + cvl2.y, this.getZ() + cvl2.z);
        ProjectileUtil.rotateTowardsMovement(this, 0.5f);
        if (this.level.isClientSide) {
            this.level.addParticle(ParticleTypes.END_ROD, this.getX() - cvl2.x, this.getY() - cvl2.y + 0.15, this.getZ() - cvl2.z, 0.0, 0.0, 0.0);
        }
        else if (this.finalTarget != null && !this.finalTarget.removed) {
            if (this.flightSteps > 0) {
                --this.flightSteps;
                if (this.flightSteps == 0) {
                    this.selectNextMoveDirection((this.currentMoveDirection == null) ? null : this.currentMoveDirection.getAxis());
                }
            }
            if (this.currentMoveDirection != null) {
                final BlockPos fk3 = new BlockPos(this);
                final Direction.Axis a4 = this.currentMoveDirection.getAxis();
                if (this.level.loadedAndEntityCanStandOn(fk3.relative(this.currentMoveDirection), this)) {
                    this.selectNextMoveDirection(a4);
                }
                else {
                    final BlockPos fk4 = new BlockPos(this.finalTarget);
                    if ((a4 == Direction.Axis.X && fk3.getX() == fk4.getX()) || (a4 == Direction.Axis.Z && fk3.getZ() == fk4.getZ()) || (a4 == Direction.Axis.Y && fk3.getY() == fk4.getY())) {
                        this.selectNextMoveDirection(a4);
                    }
                }
            }
        }
    }
    
    @Override
    public boolean isOnFire() {
        return false;
    }
    
    @Override
    public boolean shouldRenderAtSqrDistance(final double double1) {
        return double1 < 16384.0;
    }
    
    @Override
    public float getBrightness() {
        return 1.0f;
    }
    
    protected void onHit(final HitResult cvi) {
        if (cvi.getType() == HitResult.Type.ENTITY) {
            final Entity akq3 = ((EntityHitResult)cvi).getEntity();
            final boolean boolean4 = akq3.hurt(DamageSource.indirectMobAttack(this, this.owner).setProjectile(), 4.0f);
            if (boolean4) {
                this.doEnchantDamageEffects(this.owner, akq3);
                if (akq3 instanceof LivingEntity) {
                    ((LivingEntity)akq3).addEffect(new MobEffectInstance(MobEffects.LEVITATION, 200));
                }
            }
        }
        else {
            ((ServerLevel)this.level).<SimpleParticleType>sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(), this.getZ(), 2, 0.2, 0.2, 0.2, 0.0);
            this.playSound(SoundEvents.SHULKER_BULLET_HIT, 1.0f, 1.0f);
        }
        this.remove();
    }
    
    @Override
    public boolean isPickable() {
        return true;
    }
    
    @Override
    public boolean hurt(final DamageSource ajz, final float float2) {
        if (!this.level.isClientSide) {
            this.playSound(SoundEvents.SHULKER_BULLET_HURT, 1.0f, 1.0f);
            ((ServerLevel)this.level).<SimpleParticleType>sendParticles(ParticleTypes.CRIT, this.getX(), this.getY(), this.getZ(), 15, 0.2, 0.2, 0.2, 0.0);
            this.remove();
        }
        return true;
    }
    
    @Override
    public Packet<?> getAddEntityPacket() {
        return new ClientboundAddEntityPacket(this);
    }
}
