package net.minecraft.world.entity.monster;

import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.syncher.EntityDataSerializers;
import java.util.Iterator;
import java.util.Collection;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.AreaEffectCloud;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.global.LightningBolt;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.item.Items;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
import net.minecraft.world.entity.animal.Cat;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
import net.minecraft.world.entity.animal.Ocelot;
import net.minecraft.world.entity.ai.goal.SwellGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.FloatGoal;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.entity.PowerableMob;

public class Creeper extends Monster implements PowerableMob {
    private static final EntityDataAccessor<Integer> DATA_SWELL_DIR;
    private static final EntityDataAccessor<Boolean> DATA_IS_POWERED;
    private static final EntityDataAccessor<Boolean> DATA_IS_IGNITED;
    private int oldSwell;
    private int swell;
    private int maxSwell;
    private int explosionRadius;
    private int droppedSkulls;
    
    public Creeper(final EntityType<? extends Creeper> aku, final Level bjw) {
        super(aku, bjw);
        this.maxSwell = 30;
        this.explosionRadius = 3;
    }
    
    @Override
    protected void registerGoals() {
        this.goalSelector.addGoal(1, new FloatGoal(this));
        this.goalSelector.addGoal(2, new SwellGoal(this));
        this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Ocelot.class, 6.0f, 1.0, 1.2));
        this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Cat.class, 6.0f, 1.0, 1.2));
        this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, false));
        this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8));
        this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0f));
        this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
        this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true));
        this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
    }
    
    @Override
    protected void registerAttributes() {
        super.registerAttributes();
        this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25);
    }
    
    @Override
    public int getMaxFallDistance() {
        if (this.getTarget() == null) {
            return 3;
        }
        return 3 + (int)(this.getHealth() - 1.0f);
    }
    
    @Override
    public boolean causeFallDamage(final float float1, final float float2) {
        final boolean boolean4 = super.causeFallDamage(float1, float2);
        this.swell += (int)(float1 * 1.5f);
        if (this.swell > this.maxSwell - 5) {
            this.swell = this.maxSwell - 5;
        }
        return boolean4;
    }
    
    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.<Integer>define(Creeper.DATA_SWELL_DIR, -1);
        this.entityData.<Boolean>define(Creeper.DATA_IS_POWERED, false);
        this.entityData.<Boolean>define(Creeper.DATA_IS_IGNITED, false);
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag jt) {
        super.addAdditionalSaveData(jt);
        if (this.entityData.<Boolean>get(Creeper.DATA_IS_POWERED)) {
            jt.putBoolean("powered", true);
        }
        jt.putShort("Fuse", (short)this.maxSwell);
        jt.putByte("ExplosionRadius", (byte)this.explosionRadius);
        jt.putBoolean("ignited", this.isIgnited());
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag jt) {
        super.readAdditionalSaveData(jt);
        this.entityData.<Boolean>set(Creeper.DATA_IS_POWERED, jt.getBoolean("powered"));
        if (jt.contains("Fuse", 99)) {
            this.maxSwell = jt.getShort("Fuse");
        }
        if (jt.contains("ExplosionRadius", 99)) {
            this.explosionRadius = jt.getByte("ExplosionRadius");
        }
        if (jt.getBoolean("ignited")) {
            this.ignite();
        }
    }
    
    @Override
    public void tick() {
        if (this.isAlive()) {
            this.oldSwell = this.swell;
            if (this.isIgnited()) {
                this.setSwellDir(1);
            }
            final int integer2 = this.getSwellDir();
            if (integer2 > 0 && this.swell == 0) {
                this.playSound(SoundEvents.CREEPER_PRIMED, 1.0f, 0.5f);
            }
            this.swell += integer2;
            if (this.swell < 0) {
                this.swell = 0;
            }
            if (this.swell >= this.maxSwell) {
                this.swell = this.maxSwell;
                this.explodeCreeper();
            }
        }
        super.tick();
    }
    
    @Override
    protected SoundEvent getHurtSound(final DamageSource ajz) {
        return SoundEvents.CREEPER_HURT;
    }
    
    @Override
    protected SoundEvent getDeathSound() {
        return SoundEvents.CREEPER_DEATH;
    }
    
    @Override
    protected void dropCustomDeathLoot(final DamageSource ajz, final int integer, final boolean boolean3) {
        super.dropCustomDeathLoot(ajz, integer, boolean3);
        final Entity akq5 = ajz.getEntity();
        if (akq5 != this && akq5 instanceof Creeper) {
            final Creeper awi6 = (Creeper)akq5;
            if (awi6.canDropMobsSkull()) {
                awi6.increaseDroppedSkulls();
                this.spawnAtLocation(Items.CREEPER_HEAD);
            }
        }
    }
    
    @Override
    public boolean doHurtTarget(final Entity akq) {
        return true;
    }
    
    @Override
    public boolean isPowered() {
        return this.entityData.<Boolean>get(Creeper.DATA_IS_POWERED);
    }
    
    public float getSwelling(final float float1) {
        return Mth.lerp(float1, (float)this.oldSwell, (float)this.swell) / (this.maxSwell - 2);
    }
    
    public int getSwellDir() {
        return this.entityData.<Integer>get(Creeper.DATA_SWELL_DIR);
    }
    
    public void setSwellDir(final int integer) {
        this.entityData.<Integer>set(Creeper.DATA_SWELL_DIR, integer);
    }
    
    @Override
    public void thunderHit(final LightningBolt avy) {
        super.thunderHit(avy);
        this.entityData.<Boolean>set(Creeper.DATA_IS_POWERED, true);
    }
    
    @Override
    protected boolean mobInteract(final Player ayj, final InteractionHand ajk) {
        final ItemStack ben4 = ayj.getItemInHand(ajk);
        if (ben4.getItem() == Items.FLINT_AND_STEEL) {
            this.level.playSound(ayj, this.getX(), this.getY(), this.getZ(), SoundEvents.FLINTANDSTEEL_USE, this.getSoundSource(), 1.0f, this.random.nextFloat() * 0.4f + 0.8f);
            if (!this.level.isClientSide) {
                this.ignite();
                ben4.<Player>hurtAndBreak(1, ayj, ayj -> ayj.broadcastBreakEvent(ajk));
            }
            return true;
        }
        return super.mobInteract(ayj, ajk);
    }
    
    private void explodeCreeper() {
        if (!this.level.isClientSide) {
            final Explosion.BlockInteraction a2 = this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? Explosion.BlockInteraction.DESTROY : Explosion.BlockInteraction.NONE;
            final float float3 = this.isPowered() ? 2.0f : 1.0f;
            this.dead = true;
            this.level.explode(this, this.getX(), this.getY(), this.getZ(), this.explosionRadius * float3, a2);
            this.remove();
            this.spawnLingeringCloud();
        }
    }
    
    private void spawnLingeringCloud() {
        final Collection<MobEffectInstance> collection2 = this.getActiveEffects();
        if (!collection2.isEmpty()) {
            final AreaEffectCloud akp3 = new AreaEffectCloud(this.level, this.getX(), this.getY(), this.getZ());
            akp3.setRadius(2.5f);
            akp3.setRadiusOnUse(-0.5f);
            akp3.setWaitTime(10);
            akp3.setDuration(akp3.getDuration() / 2);
            akp3.setRadiusPerTick(-akp3.getRadius() / akp3.getDuration());
            for (final MobEffectInstance akk5 : collection2) {
                akp3.addEffect(new MobEffectInstance(akk5));
            }
            this.level.addFreshEntity(akp3);
        }
    }
    
    public boolean isIgnited() {
        return this.entityData.<Boolean>get(Creeper.DATA_IS_IGNITED);
    }
    
    public void ignite() {
        this.entityData.<Boolean>set(Creeper.DATA_IS_IGNITED, true);
    }
    
    public boolean canDropMobsSkull() {
        return this.isPowered() && this.droppedSkulls < 1;
    }
    
    public void increaseDroppedSkulls() {
        ++this.droppedSkulls;
    }
    
    static {
        DATA_SWELL_DIR = SynchedEntityData.<Integer>defineId(Creeper.class, EntityDataSerializers.INT);
        DATA_IS_POWERED = SynchedEntityData.<Boolean>defineId(Creeper.class, EntityDataSerializers.BOOLEAN);
        DATA_IS_IGNITED = SynchedEntityData.<Boolean>defineId(Creeper.class, EntityDataSerializers.BOOLEAN);
    }
}
