package net.minecraft.world.entity.animal;

import java.util.stream.Collector;
import com.google.common.collect.ImmutableList;
import java.util.Comparator;
import java.util.stream.Stream;
import java.util.Iterator;
import java.util.List;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.NaturalSpawner;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.InteractionHand;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.monster.SharedMonsterAttributes;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.entity.monster.Enemy;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.DefendVillageTargetGoal;
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
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.OfferFlowerGoal;
import net.minecraft.world.entity.ai.goal.MoveThroughVillageGoal;
import net.minecraft.world.entity.ai.goal.MoveBackToVillage;
import net.minecraft.world.entity.ai.goal.MoveTowardsTargetGoal;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import net.minecraft.network.syncher.EntityDataAccessor;

public class IronGolem extends AbstractGolem {
    protected static final EntityDataAccessor<Byte> DATA_FLAGS_ID;
    private int attackAnimationTick;
    private int offerFlowerTick;
    
    public IronGolem(final EntityType<? extends IronGolem> aku, final Level bjw) {
        super(aku, bjw);
        this.maxUpStep = 1.0f;
    }
    
    @Override
    protected void registerGoals() {
        this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0, true));
        this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9, 32.0f));
        this.goalSelector.addGoal(2, new MoveBackToVillage(this, 0.6));
        this.goalSelector.addGoal(3, new MoveThroughVillageGoal(this, 0.6, false, 4, () -> false));
        this.goalSelector.addGoal(5, new OfferFlowerGoal(this));
        this.goalSelector.addGoal(6, new WaterAvoidingRandomStrollGoal(this, 0.6));
        this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0f));
        this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
        this.targetSelector.addGoal(1, new DefendVillageTargetGoal(this));
        this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
        this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Mob.class, 5, false, false, akz -> akz instanceof Enemy && !(akz instanceof Creeper)));
    }
    
    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.<Byte>define(IronGolem.DATA_FLAGS_ID, (Byte)0);
    }
    
    @Override
    protected void registerAttributes() {
        super.registerAttributes();
        this.getAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(100.0);
        this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25);
        this.getAttribute(SharedMonsterAttributes.KNOCKBACK_RESISTANCE).setBaseValue(1.0);
        this.getAttributes().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(15.0);
    }
    
    @Override
    protected int decreaseAirSupply(final int integer) {
        return integer;
    }
    
    @Override
    protected void doPush(final Entity akq) {
        if (akq instanceof Enemy && !(akq instanceof Creeper) && this.getRandom().nextInt(20) == 0) {
            this.setTarget((LivingEntity)akq);
        }
        super.doPush(akq);
    }
    
    @Override
    public void aiStep() {
        super.aiStep();
        if (this.attackAnimationTick > 0) {
            --this.attackAnimationTick;
        }
        if (this.offerFlowerTick > 0) {
            --this.offerFlowerTick;
        }
        if (Entity.getHorizontalDistanceSqr(this.getDeltaMovement()) > 2.500000277905201E-7 && this.random.nextInt(5) == 0) {
            final int integer2 = Mth.floor(this.getX());
            final int integer3 = Mth.floor(this.getY() - 0.20000000298023224);
            final int integer4 = Mth.floor(this.getZ());
            final BlockState byj5 = this.level.getBlockState(new BlockPos(integer2, integer3, integer4));
            if (!byj5.isAir()) {
                this.level.addParticle(new BlockParticleOption(ParticleTypes.BLOCK, byj5), this.getX() + (this.random.nextFloat() - 0.5) * this.getBbWidth(), this.getY() + 0.1, this.getZ() + (this.random.nextFloat() - 0.5) * this.getBbWidth(), 4.0 * (this.random.nextFloat() - 0.5), 0.5, (this.random.nextFloat() - 0.5) * 4.0);
            }
        }
    }
    
    @Override
    public boolean canAttackType(final EntityType<?> aku) {
        return (!this.isPlayerCreated() || aku != EntityType.PLAYER) && aku != EntityType.CREEPER && super.canAttackType(aku);
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag jt) {
        super.addAdditionalSaveData(jt);
        jt.putBoolean("PlayerCreated", this.isPlayerCreated());
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag jt) {
        super.readAdditionalSaveData(jt);
        this.setPlayerCreated(jt.getBoolean("PlayerCreated"));
    }
    
    private float getAttackDamage() {
        return (float)this.getAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).getValue();
    }
    
    @Override
    public boolean doHurtTarget(final Entity akq) {
        this.attackAnimationTick = 10;
        this.level.broadcastEntityEvent(this, (byte)4);
        final float float3 = this.getAttackDamage();
        final float float4 = (float3 > 0.0f) ? (float3 / 2.0f + this.random.nextInt((int)float3)) : 0.0f;
        final boolean boolean5 = akq.hurt(DamageSource.mobAttack(this), float4);
        if (boolean5) {
            akq.setDeltaMovement(akq.getDeltaMovement().add(0.0, 0.4000000059604645, 0.0));
            this.doEnchantDamageEffects(this, akq);
        }
        this.playSound(SoundEvents.IRON_GOLEM_ATTACK, 1.0f, 1.0f);
        return boolean5;
    }
    
    @Override
    public boolean hurt(final DamageSource ajz, final float float2) {
        final Crackiness a4 = this.getCrackiness();
        final boolean boolean5 = super.hurt(ajz, float2);
        if (boolean5 && this.getCrackiness() != a4) {
            this.playSound(SoundEvents.IRON_GOLEM_DAMAGE, 1.0f, 1.0f);
        }
        return boolean5;
    }
    
    public Crackiness getCrackiness() {
        return Crackiness.byFraction(this.getHealth() / this.getMaxHealth());
    }
    
    @Override
    public void handleEntityEvent(final byte byte1) {
        if (byte1 == 4) {
            this.attackAnimationTick = 10;
            this.playSound(SoundEvents.IRON_GOLEM_ATTACK, 1.0f, 1.0f);
        }
        else if (byte1 == 11) {
            this.offerFlowerTick = 400;
        }
        else if (byte1 == 34) {
            this.offerFlowerTick = 0;
        }
        else {
            super.handleEntityEvent(byte1);
        }
    }
    
    public int getAttackAnimationTick() {
        return this.attackAnimationTick;
    }
    
    public void offerFlower(final boolean boolean1) {
        if (boolean1) {
            this.offerFlowerTick = 400;
            this.level.broadcastEntityEvent(this, (byte)11);
        }
        else {
            this.offerFlowerTick = 0;
            this.level.broadcastEntityEvent(this, (byte)34);
        }
    }
    
    @Override
    protected SoundEvent getHurtSound(final DamageSource ajz) {
        return SoundEvents.IRON_GOLEM_HURT;
    }
    
    @Override
    protected SoundEvent getDeathSound() {
        return SoundEvents.IRON_GOLEM_DEATH;
    }
    
    @Override
    protected boolean mobInteract(final Player ayj, final InteractionHand ajk) {
        final ItemStack ben4 = ayj.getItemInHand(ajk);
        final Item bei5 = ben4.getItem();
        if (bei5 != Items.IRON_INGOT) {
            return false;
        }
        final float float6 = this.getHealth();
        this.heal(25.0f);
        if (this.getHealth() == float6) {
            return false;
        }
        final float float7 = 1.0f + (this.random.nextFloat() - this.random.nextFloat()) * 0.2f;
        this.playSound(SoundEvents.IRON_GOLEM_REPAIR, 1.0f, float7);
        if (!ayj.abilities.instabuild) {
            ben4.shrink(1);
        }
        return true;
    }
    
    @Override
    protected void playStepSound(final BlockPos fk, final BlockState byj) {
        this.playSound(SoundEvents.IRON_GOLEM_STEP, 1.0f, 1.0f);
    }
    
    public int getOfferFlowerTick() {
        return this.offerFlowerTick;
    }
    
    public boolean isPlayerCreated() {
        return (this.entityData.<Byte>get(IronGolem.DATA_FLAGS_ID) & 0x1) != 0x0;
    }
    
    public void setPlayerCreated(final boolean boolean1) {
        final byte byte3 = this.entityData.<Byte>get(IronGolem.DATA_FLAGS_ID);
        if (boolean1) {
            this.entityData.<Byte>set(IronGolem.DATA_FLAGS_ID, (byte)(byte3 | 0x1));
        }
        else {
            this.entityData.<Byte>set(IronGolem.DATA_FLAGS_ID, (byte)(byte3 & 0xFFFFFFFE));
        }
    }
    
    @Override
    public void die(final DamageSource ajz) {
        super.die(ajz);
    }
    
    @Override
    public boolean checkSpawnObstruction(final LevelReader bjz) {
        final BlockPos fk3 = new BlockPos(this);
        final BlockPos fk4 = fk3.below();
        final BlockState byj5 = bjz.getBlockState(fk4);
        if (byj5.entityCanStandOn(bjz, fk4, this)) {
            for (int integer6 = 1; integer6 < 3; ++integer6) {
                final BlockPos fk5 = fk3.above(integer6);
                final BlockState byj6 = bjz.getBlockState(fk5);
                if (!NaturalSpawner.isValidEmptySpawnBlock(bjz, fk5, byj6, byj6.getFluidState())) {
                    return false;
                }
            }
            return NaturalSpawner.isValidEmptySpawnBlock(bjz, fk3, bjz.getBlockState(fk3), Fluids.EMPTY.defaultFluidState()) && bjz.isUnobstructed(this);
        }
        return false;
    }
    
    static {
        DATA_FLAGS_ID = SynchedEntityData.<Byte>defineId(IronGolem.class, EntityDataSerializers.BYTE);
    }
    
    public enum Crackiness {
        NONE(1.0f), 
        LOW(0.75f), 
        MEDIUM(0.5f), 
        HIGH(0.25f);
        
        private static final List<Crackiness> BY_DAMAGE;
        private final float fraction;
        
        private Crackiness(final float float3) {
            this.fraction = float3;
        }
        
        public static Crackiness byFraction(final float float1) {
            for (final Crackiness a3 : Crackiness.BY_DAMAGE) {
                if (float1 < a3.fraction) {
                    return a3;
                }
            }
            return Crackiness.NONE;
        }
        
        static {
            BY_DAMAGE = Stream.<Crackiness>of(values()).sorted(Comparator.comparingDouble(a -> a.fraction)).collect(ImmutableList.toImmutableList());
        }
    }
}
