package net.minecraft.world.entity.projectile;

import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.Packet;
import javax.annotation.Nullable;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.CompoundTag;
import java.util.Iterator;
import net.minecraft.world.phys.AABB;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.phys.Vec3;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import java.util.UUID;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Entity;

public abstract class ThrowableProjectile extends Entity implements Projectile {
    private int xBlock;
    private int yBlock;
    private int zBlock;
    protected boolean inGround;
    public int shakeTime;
    protected LivingEntity owner;
    private UUID ownerId;
    private Entity entityToIgnore;
    private int timeToIgnore;
    
    protected ThrowableProjectile(final EntityType<? extends ThrowableProjectile> aku, final Level bjw) {
        super(aku, bjw);
        this.xBlock = -1;
        this.yBlock = -1;
        this.zBlock = -1;
    }
    
    protected ThrowableProjectile(final EntityType<? extends ThrowableProjectile> aku, final double double2, final double double3, final double double4, final Level bjw) {
        this(aku, bjw);
        this.setPos(double2, double3, double4);
    }
    
    protected ThrowableProjectile(final EntityType<? extends ThrowableProjectile> aku, final LivingEntity akz, final Level bjw) {
        this(aku, akz.getX(), akz.getEyeY() - 0.10000000149011612, akz.getZ(), bjw);
        this.owner = akz;
        this.ownerId = akz.getUUID();
    }
    
    @Override
    public boolean shouldRenderAtSqrDistance(final double double1) {
        double double2 = this.getBoundingBox().getSize() * 4.0;
        if (Double.isNaN(double2)) {
            double2 = 4.0;
        }
        double2 *= 64.0;
        return double1 < double2 * double2;
    }
    
    public void shootFromRotation(final Entity akq, final float float2, final float float3, final float float4, final float float5, final float float6) {
        final float float7 = -Mth.sin(float3 * 0.017453292f) * Mth.cos(float2 * 0.017453292f);
        final float float8 = -Mth.sin((float2 + float4) * 0.017453292f);
        final float float9 = Mth.cos(float3 * 0.017453292f) * Mth.cos(float2 * 0.017453292f);
        this.shoot(float7, float8, float9, float5, float6);
        final Vec3 cvl11 = akq.getDeltaMovement();
        this.setDeltaMovement(this.getDeltaMovement().add(cvl11.x, akq.onGround ? 0.0 : cvl11.y, cvl11.z));
    }
    
    @Override
    public void shoot(final double double1, final double double2, final double double3, final float float4, final float float5) {
        final Vec3 cvl10 = new Vec3(double1, double2, double3).normalize().add(this.random.nextGaussian() * 0.007499999832361937 * float5, this.random.nextGaussian() * 0.007499999832361937 * float5, this.random.nextGaussian() * 0.007499999832361937 * float5).scale(float4);
        this.setDeltaMovement(cvl10);
        final float float6 = Mth.sqrt(Entity.getHorizontalDistanceSqr(cvl10));
        this.yRot = (float)(Mth.atan2(cvl10.x, cvl10.z) * 57.2957763671875);
        this.xRot = (float)(Mth.atan2(cvl10.y, float6) * 57.2957763671875);
        this.yRotO = this.yRot;
        this.xRotO = this.xRot;
    }
    
    @Override
    public void lerpMotion(final double double1, final double double2, final double double3) {
        this.setDeltaMovement(double1, double2, double3);
        if (this.xRotO == 0.0f && this.yRotO == 0.0f) {
            final float float8 = Mth.sqrt(double1 * double1 + double3 * double3);
            this.yRot = (float)(Mth.atan2(double1, double3) * 57.2957763671875);
            this.xRot = (float)(Mth.atan2(double2, float8) * 57.2957763671875);
            this.yRotO = this.yRot;
            this.xRotO = this.xRot;
        }
    }
    
    @Override
    public void tick() {
        super.tick();
        if (this.shakeTime > 0) {
            --this.shakeTime;
        }
        if (this.inGround) {
            this.inGround = false;
            this.setDeltaMovement(this.getDeltaMovement().multiply(this.random.nextFloat() * 0.2f, this.random.nextFloat() * 0.2f, this.random.nextFloat() * 0.2f));
        }
        final AABB cvf2 = this.getBoundingBox().expandTowards(this.getDeltaMovement()).inflate(1.0);
        for (final Entity akq2 : this.level.getEntities(this, cvf2, akq -> !akq.isSpectator() && akq.isPickable())) {
            if (akq2 == this.entityToIgnore) {
                ++this.timeToIgnore;
                break;
            }
            if (this.owner != null && this.tickCount < 2 && this.entityToIgnore == null) {
                this.entityToIgnore = akq2;
                this.timeToIgnore = 3;
                break;
            }
        }
        final HitResult cvi3 = ProjectileUtil.getHitResult(this, cvf2, akq -> !akq.isSpectator() && akq.isPickable() && akq != this.entityToIgnore, ClipContext.Block.OUTLINE, true);
        if (this.entityToIgnore != null && this.timeToIgnore-- <= 0) {
            this.entityToIgnore = null;
        }
        if (cvi3.getType() != HitResult.Type.MISS) {
            if (cvi3.getType() == HitResult.Type.BLOCK && this.level.getBlockState(((BlockHitResult)cvi3).getBlockPos()).getBlock() == Blocks.NETHER_PORTAL) {
                this.handleInsidePortal(((BlockHitResult)cvi3).getBlockPos());
            }
            else {
                this.onHit(cvi3);
            }
        }
        final Vec3 cvl4 = this.getDeltaMovement();
        final double double5 = this.getX() + cvl4.x;
        final double double6 = this.getY() + cvl4.y;
        final double double7 = this.getZ() + cvl4.z;
        final float float11 = Mth.sqrt(Entity.getHorizontalDistanceSqr(cvl4));
        this.yRot = (float)(Mth.atan2(cvl4.x, cvl4.z) * 57.2957763671875);
        this.xRot = (float)(Mth.atan2(cvl4.y, float11) * 57.2957763671875);
        while (this.xRot - this.xRotO < -180.0f) {
            this.xRotO -= 360.0f;
        }
        while (this.xRot - this.xRotO >= 180.0f) {
            this.xRotO += 360.0f;
        }
        while (this.yRot - this.yRotO < -180.0f) {
            this.yRotO -= 360.0f;
        }
        while (this.yRot - this.yRotO >= 180.0f) {
            this.yRotO += 360.0f;
        }
        this.xRot = Mth.lerp(0.2f, this.xRotO, this.xRot);
        this.yRot = Mth.lerp(0.2f, this.yRotO, this.yRot);
        float float13;
        if (this.isInWater()) {
            for (int integer13 = 0; integer13 < 4; ++integer13) {
                final float float12 = 0.25f;
                this.level.addParticle(ParticleTypes.BUBBLE, double5 - cvl4.x * 0.25, double6 - cvl4.y * 0.25, double7 - cvl4.z * 0.25, cvl4.x, cvl4.y, cvl4.z);
            }
            float13 = 0.8f;
        }
        else {
            float13 = 0.99f;
        }
        this.setDeltaMovement(cvl4.scale(float13));
        if (!this.isNoGravity()) {
            final Vec3 cvl5 = this.getDeltaMovement();
            this.setDeltaMovement(cvl5.x, cvl5.y - this.getGravity(), cvl5.z);
        }
        this.setPos(double5, double6, double7);
    }
    
    protected float getGravity() {
        return 0.03f;
    }
    
    protected abstract void onHit(final HitResult cvi);
    
    public void addAdditionalSaveData(final CompoundTag jt) {
        jt.putInt("xTile", this.xBlock);
        jt.putInt("yTile", this.yBlock);
        jt.putInt("zTile", this.zBlock);
        jt.putByte("shake", (byte)this.shakeTime);
        jt.putBoolean("inGround", this.inGround);
        if (this.ownerId != null) {
            jt.put("owner", NbtUtils.createUUIDTag(this.ownerId));
        }
    }
    
    public void readAdditionalSaveData(final CompoundTag jt) {
        this.xBlock = jt.getInt("xTile");
        this.yBlock = jt.getInt("yTile");
        this.zBlock = jt.getInt("zTile");
        this.shakeTime = (jt.getByte("shake") & 0xFF);
        this.inGround = jt.getBoolean("inGround");
        this.owner = null;
        if (jt.contains("owner", 10)) {
            this.ownerId = NbtUtils.loadUUIDTag(jt.getCompound("owner"));
        }
    }
    
    @Nullable
    public LivingEntity getOwner() {
        if ((this.owner == null || this.owner.removed) && this.ownerId != null && this.level instanceof ServerLevel) {
            final Entity akq2 = ((ServerLevel)this.level).getEntity(this.ownerId);
            if (akq2 instanceof LivingEntity) {
                this.owner = (LivingEntity)akq2;
            }
            else {
                this.owner = null;
            }
        }
        return this.owner;
    }
    
    @Override
    public Packet<?> getAddEntityPacket() {
        return new ClientboundAddEntityPacket(this);
    }
}
