package net.minecraft.world.level;

import java.util.AbstractList;
import org.apache.logging.log4j.LogManager;
import java.util.function.Function;
import net.minecraft.nbt.Tag;
import net.minecraft.util.WeighedRandom;
import java.util.Iterator;
import net.minecraft.nbt.ListTag;
import java.util.Optional;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.SpawnGroupData;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.Registry;
import net.minecraft.world.entity.EntityType;
import net.minecraft.core.BlockPos;
import net.minecraft.ResourceLocationException;
import net.minecraft.util.StringUtil;
import net.minecraft.resources.ResourceLocation;
import com.google.common.collect.Lists;
import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
import java.util.List;
import org.apache.logging.log4j.Logger;

public abstract class BaseSpawner {
    private static final Logger LOGGER;
    private int spawnDelay;
    private final List<SpawnData> spawnPotentials;
    private SpawnData nextSpawnData;
    private double spin;
    private double oSpin;
    private int minSpawnDelay;
    private int maxSpawnDelay;
    private int spawnCount;
    @Nullable
    private Entity displayEntity;
    private int maxNearbyEntities;
    private int requiredPlayerRange;
    private int spawnRange;
    
    public BaseSpawner() {
        this.spawnDelay = 20;
        this.spawnPotentials = Lists.newArrayList();
        this.nextSpawnData = new SpawnData();
        this.minSpawnDelay = 200;
        this.maxSpawnDelay = 800;
        this.spawnCount = 4;
        this.maxNearbyEntities = 6;
        this.requiredPlayerRange = 16;
        this.spawnRange = 4;
    }
    
    @Nullable
    private ResourceLocation getEntityId() {
        final String string2 = this.nextSpawnData.getTag().getString("id");
        try {
            return StringUtil.isNullOrEmpty(string2) ? null : new ResourceLocation(string2);
        }
        catch (ResourceLocationException t3) {
            final BlockPos fu4 = this.getPos();
            BaseSpawner.LOGGER.warn("Invalid entity id '{}' at spawner {}:[{},{},{}]", string2, this.getLevel().dimension().location(), fu4.getX(), fu4.getY(), fu4.getZ());
            return null;
        }
    }
    
    public void setEntityId(final EntityType<?> aoq) {
        this.nextSpawnData.getTag().putString("id", Registry.ENTITY_TYPE.getKey(aoq).toString());
    }
    
    private boolean isNearPlayer() {
        final BlockPos fu2 = this.getPos();
        return this.getLevel().hasNearbyAlivePlayer(fu2.getX() + 0.5, fu2.getY() + 0.5, fu2.getZ() + 0.5, this.requiredPlayerRange);
    }
    
    public void tick() {
        if (!this.isNearPlayer()) {
            this.oSpin = this.spin;
            return;
        }
        final Level bqb2 = this.getLevel();
        final BlockPos fu3 = this.getPos();
        if (bqb2.isClientSide) {
            final double double4 = fu3.getX() + bqb2.random.nextDouble();
            final double double5 = fu3.getY() + bqb2.random.nextDouble();
            final double double6 = fu3.getZ() + bqb2.random.nextDouble();
            bqb2.addParticle(ParticleTypes.SMOKE, double4, double5, double6, 0.0, 0.0, 0.0);
            bqb2.addParticle(ParticleTypes.FLAME, double4, double5, double6, 0.0, 0.0, 0.0);
            if (this.spawnDelay > 0) {
                --this.spawnDelay;
            }
            this.oSpin = this.spin;
            this.spin = (this.spin + 1000.0f / (this.spawnDelay + 200.0f)) % 360.0;
        }
        else {
            if (this.spawnDelay == -1) {
                this.delay();
            }
            if (this.spawnDelay > 0) {
                --this.spawnDelay;
                return;
            }
            boolean boolean4 = false;
            for (int integer5 = 0; integer5 < this.spawnCount; ++integer5) {
                final CompoundTag le6 = this.nextSpawnData.getTag();
                final Optional<EntityType<?>> optional7 = EntityType.by(le6);
                if (!optional7.isPresent()) {
                    this.delay();
                    return;
                }
                final ListTag lk8 = le6.getList("Pos", 6);
                final int integer6 = lk8.size();
                final double double7 = (integer6 >= 1) ? lk8.getDouble(0) : (fu3.getX() + (bqb2.random.nextDouble() - bqb2.random.nextDouble()) * this.spawnRange + 0.5);
                final double double8 = (integer6 >= 2) ? lk8.getDouble(1) : (fu3.getY() + bqb2.random.nextInt(3) - 1);
                final double double9 = (integer6 >= 3) ? lk8.getDouble(2) : (fu3.getZ() + (bqb2.random.nextDouble() - bqb2.random.nextDouble()) * this.spawnRange + 0.5);
                if (bqb2.noCollision(optional7.get().getAABB(double7, double8, double9))) {
                    if (SpawnPlacements.<Entity>checkSpawnRules(optional7.get(), bqb2.getLevel(), MobSpawnType.SPAWNER, new BlockPos(double7, double8, double9), bqb2.getRandom())) {
                        final Entity aom2 = EntityType.loadEntityRecursive(le6, bqb2, aom -> {
                            aom.moveTo(double7, double8, double9, aom.yRot, aom.xRot);
                            return aom;
                        });
                        if (aom2 == null) {
                            this.delay();
                            return;
                        }
                        final int integer7 = bqb2.<Entity>getEntitiesOfClass(aom2.getClass(), new AABB(fu3.getX(), fu3.getY(), fu3.getZ(), fu3.getX() + 1, fu3.getY() + 1, fu3.getZ() + 1).inflate(this.spawnRange)).size();
                        if (integer7 >= this.maxNearbyEntities) {
                            this.delay();
                            return;
                        }
                        aom2.moveTo(aom2.getX(), aom2.getY(), aom2.getZ(), bqb2.random.nextFloat() * 360.0f, 0.0f);
                        if (aom2 instanceof Mob) {
                            final Mob aoz18 = (Mob)aom2;
                            if (!aoz18.checkSpawnRules(bqb2, MobSpawnType.SPAWNER)) {
                                continue;
                            }
                            if (!aoz18.checkSpawnObstruction(bqb2)) {
                                continue;
                            }
                            if (this.nextSpawnData.getTag().size() == 1 && this.nextSpawnData.getTag().contains("id", 8)) {
                                ((Mob)aom2).finalizeSpawn(bqb2, bqb2.getCurrentDifficultyAt(aom2.blockPosition()), MobSpawnType.SPAWNER, null, null);
                            }
                        }
                        this.addWithPassengers(aom2);
                        bqb2.levelEvent(2004, fu3, 0);
                        if (aom2 instanceof Mob) {
                            ((Mob)aom2).spawnAnim();
                        }
                        boolean4 = true;
                    }
                }
            }
            if (boolean4) {
                this.delay();
            }
        }
    }
    
    private void addWithPassengers(final Entity aom) {
        if (!this.getLevel().addFreshEntity(aom)) {
            return;
        }
        for (final Entity aom2 : aom.getPassengers()) {
            this.addWithPassengers(aom2);
        }
    }
    
    private void delay() {
        if (this.maxSpawnDelay <= this.minSpawnDelay) {
            this.spawnDelay = this.minSpawnDelay;
        }
        else {
            this.spawnDelay = this.minSpawnDelay + this.getLevel().random.nextInt(this.maxSpawnDelay - this.minSpawnDelay);
        }
        if (!this.spawnPotentials.isEmpty()) {
            this.setNextSpawnData(WeighedRandom.<SpawnData>getRandomItem(this.getLevel().random, this.spawnPotentials));
        }
        this.broadcastEvent(1);
    }
    
    public void load(final CompoundTag le) {
        this.spawnDelay = le.getShort("Delay");
        this.spawnPotentials.clear();
        if (le.contains("SpawnPotentials", 9)) {
            final ListTag lk3 = le.getList("SpawnPotentials", 10);
            for (int integer4 = 0; integer4 < lk3.size(); ++integer4) {
                this.spawnPotentials.add(new SpawnData(lk3.getCompound(integer4)));
            }
        }
        if (le.contains("SpawnData", 10)) {
            this.setNextSpawnData(new SpawnData(1, le.getCompound("SpawnData")));
        }
        else if (!this.spawnPotentials.isEmpty()) {
            this.setNextSpawnData(WeighedRandom.<SpawnData>getRandomItem(this.getLevel().random, this.spawnPotentials));
        }
        if (le.contains("MinSpawnDelay", 99)) {
            this.minSpawnDelay = le.getShort("MinSpawnDelay");
            this.maxSpawnDelay = le.getShort("MaxSpawnDelay");
            this.spawnCount = le.getShort("SpawnCount");
        }
        if (le.contains("MaxNearbyEntities", 99)) {
            this.maxNearbyEntities = le.getShort("MaxNearbyEntities");
            this.requiredPlayerRange = le.getShort("RequiredPlayerRange");
        }
        if (le.contains("SpawnRange", 99)) {
            this.spawnRange = le.getShort("SpawnRange");
        }
        if (this.getLevel() != null) {
            this.displayEntity = null;
        }
    }
    
    public CompoundTag save(final CompoundTag le) {
        final ResourceLocation uh3 = this.getEntityId();
        if (uh3 == null) {
            return le;
        }
        le.putShort("Delay", (short)this.spawnDelay);
        le.putShort("MinSpawnDelay", (short)this.minSpawnDelay);
        le.putShort("MaxSpawnDelay", (short)this.maxSpawnDelay);
        le.putShort("SpawnCount", (short)this.spawnCount);
        le.putShort("MaxNearbyEntities", (short)this.maxNearbyEntities);
        le.putShort("RequiredPlayerRange", (short)this.requiredPlayerRange);
        le.putShort("SpawnRange", (short)this.spawnRange);
        le.put("SpawnData", this.nextSpawnData.getTag().copy());
        final ListTag lk4 = new ListTag();
        if (this.spawnPotentials.isEmpty()) {
            ((AbstractList<CompoundTag>)lk4).add(this.nextSpawnData.save());
        }
        else {
            for (final SpawnData bqp6 : this.spawnPotentials) {
                ((AbstractList<CompoundTag>)lk4).add(bqp6.save());
            }
        }
        le.put("SpawnPotentials", lk4);
        return le;
    }
    
    @Nullable
    public Entity getOrCreateDisplayEntity() {
        if (this.displayEntity == null) {
            this.displayEntity = EntityType.loadEntityRecursive(this.nextSpawnData.getTag(), this.getLevel(), Function.<Entity>identity());
            if (this.nextSpawnData.getTag().size() == 1 && this.nextSpawnData.getTag().contains("id", 8) && this.displayEntity instanceof Mob) {
                ((Mob)this.displayEntity).finalizeSpawn(this.getLevel(), this.getLevel().getCurrentDifficultyAt(this.displayEntity.blockPosition()), MobSpawnType.SPAWNER, null, null);
            }
        }
        return this.displayEntity;
    }
    
    public boolean onEventTriggered(final int integer) {
        if (integer == 1 && this.getLevel().isClientSide) {
            this.spawnDelay = this.minSpawnDelay;
            return true;
        }
        return false;
    }
    
    public void setNextSpawnData(final SpawnData bqp) {
        this.nextSpawnData = bqp;
    }
    
    public abstract void broadcastEvent(final int integer);
    
    public abstract Level getLevel();
    
    public abstract BlockPos getPos();
    
    public double getSpin() {
        return this.spin;
    }
    
    public double getoSpin() {
        return this.oSpin;
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
