package net.minecraft.world.entity.npc;

import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.network.syncher.EntityDataSerializers;
import java.util.Iterator;
import java.util.Set;
import com.google.common.collect.Sets;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.nbt.Tag;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.trading.MerchantOffer;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.Pose;
import net.minecraft.nbt.CompoundTag;
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.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.item.trading.MerchantOffers;
import javax.annotation.Nullable;
import net.minecraft.world.entity.player.Player;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.world.item.trading.Merchant;
import net.minecraft.world.entity.AgableMob;

public abstract class AbstractVillager extends AgableMob implements Npc, Merchant {
    private static final EntityDataAccessor<Integer> DATA_UNHAPPY_COUNTER;
    @Nullable
    private Player tradingPlayer;
    @Nullable
    protected MerchantOffers offers;
    private final SimpleContainer inventory;
    
    public AbstractVillager(final EntityType<? extends AbstractVillager> aoq, final Level bqb) {
        super(aoq, bqb);
        this.inventory = new SimpleContainer(8);
        this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, 16.0f);
        this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, -1.0f);
    }
    
    @Override
    public SpawnGroupData finalizeSpawn(final LevelAccessor bqc, final DifficultyInstance ane, final MobSpawnType apb, @Nullable SpawnGroupData apo, @Nullable final CompoundTag le) {
        if (apo == null) {
            apo = new AgableMobGroupData();
            ((AgableMobGroupData)apo).setShouldSpawnBaby(false);
        }
        return super.finalizeSpawn(bqc, ane, apb, apo, le);
    }
    
    public int getUnhappyCounter() {
        return this.entityData.<Integer>get(AbstractVillager.DATA_UNHAPPY_COUNTER);
    }
    
    public void setUnhappyCounter(final int integer) {
        this.entityData.<Integer>set(AbstractVillager.DATA_UNHAPPY_COUNTER, integer);
    }
    
    @Override
    public int getVillagerXp() {
        return 0;
    }
    
    @Override
    protected float getStandingEyeHeight(final Pose apj, final EntityDimensions aon) {
        if (this.isBaby()) {
            return 0.81f;
        }
        return 1.62f;
    }
    
    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.<Integer>define(AbstractVillager.DATA_UNHAPPY_COUNTER, 0);
    }
    
    @Override
    public void setTradingPlayer(@Nullable final Player bec) {
        this.tradingPlayer = bec;
    }
    
    @Nullable
    @Override
    public Player getTradingPlayer() {
        return this.tradingPlayer;
    }
    
    public boolean isTrading() {
        return this.tradingPlayer != null;
    }
    
    @Override
    public MerchantOffers getOffers() {
        if (this.offers == null) {
            this.offers = new MerchantOffers();
            this.updateTrades();
        }
        return this.offers;
    }
    
    @Override
    public void overrideOffers(@Nullable final MerchantOffers bpa) {
    }
    
    @Override
    public void overrideXp(final int integer) {
    }
    
    @Override
    public void notifyTrade(final MerchantOffer boz) {
        boz.increaseUses();
        this.ambientSoundTime = -this.getAmbientSoundInterval();
        this.rewardTradeXp(boz);
        if (this.tradingPlayer instanceof ServerPlayer) {
            CriteriaTriggers.TRADE.trigger((ServerPlayer)this.tradingPlayer, this, boz.getResult());
        }
    }
    
    protected abstract void rewardTradeXp(final MerchantOffer boz);
    
    @Override
    public boolean showProgressBar() {
        return true;
    }
    
    @Override
    public void notifyTradeUpdated(final ItemStack bki) {
        if (!this.level.isClientSide && this.ambientSoundTime > -this.getAmbientSoundInterval() + 20) {
            this.ambientSoundTime = -this.getAmbientSoundInterval();
            this.playSound(this.getTradeUpdatedSound(!bki.isEmpty()), this.getSoundVolume(), this.getVoicePitch());
        }
    }
    
    @Override
    public SoundEvent getNotifyTradeSound() {
        return SoundEvents.VILLAGER_YES;
    }
    
    protected SoundEvent getTradeUpdatedSound(final boolean boolean1) {
        return boolean1 ? SoundEvents.VILLAGER_YES : SoundEvents.VILLAGER_NO;
    }
    
    public void playCelebrateSound() {
        this.playSound(SoundEvents.VILLAGER_CELEBRATE, this.getSoundVolume(), this.getVoicePitch());
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag le) {
        super.addAdditionalSaveData(le);
        final MerchantOffers bpa3 = this.getOffers();
        if (!bpa3.isEmpty()) {
            le.put("Offers", bpa3.createTag());
        }
        le.put("Inventory", this.inventory.createTag());
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag le) {
        super.readAdditionalSaveData(le);
        if (le.contains("Offers", 10)) {
            this.offers = new MerchantOffers(le.getCompound("Offers"));
        }
        this.inventory.fromTag(le.getList("Inventory", 10));
    }
    
    @Nullable
    @Override
    public Entity changeDimension(final ServerLevel zd) {
        this.stopTrading();
        return super.changeDimension(zd);
    }
    
    protected void stopTrading() {
        this.setTradingPlayer(null);
    }
    
    @Override
    public void die(final DamageSource anw) {
        super.die(anw);
        this.stopTrading();
    }
    
    protected void addParticlesAroundSelf(final ParticleOptions hf) {
        for (int integer3 = 0; integer3 < 5; ++integer3) {
            final double double4 = this.random.nextGaussian() * 0.02;
            final double double5 = this.random.nextGaussian() * 0.02;
            final double double6 = this.random.nextGaussian() * 0.02;
            this.level.addParticle(hf, this.getRandomX(1.0), this.getRandomY() + 1.0, this.getRandomZ(1.0), double4, double5, double6);
        }
    }
    
    @Override
    public boolean canBeLeashed(final Player bec) {
        return false;
    }
    
    public SimpleContainer getInventory() {
        return this.inventory;
    }
    
    @Override
    public boolean setSlot(final int integer, final ItemStack bki) {
        if (super.setSlot(integer, bki)) {
            return true;
        }
        final int integer2 = integer - 300;
        if (integer2 >= 0 && integer2 < this.inventory.getContainerSize()) {
            this.inventory.setItem(integer2, bki);
            return true;
        }
        return false;
    }
    
    @Override
    public Level getLevel() {
        return this.level;
    }
    
    protected abstract void updateTrades();
    
    protected void addOffersFromItemListings(final MerchantOffers bpa, final VillagerTrades.ItemListing[] arr, final int integer) {
        final Set<Integer> set5 = Sets.newHashSet();
        if (arr.length > integer) {
            while (set5.size() < integer) {
                set5.add(this.random.nextInt(arr.length));
            }
        }
        else {
            for (int integer2 = 0; integer2 < arr.length; ++integer2) {
                set5.add(integer2);
            }
        }
        for (final Integer integer3 : set5) {
            final VillagerTrades.ItemListing f8 = arr[integer3];
            final MerchantOffer boz9 = f8.getOffer(this, this.random);
            if (boz9 != null) {
                bpa.add(boz9);
            }
        }
    }
    
    static {
        DATA_UNHAPPY_COUNTER = SynchedEntityData.<Integer>defineId(AbstractVillager.class, EntityDataSerializers.INT);
    }
}
