package net.minecraft.world.entity.npc;

import net.minecraft.world.entity.ai.sensing.NearestLivingEntitySensor;
import com.google.common.collect.ImmutableMap;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.core.SerializableLong;
import net.minecraft.world.entity.ai.gossip.GossipType;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.entity.animal.IronGolem;
import net.minecraft.world.phys.AABB;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.monster.Witch;
import net.minecraft.world.entity.global.LightningBolt;
import net.minecraft.world.entity.AgableMob;
import net.minecraft.world.entity.SpawnGroupData;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.core.Registry;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.network.protocol.game.DebugPackets;
import java.util.Optional;
import java.util.List;
import net.minecraft.world.entity.ExperienceOrb;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.nbt.ListTag;
import net.minecraft.world.item.trading.MerchantOffers;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import java.util.Iterator;
import net.minecraft.world.item.trading.MerchantOffer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.item.ItemStack;
import net.minecraft.stats.Stats;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.Items;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.raid.Raid;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ai.village.ReputationEventType;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import com.google.common.collect.ImmutableSet;
import com.mojang.datafixers.util.Pair;
import net.minecraft.world.entity.ai.memory.MemoryStatus;
import net.minecraft.world.entity.ai.behavior.VillagerGoalPackages;
import net.minecraft.world.entity.schedule.Activity;
import net.minecraft.world.entity.schedule.Schedule;
import net.minecraft.world.entity.monster.SharedMonsterAttributes;
import net.minecraft.server.level.ServerLevel;
import java.util.Collection;
import net.minecraft.world.entity.ai.Brain;
import com.mojang.datafixers.types.DynamicOps;
import com.mojang.datafixers.Dynamic;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.ai.village.poi.PoiType;
import java.util.function.BiPredicate;
import net.minecraft.core.GlobalPos;
import net.minecraft.world.entity.ai.sensing.Sensor;
import net.minecraft.world.entity.ai.sensing.SensorType;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import com.google.common.collect.ImmutableList;
import net.minecraft.world.entity.ai.gossip.GossipContainer;
import javax.annotation.Nullable;
import net.minecraft.world.entity.player.Player;
import java.util.Set;
import net.minecraft.world.item.Item;
import java.util.Map;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.entity.ReputationEventHandler;

public class Villager extends AbstractVillager implements ReputationEventHandler, VillagerDataHolder {
    private static final EntityDataAccessor<VillagerData> DATA_VILLAGER_DATA;
    public static final Map<Item, Integer> FOOD_POINTS;
    private static final Set<Item> WANTED_ITEMS;
    private int updateMerchantTimer;
    private boolean increaseProfessionLevelOnUpdate;
    @Nullable
    private Player lastTradedPlayer;
    private byte foodLevel;
    private final GossipContainer gossips;
    private long lastGossipTime;
    private long lastGossipDecayTime;
    private int villagerXp;
    private long lastRestockGameTime;
    private int numberOfRestocksToday;
    private long lastRestockCheckDayTime;
    private static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES;
    private static final ImmutableList<SensorType<? extends Sensor<? super Villager>>> SENSOR_TYPES;
    public static final Map<MemoryModuleType<GlobalPos>, BiPredicate<Villager, PoiType>> POI_MEMORIES;
    
    public Villager(final EntityType<? extends Villager> aku, final Level bjw) {
        this(aku, bjw, VillagerType.PLAINS);
    }
    
    public Villager(final EntityType<? extends Villager> aku, final Level bjw, final VillagerType ayb) {
        super(aku, bjw);
        this.gossips = new GossipContainer();
        ((GroundPathNavigation)this.getNavigation()).setCanOpenDoors(true);
        this.getNavigation().setCanFloat(true);
        this.setCanPickUpLoot(true);
        this.setVillagerData(this.getVillagerData().setType(ayb).setProfession(VillagerProfession.NONE));
        this.brain = this.makeBrain(new Dynamic((DynamicOps)NbtOps.INSTANCE, new CompoundTag()));
    }
    
    @Override
    public Brain<Villager> getBrain() {
        return (Brain<Villager>)super.getBrain();
    }
    
    @Override
    protected Brain<?> makeBrain(final Dynamic<?> dynamic) {
        final Brain<Villager> alp3 = new Brain<Villager>(Villager.MEMORY_TYPES, Villager.SENSOR_TYPES, dynamic);
        this.registerBrainGoals(alp3);
        return alp3;
    }
    
    public void refreshBrain(final ServerLevel xd) {
        final Brain<Villager> alp3 = this.getBrain();
        alp3.stopAll(xd, this);
        this.brain = alp3.copyWithoutGoals();
        this.registerBrainGoals(this.getBrain());
    }
    
    private void registerBrainGoals(final Brain<Villager> alp) {
        final VillagerProfession axz3 = this.getVillagerData().getProfession();
        final float float4 = (float)this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getValue();
        if (this.isBaby()) {
            alp.setSchedule(Schedule.VILLAGER_BABY);
            alp.addActivity(Activity.PLAY, VillagerGoalPackages.getPlayPackage(float4));
        }
        else {
            alp.setSchedule(Schedule.VILLAGER_DEFAULT);
            alp.addActivity(Activity.WORK, VillagerGoalPackages.getWorkPackage(axz3, float4), ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT)));
        }
        alp.addActivity(Activity.CORE, VillagerGoalPackages.getCorePackage(axz3, float4));
        alp.addActivity(Activity.MEET, VillagerGoalPackages.getMeetPackage(axz3, float4), ImmutableSet.of(Pair.of(MemoryModuleType.MEETING_POINT, MemoryStatus.VALUE_PRESENT)));
        alp.addActivity(Activity.REST, VillagerGoalPackages.getRestPackage(axz3, float4));
        alp.addActivity(Activity.IDLE, VillagerGoalPackages.getIdlePackage(axz3, float4));
        alp.addActivity(Activity.PANIC, VillagerGoalPackages.getPanicPackage(axz3, float4));
        alp.addActivity(Activity.PRE_RAID, VillagerGoalPackages.getPreRaidPackage(axz3, float4));
        alp.addActivity(Activity.RAID, VillagerGoalPackages.getRaidPackage(axz3, float4));
        alp.addActivity(Activity.HIDE, VillagerGoalPackages.getHidePackage(axz3, float4));
        alp.setCoreActivities(ImmutableSet.<Activity>of(Activity.CORE));
        alp.setDefaultActivity(Activity.IDLE);
        alp.setActivity(Activity.IDLE);
        alp.updateActivity(this.level.getDayTime(), this.level.getGameTime());
    }
    
    @Override
    protected void ageBoundaryReached() {
        super.ageBoundaryReached();
        if (this.level instanceof ServerLevel) {
            this.refreshBrain((ServerLevel)this.level);
        }
    }
    
    @Override
    protected void registerAttributes() {
        super.registerAttributes();
        this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5);
        this.getAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(48.0);
    }
    
    @Override
    protected void customServerAiStep() {
        this.level.getProfiler().push("brain");
        this.getBrain().tick((ServerLevel)this.level, this);
        this.level.getProfiler().pop();
        if (!this.isTrading() && this.updateMerchantTimer > 0) {
            --this.updateMerchantTimer;
            if (this.updateMerchantTimer <= 0) {
                if (this.increaseProfessionLevelOnUpdate) {
                    this.increaseMerchantCareer();
                    this.increaseProfessionLevelOnUpdate = false;
                }
                this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 200, 0));
            }
        }
        if (this.lastTradedPlayer != null && this.level instanceof ServerLevel) {
            ((ServerLevel)this.level).onReputationEvent(ReputationEventType.TRADE, this.lastTradedPlayer, this);
            this.level.broadcastEntityEvent(this, (byte)14);
            this.lastTradedPlayer = null;
        }
        if (!this.isNoAi() && this.random.nextInt(100) == 0) {
            final Raid azn2 = ((ServerLevel)this.level).getRaidAt(new BlockPos(this));
            if (azn2 != null && azn2.isActive() && !azn2.isOver()) {
                this.level.broadcastEntityEvent(this, (byte)42);
            }
        }
        if (this.getVillagerData().getProfession() == VillagerProfession.NONE && this.isTrading()) {
            this.stopTrading();
        }
        super.customServerAiStep();
    }
    
    @Override
    public void tick() {
        super.tick();
        if (this.getUnhappyCounter() > 0) {
            this.setUnhappyCounter(this.getUnhappyCounter() - 1);
        }
        this.maybeDecayGossip();
    }
    
    @Override
    public boolean mobInteract(final Player ayj, final InteractionHand ajk) {
        final ItemStack ben4 = ayj.getItemInHand(ajk);
        final boolean boolean5 = ben4.getItem() == Items.NAME_TAG;
        if (boolean5) {
            ben4.interactEnemy(ayj, this, ajk);
            return true;
        }
        if (ben4.getItem() == Items.VILLAGER_SPAWN_EGG || !this.isAlive() || this.isTrading() || this.isSleeping()) {
            return super.mobInteract(ayj, ajk);
        }
        if (this.isBaby()) {
            this.setUnhappy();
            return super.mobInteract(ayj, ajk);
        }
        final boolean boolean6 = this.getOffers().isEmpty();
        if (ajk == InteractionHand.MAIN_HAND) {
            if (boolean6 && !this.level.isClientSide) {
                this.setUnhappy();
            }
            ayj.awardStat(Stats.TALKED_TO_VILLAGER);
        }
        if (boolean6) {
            return super.mobInteract(ayj, ajk);
        }
        if (!this.level.isClientSide && !this.offers.isEmpty()) {
            this.startTrading(ayj);
        }
        return true;
    }
    
    private void setUnhappy() {
        this.setUnhappyCounter(40);
        if (!this.level.isClientSide()) {
            this.playSound(SoundEvents.VILLAGER_NO, this.getSoundVolume(), this.getVoicePitch());
        }
    }
    
    private void startTrading(final Player ayj) {
        this.updateSpecialPrices(ayj);
        this.setTradingPlayer(ayj);
        this.openTradingScreen(ayj, this.getDisplayName(), this.getVillagerData().getLevel());
    }
    
    @Override
    public void setTradingPlayer(@Nullable final Player ayj) {
        final boolean boolean3 = this.getTradingPlayer() != null && ayj == null;
        super.setTradingPlayer(ayj);
        if (boolean3) {
            this.stopTrading();
        }
    }
    
    @Override
    protected void stopTrading() {
        super.stopTrading();
        this.resetSpecialPrices();
    }
    
    private void resetSpecialPrices() {
        for (final MerchantOffer biz3 : this.getOffers()) {
            biz3.resetSpecialPriceDiff();
        }
    }
    
    @Override
    public boolean canRestock() {
        return true;
    }
    
    public void restock() {
        this.updateDemand();
        for (final MerchantOffer biz3 : this.getOffers()) {
            biz3.resetUses();
        }
        if (this.getVillagerData().getProfession() == VillagerProfession.FARMER) {
            this.makeBread();
        }
        this.lastRestockGameTime = this.level.getGameTime();
        ++this.numberOfRestocksToday;
    }
    
    private boolean needsToRestock() {
        for (final MerchantOffer biz3 : this.getOffers()) {
            if (biz3.needsRestock()) {
                return true;
            }
        }
        return false;
    }
    
    private boolean allowedToRestock() {
        return this.numberOfRestocksToday == 0 || (this.numberOfRestocksToday < 2 && this.level.getGameTime() > this.lastRestockGameTime + 2400L);
    }
    
    public boolean shouldRestock() {
        final long long2 = this.lastRestockGameTime + 12000L;
        final long long3 = this.level.getGameTime();
        boolean boolean6 = long3 > long2;
        final long long4 = this.level.getDayTime();
        if (this.lastRestockCheckDayTime > 0L) {
            final long long5 = this.lastRestockCheckDayTime / 24000L;
            final long long6 = long4 / 24000L;
            boolean6 |= (long6 > long5);
        }
        this.lastRestockCheckDayTime = long4;
        if (boolean6) {
            this.lastRestockGameTime = long3;
            this.resetNumberOfRestocks();
        }
        return this.allowedToRestock() && this.needsToRestock();
    }
    
    private void catchUpDemand() {
        final int integer2 = 2 - this.numberOfRestocksToday;
        if (integer2 > 0) {
            for (final MerchantOffer biz4 : this.getOffers()) {
                biz4.resetUses();
            }
        }
        for (int integer3 = 0; integer3 < integer2; ++integer3) {
            this.updateDemand();
        }
    }
    
    private void updateDemand() {
        for (final MerchantOffer biz3 : this.getOffers()) {
            biz3.updateDemand();
        }
    }
    
    private void updateSpecialPrices(final Player ayj) {
        final int integer3 = this.getPlayerReputation(ayj);
        if (integer3 != 0) {
            for (final MerchantOffer biz5 : this.getOffers()) {
                biz5.addToSpecialPriceDiff(-Mth.floor(integer3 * biz5.getPriceMultiplier()));
            }
        }
        if (ayj.hasEffect(MobEffects.HERO_OF_THE_VILLAGE)) {
            final MobEffectInstance akk4 = ayj.getEffect(MobEffects.HERO_OF_THE_VILLAGE);
            final int integer4 = akk4.getAmplifier();
            for (final MerchantOffer biz6 : this.getOffers()) {
                final double double8 = 0.3 + 0.0625 * integer4;
                final int integer5 = (int)Math.floor(double8 * biz6.getBaseCostA().getCount());
                biz6.addToSpecialPriceDiff(-Math.max(integer5, 1));
            }
        }
    }
    
    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.<VillagerData>define(Villager.DATA_VILLAGER_DATA, new VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, 1));
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag jt) {
        super.addAdditionalSaveData(jt);
        jt.put("VillagerData", this.getVillagerData().<Tag>serialize((com.mojang.datafixers.types.DynamicOps<Tag>)NbtOps.INSTANCE));
        jt.putByte("FoodLevel", this.foodLevel);
        jt.put("Gossips", (Tag)this.gossips.store((com.mojang.datafixers.types.DynamicOps<Object>)NbtOps.INSTANCE).getValue());
        jt.putInt("Xp", this.villagerXp);
        jt.putLong("LastRestock", this.lastRestockGameTime);
        jt.putLong("LastGossipDecay", this.lastGossipDecayTime);
        jt.putInt("RestocksToday", this.numberOfRestocksToday);
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag jt) {
        super.readAdditionalSaveData(jt);
        if (jt.contains("VillagerData", 10)) {
            this.setVillagerData(new VillagerData(new Dynamic((DynamicOps)NbtOps.INSTANCE, jt.get("VillagerData"))));
        }
        if (jt.contains("Offers", 10)) {
            this.offers = new MerchantOffers(jt.getCompound("Offers"));
        }
        if (jt.contains("FoodLevel", 1)) {
            this.foodLevel = jt.getByte("FoodLevel");
        }
        final ListTag jz3 = jt.getList("Gossips", 10);
        this.gossips.update(new Dynamic((DynamicOps)NbtOps.INSTANCE, jz3));
        if (jt.contains("Xp", 3)) {
            this.villagerXp = jt.getInt("Xp");
        }
        this.lastRestockGameTime = jt.getLong("LastRestock");
        this.lastGossipDecayTime = jt.getLong("LastGossipDecay");
        this.setCanPickUpLoot(true);
        if (this.level instanceof ServerLevel) {
            this.refreshBrain((ServerLevel)this.level);
        }
        this.numberOfRestocksToday = jt.getInt("RestocksToday");
    }
    
    @Override
    public boolean removeWhenFarAway(final double double1) {
        return false;
    }
    
    @Nullable
    @Override
    protected SoundEvent getAmbientSound() {
        if (this.isSleeping()) {
            return null;
        }
        if (this.isTrading()) {
            return SoundEvents.VILLAGER_TRADE;
        }
        return SoundEvents.VILLAGER_AMBIENT;
    }
    
    @Override
    protected SoundEvent getHurtSound(final DamageSource ajz) {
        return SoundEvents.VILLAGER_HURT;
    }
    
    @Override
    protected SoundEvent getDeathSound() {
        return SoundEvents.VILLAGER_DEATH;
    }
    
    public void playWorkSound() {
        final SoundEvent aah2 = this.getVillagerData().getProfession().getWorkSound();
        if (aah2 != null) {
            this.playSound(aah2, this.getSoundVolume(), this.getVoicePitch());
        }
    }
    
    public void setVillagerData(final VillagerData axx) {
        final VillagerData axx2 = this.getVillagerData();
        if (axx2.getProfession() != axx.getProfession()) {
            this.offers = null;
        }
        this.entityData.<VillagerData>set(Villager.DATA_VILLAGER_DATA, axx);
    }
    
    @Override
    public VillagerData getVillagerData() {
        return this.entityData.<VillagerData>get(Villager.DATA_VILLAGER_DATA);
    }
    
    @Override
    protected void rewardTradeXp(final MerchantOffer biz) {
        int integer3 = 3 + this.random.nextInt(4);
        this.villagerXp += biz.getXp();
        this.lastTradedPlayer = this.getTradingPlayer();
        if (this.shouldIncreaseLevel()) {
            this.updateMerchantTimer = 40;
            this.increaseProfessionLevelOnUpdate = true;
            integer3 += 5;
        }
        if (biz.shouldRewardExp()) {
            this.level.addFreshEntity(new ExperienceOrb(this.level, this.getX(), this.getY() + 0.5, this.getZ(), integer3));
        }
    }
    
    @Override
    public void setLastHurtByMob(@Nullable final LivingEntity akz) {
        if (akz != null && this.level instanceof ServerLevel) {
            ((ServerLevel)this.level).onReputationEvent(ReputationEventType.VILLAGER_HURT, akz, this);
            if (this.isAlive() && akz instanceof Player) {
                this.level.broadcastEntityEvent(this, (byte)13);
            }
        }
        super.setLastHurtByMob(akz);
    }
    
    @Override
    public void die(final DamageSource ajz) {
        Villager.LOGGER.info("Villager {} died, message: '{}'", this, ajz.getLocalizedDeathMessage(this).getString());
        final Entity akq3 = ajz.getEntity();
        if (akq3 != null) {
            this.tellWitnessesThatIWasMurdered(akq3);
        }
        this.releasePoi(MemoryModuleType.HOME);
        this.releasePoi(MemoryModuleType.JOB_SITE);
        this.releasePoi(MemoryModuleType.MEETING_POINT);
        super.die(ajz);
    }
    
    private void tellWitnessesThatIWasMurdered(final Entity akq) {
        if (!(this.level instanceof ServerLevel)) {
            return;
        }
        final Optional<List<LivingEntity>> optional3 = this.brain.<List<LivingEntity>>getMemory(MemoryModuleType.VISIBLE_LIVING_ENTITIES);
        if (!optional3.isPresent()) {
            return;
        }
        final ServerLevel xd4 = (ServerLevel)this.level;
        optional3.get().stream().filter(akz -> akz instanceof ReputationEventHandler).forEach(akz -> xd4.onReputationEvent(ReputationEventType.VILLAGER_KILLED, akq, akz));
    }
    
    public void releasePoi(final MemoryModuleType<GlobalPos> arl) {
        if (!(this.level instanceof ServerLevel)) {
            return;
        }
        final MinecraftServer minecraftServer3 = ((ServerLevel)this.level).getServer();
        final ServerLevel xd5;
        final PoiManager asr6;
        final Optional<PoiType> optional7;
        final BiPredicate<Villager, PoiType> biPredicate8;
        this.brain.<GlobalPos>getMemory(arl).ifPresent(fr -> {
            xd5 = minecraftServer3.getLevel(fr.dimension());
            asr6 = xd5.getPoiManager();
            optional7 = asr6.getType(fr.pos());
            biPredicate8 = Villager.POI_MEMORIES.get(arl);
            if (optional7.isPresent() && biPredicate8.test(this, optional7.get())) {
                asr6.release(fr.pos());
                DebugPackets.sendPoiTicketCountPacket(xd5, fr.pos());
            }
        });
    }
    
    public boolean canBreed() {
        return this.foodLevel + this.countFoodPointsInInventory() >= 12 && this.getAge() == 0;
    }
    
    private boolean hungry() {
        return this.foodLevel < 12;
    }
    
    private void eatUntilFull() {
        if (!this.hungry() || this.countFoodPointsInInventory() == 0) {
            return;
        }
        for (int integer2 = 0; integer2 < this.getInventory().getContainerSize(); ++integer2) {
            final ItemStack ben3 = this.getInventory().getItem(integer2);
            if (!ben3.isEmpty()) {
                final Integer integer3 = Villager.FOOD_POINTS.get(ben3.getItem());
                if (integer3 != null) {
                    int integer5;
                    for (int integer4 = integer5 = ben3.getCount(); integer5 > 0; --integer5) {
                        this.foodLevel += (byte)integer3;
                        this.getInventory().removeItem(integer2, 1);
                        if (!this.hungry()) {
                            return;
                        }
                    }
                }
            }
        }
    }
    
    public int getPlayerReputation(final Player ayj) {
        return this.gossips.getReputation(ayj.getUUID(), arj -> true);
    }
    
    private void digestFood(final int integer) {
        this.foodLevel -= (byte)integer;
    }
    
    public void eatAndDigestFood() {
        this.eatUntilFull();
        this.digestFood(12);
    }
    
    public void setOffers(final MerchantOffers bja) {
        this.offers = bja;
    }
    
    private boolean shouldIncreaseLevel() {
        final int integer2 = this.getVillagerData().getLevel();
        return VillagerData.canLevelUp(integer2) && this.villagerXp >= VillagerData.getMaxXpPerLevel(integer2);
    }
    
    private void increaseMerchantCareer() {
        this.setVillagerData(this.getVillagerData().setLevel(this.getVillagerData().getLevel() + 1));
        this.updateTrades();
    }
    
    @Override
    protected Component getTypeName() {
        return new TranslatableComponent(this.getType().getDescriptionId() + '.' + Registry.VILLAGER_PROFESSION.getKey(this.getVillagerData().getProfession()).getPath(), new Object[0]);
    }
    
    @Override
    public void handleEntityEvent(final byte byte1) {
        if (byte1 == 12) {
            this.addParticlesAroundSelf(ParticleTypes.HEART);
        }
        else if (byte1 == 13) {
            this.addParticlesAroundSelf(ParticleTypes.ANGRY_VILLAGER);
        }
        else if (byte1 == 14) {
            this.addParticlesAroundSelf(ParticleTypes.HAPPY_VILLAGER);
        }
        else if (byte1 == 42) {
            this.addParticlesAroundSelf(ParticleTypes.SPLASH);
        }
        else {
            super.handleEntityEvent(byte1);
        }
    }
    
    @Nullable
    @Override
    public SpawnGroupData finalizeSpawn(final LevelAccessor bjx, final DifficultyInstance ajj, final MobSpawnType alc, @Nullable final SpawnGroupData alm, @Nullable final CompoundTag jt) {
        if (alc == MobSpawnType.BREEDING) {
            this.setVillagerData(this.getVillagerData().setProfession(VillagerProfession.NONE));
        }
        if (alc == MobSpawnType.COMMAND || alc == MobSpawnType.SPAWN_EGG || alc == MobSpawnType.SPAWNER || alc == MobSpawnType.DISPENSER) {
            this.setVillagerData(this.getVillagerData().setType(VillagerType.byBiome(bjx.getBiome(new BlockPos(this)))));
        }
        return super.finalizeSpawn(bjx, ajj, alc, alm, jt);
    }
    
    @Override
    public Villager getBreedOffspring(final AgableMob ako) {
        final double double4 = this.random.nextDouble();
        VillagerType ayb3;
        if (double4 < 0.5) {
            ayb3 = VillagerType.byBiome(this.level.getBiome(new BlockPos(this)));
        }
        else if (double4 < 0.75) {
            ayb3 = this.getVillagerData().getType();
        }
        else {
            ayb3 = ((Villager)ako).getVillagerData().getType();
        }
        final Villager axw6 = new Villager(EntityType.VILLAGER, this.level, ayb3);
        axw6.finalizeSpawn(this.level, this.level.getCurrentDifficultyAt(new BlockPos(axw6)), MobSpawnType.BREEDING, null, null);
        return axw6;
    }
    
    @Override
    public void thunderHit(final LightningBolt avy) {
        final Witch axn3 = EntityType.WITCH.create(this.level);
        axn3.moveTo(this.getX(), this.getY(), this.getZ(), this.yRot, this.xRot);
        axn3.finalizeSpawn(this.level, this.level.getCurrentDifficultyAt(new BlockPos(axn3)), MobSpawnType.CONVERSION, null, null);
        axn3.setNoAi(this.isNoAi());
        if (this.hasCustomName()) {
            axn3.setCustomName(this.getCustomName());
            axn3.setCustomNameVisible(this.isCustomNameVisible());
        }
        this.level.addFreshEntity(axn3);
        this.remove();
    }
    
    @Override
    protected void pickUpItem(final ItemEntity awb) {
        final ItemStack ben3 = awb.getItem();
        final Item bei4 = ben3.getItem();
        if (this.wantToPickUp(bei4)) {
            final SimpleContainer ajq5 = this.getInventory();
            boolean boolean6 = false;
            for (int integer7 = 0; integer7 < ajq5.getContainerSize(); ++integer7) {
                final ItemStack ben4 = ajq5.getItem(integer7);
                if (ben4.isEmpty() || (ben4.getItem() == bei4 && ben4.getCount() < ben4.getMaxStackSize())) {
                    boolean6 = true;
                    break;
                }
            }
            if (!boolean6) {
                return;
            }
            int integer7 = ajq5.countItem(bei4);
            if (integer7 == 256) {
                return;
            }
            if (integer7 > 256) {
                ajq5.removeItemType(bei4, integer7 - 256);
                return;
            }
            this.take(awb, ben3.getCount());
            final ItemStack ben4 = ajq5.addItem(ben3);
            if (ben4.isEmpty()) {
                awb.remove();
            }
            else {
                ben3.setCount(ben4.getCount());
            }
        }
    }
    
    public boolean wantToPickUp(final Item bei) {
        return Villager.WANTED_ITEMS.contains(bei) || this.getVillagerData().getProfession().getRequestedItems().contains(bei);
    }
    
    public boolean hasExcessFood() {
        return this.countFoodPointsInInventory() >= 24;
    }
    
    public boolean wantsMoreFood() {
        return this.countFoodPointsInInventory() < 12;
    }
    
    private int countFoodPointsInInventory() {
        final SimpleContainer ajq2 = this.getInventory();
        return Villager.FOOD_POINTS.entrySet().stream().mapToInt(entry -> ajq2.countItem(entry.getKey()) * (int)entry.getValue()).sum();
    }
    
    private void makeBread() {
        final SimpleContainer ajq2 = this.getInventory();
        final int integer3 = ajq2.countItem(Items.WHEAT);
        final int integer4 = integer3 / 3;
        if (integer4 == 0) {
            return;
        }
        final int integer5 = integer4 * 3;
        ajq2.removeItemType(Items.WHEAT, integer5);
        final ItemStack ben6 = ajq2.addItem(new ItemStack(Items.BREAD, integer4));
        if (!ben6.isEmpty()) {
            this.spawnAtLocation(ben6, 0.5f);
        }
    }
    
    public boolean hasFarmSeeds() {
        final SimpleContainer ajq2 = this.getInventory();
        return ajq2.hasAnyOf(ImmutableSet.<Item>of(Items.WHEAT_SEEDS, Items.POTATO, Items.CARROT, Items.BEETROOT_SEEDS));
    }
    
    @Override
    protected void updateTrades() {
        final VillagerData axx2 = this.getVillagerData();
        final Int2ObjectMap<VillagerTrades.ItemListing[]> int2ObjectMap3 = VillagerTrades.TRADES.get(axx2.getProfession());
        if (int2ObjectMap3 == null || int2ObjectMap3.isEmpty()) {
            return;
        }
        final VillagerTrades.ItemListing[] arr4 = (VillagerTrades.ItemListing[])int2ObjectMap3.get(axx2.getLevel());
        if (arr4 == null) {
            return;
        }
        final MerchantOffers bja5 = this.getOffers();
        this.addOffersFromItemListings(bja5, arr4, 2);
    }
    
    public void gossip(final Villager axw, final long long2) {
        if ((long2 >= this.lastGossipTime && long2 < this.lastGossipTime + 1200L) || (long2 >= axw.lastGossipTime && long2 < axw.lastGossipTime + 1200L)) {
            return;
        }
        this.gossips.transferFrom(axw.gossips, this.random, 10);
        this.lastGossipTime = long2;
        this.spawnGolemIfNeeded(axw.lastGossipTime = long2, 5);
    }
    
    private void maybeDecayGossip() {
        final long long2 = this.level.getGameTime();
        if (this.lastGossipDecayTime == 0L) {
            this.lastGossipDecayTime = long2;
            return;
        }
        if (long2 < this.lastGossipDecayTime + 24000L) {
            return;
        }
        this.gossips.decay();
        this.lastGossipDecayTime = long2;
    }
    
    public void spawnGolemIfNeeded(final long long1, final int integer) {
        if (!this.wantsToSpawnGolem(long1)) {
            return;
        }
        final AABB cvf5 = this.getBoundingBox().inflate(10.0, 10.0, 10.0);
        final List<Villager> list6 = this.level.<Villager>getEntitiesOfClass(Villager.class, cvf5);
        final List<Villager> list7 = list6.stream().filter(axw -> axw.wantsToSpawnGolem(long1)).limit(5L).collect(Collectors.toList());
        if (list7.size() < integer) {
            return;
        }
        final IronGolem atl8 = this.trySpawnGolem();
        if (atl8 == null) {
            return;
        }
        list6.forEach(axw -> axw.sawGolem(long1));
    }
    
    private void sawGolem(final long long1) {
        this.brain.<Long>setMemory(MemoryModuleType.GOLEM_LAST_SEEN_TIME, long1);
    }
    
    private boolean hasSeenGolemRecently(final long long1) {
        final Optional<Long> optional4 = this.brain.<Long>getMemory(MemoryModuleType.GOLEM_LAST_SEEN_TIME);
        if (!optional4.isPresent()) {
            return false;
        }
        final Long long2 = optional4.get();
        return long1 - long2 <= 600L;
    }
    
    public boolean wantsToSpawnGolem(final long long1) {
        final VillagerData axx4 = this.getVillagerData();
        return axx4.getProfession() != VillagerProfession.NONE && axx4.getProfession() != VillagerProfession.NITWIT && this.golemSpawnConditionsMet(this.level.getGameTime()) && !this.hasSeenGolemRecently(long1);
    }
    
    @Nullable
    private IronGolem trySpawnGolem() {
        final BlockPos fk2 = new BlockPos(this);
        for (int integer3 = 0; integer3 < 10; ++integer3) {
            final double double4 = this.level.random.nextInt(16) - 8;
            final double double5 = this.level.random.nextInt(16) - 8;
            double double6 = 6.0;
            for (int integer4 = 0; integer4 >= -12; --integer4) {
                final BlockPos fk3 = fk2.offset(double4, double6 + integer4, double5);
                if ((this.level.getBlockState(fk3).isAir() || this.level.getBlockState(fk3).getMaterial().isLiquid()) && this.level.getBlockState(fk3.below()).getMaterial().isSolidBlocking()) {
                    double6 += integer4;
                    break;
                }
            }
            final BlockPos fk4 = fk2.offset(double4, double6, double5);
            final IronGolem atl11 = EntityType.IRON_GOLEM.create(this.level, null, null, null, fk4, MobSpawnType.MOB_SUMMONED, false, false);
            if (atl11 != null) {
                if (atl11.checkSpawnRules(this.level, MobSpawnType.MOB_SUMMONED) && atl11.checkSpawnObstruction(this.level)) {
                    this.level.addFreshEntity(atl11);
                    return atl11;
                }
                atl11.remove();
            }
        }
        return null;
    }
    
    @Override
    public void onReputationEventFrom(final ReputationEventType aso, final Entity akq) {
        if (aso == ReputationEventType.ZOMBIE_VILLAGER_CURED) {
            this.gossips.add(akq.getUUID(), GossipType.MAJOR_POSITIVE, 20);
            this.gossips.add(akq.getUUID(), GossipType.MINOR_POSITIVE, 25);
        }
        else if (aso == ReputationEventType.TRADE) {
            this.gossips.add(akq.getUUID(), GossipType.TRADING, 2);
        }
        else if (aso == ReputationEventType.VILLAGER_HURT) {
            this.gossips.add(akq.getUUID(), GossipType.MINOR_NEGATIVE, 25);
        }
        else if (aso == ReputationEventType.VILLAGER_KILLED) {
            this.gossips.add(akq.getUUID(), GossipType.MAJOR_NEGATIVE, 25);
        }
    }
    
    @Override
    public int getVillagerXp() {
        return this.villagerXp;
    }
    
    public void setVillagerXp(final int integer) {
        this.villagerXp = integer;
    }
    
    private void resetNumberOfRestocks() {
        this.catchUpDemand();
        this.numberOfRestocksToday = 0;
    }
    
    public GossipContainer getGossips() {
        return this.gossips;
    }
    
    public void setGossips(final Tag kj) {
        this.gossips.update(new Dynamic((DynamicOps)NbtOps.INSTANCE, kj));
    }
    
    @Override
    protected void sendDebugPackets() {
        super.sendDebugPackets();
        DebugPackets.sendEntityBrain(this);
    }
    
    @Override
    public void startSleeping(final BlockPos fk) {
        super.startSleeping(fk);
        this.brain.<SerializableLong>setMemory(MemoryModuleType.LAST_SLEPT, SerializableLong.of(this.level.getGameTime()));
    }
    
    @Override
    public void stopSleeping() {
        super.stopSleeping();
        this.brain.<SerializableLong>setMemory(MemoryModuleType.LAST_WOKEN, SerializableLong.of(this.level.getGameTime()));
    }
    
    private boolean golemSpawnConditionsMet(final long long1) {
        final Optional<SerializableLong> optional4 = this.brain.<SerializableLong>getMemory(MemoryModuleType.LAST_SLEPT);
        final Optional<SerializableLong> optional5 = this.brain.<SerializableLong>getMemory(MemoryModuleType.LAST_WORKED_AT_POI);
        return optional4.isPresent() && optional5.isPresent() && long1 - optional4.get().value() < 24000L && long1 - optional5.get().value() < 36000L;
    }
    
    static {
        DATA_VILLAGER_DATA = SynchedEntityData.<VillagerData>defineId(Villager.class, EntityDataSerializers.VILLAGER_DATA);
        FOOD_POINTS = ImmutableMap.<Item, Integer>of(Items.BREAD, Integer.valueOf(4), Items.POTATO, Integer.valueOf(1), Items.CARROT, Integer.valueOf(1), Items.BEETROOT, Integer.valueOf(1));
        WANTED_ITEMS = ImmutableSet.<Item>of(Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, new Item[] { Items.BEETROOT_SEEDS });
        MEMORY_TYPES = ImmutableList.<MemoryModuleType<GlobalPos>>of(MemoryModuleType.HOME, MemoryModuleType.JOB_SITE, MemoryModuleType.MEETING_POINT, (MemoryModuleType<GlobalPos>)MemoryModuleType.LIVING_ENTITIES, (MemoryModuleType<GlobalPos>)MemoryModuleType.VISIBLE_LIVING_ENTITIES, (MemoryModuleType<GlobalPos>)MemoryModuleType.VISIBLE_VILLAGER_BABIES, (MemoryModuleType<GlobalPos>)MemoryModuleType.NEAREST_PLAYERS, (MemoryModuleType<GlobalPos>)MemoryModuleType.NEAREST_VISIBLE_PLAYER, (MemoryModuleType<GlobalPos>)MemoryModuleType.WALK_TARGET, (MemoryModuleType<GlobalPos>)MemoryModuleType.LOOK_TARGET, (MemoryModuleType<GlobalPos>)MemoryModuleType.INTERACTION_TARGET, (MemoryModuleType<GlobalPos>)MemoryModuleType.BREED_TARGET, MemoryModuleType.PATH, MemoryModuleType.INTERACTABLE_DOORS, MemoryModuleType.OPENED_DOORS, MemoryModuleType.NEAREST_BED, MemoryModuleType.HURT_BY, MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.NEAREST_HOSTILE, MemoryModuleType.SECONDARY_JOB_SITE, MemoryModuleType.HIDING_PLACE, MemoryModuleType.HEARD_BELL_TIME, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.LAST_SLEPT, MemoryModuleType.LAST_WOKEN, MemoryModuleType.LAST_WORKED_AT_POI, MemoryModuleType.GOLEM_LAST_SEEN_TIME);
        SENSOR_TYPES = ImmutableList.<SensorType<NearestLivingEntitySensor>>of(SensorType.NEAREST_LIVING_ENTITIES, (SensorType<NearestLivingEntitySensor>)SensorType.NEAREST_PLAYERS, (SensorType<NearestLivingEntitySensor>)SensorType.INTERACTABLE_DOORS, (SensorType<NearestLivingEntitySensor>)SensorType.NEAREST_BED, (SensorType<NearestLivingEntitySensor>)SensorType.HURT_BY, (SensorType<NearestLivingEntitySensor>)SensorType.VILLAGER_HOSTILES, (SensorType<NearestLivingEntitySensor>)SensorType.VILLAGER_BABIES, (SensorType<NearestLivingEntitySensor>)SensorType.SECONDARY_POIS, (SensorType<NearestLivingEntitySensor>)SensorType.GOLEM_LAST_SEEN);
        POI_MEMORIES = ImmutableMap.<MemoryModuleType<GlobalPos>, Object>of(MemoryModuleType.HOME, (axw, asu) -> asu == PoiType.HOME, MemoryModuleType.JOB_SITE, (axw, asu) -> axw.getVillagerData().getProfession().getJobPoiType() == asu, MemoryModuleType.MEETING_POINT, (axw, asu) -> asu == PoiType.MEETING);
    }
}
