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 r3) {
            final BlockPos fk4 = this.getPos();
            BaseSpawner.LOGGER.warn("Invalid entity id '{}' at spawner {}:[{},{},{}]", string2, this.getLevel().dimension.getType(), fk4.getX(), fk4.getY(), fk4.getZ());
            return null;
        }
    }
    
    public void setEntityId(final EntityType<?> aku) {
        this.nextSpawnData.getTag().putString("id", Registry.ENTITY_TYPE.getKey(aku).toString());
    }
    
    private boolean isNearPlayer() {
        final BlockPos fk2 = this.getPos();
        return this.getLevel().hasNearbyAlivePlayer(fk2.getX() + 0.5, fk2.getY() + 0.5, fk2.getZ() + 0.5, this.requiredPlayerRange);
    }
    
    public void tick() {
        if (!this.isNearPlayer()) {
            this.oSpin = this.spin;
            return;
        }
        final Level bjw2 = this.getLevel();
        final BlockPos fk3 = this.getPos();
        if (bjw2.isClientSide) {
            final double double4 = fk3.getX() + (double)bjw2.random.nextFloat();
            final double double5 = fk3.getY() + (double)bjw2.random.nextFloat();
            final double double6 = fk3.getZ() + (double)bjw2.random.nextFloat();
            bjw2.addParticle(ParticleTypes.SMOKE, double4, double5, double6, 0.0, 0.0, 0.0);
            bjw2.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 jt6 = this.nextSpawnData.getTag();
                final Optional<EntityType<?>> optional7 = EntityType.by(jt6);
                if (!optional7.isPresent()) {
                    this.delay();
                    return;
                }
                final ListTag jz8 = jt6.getList("Pos", 6);
                final int integer6 = jz8.size();
                final double double7 = (integer6 >= 1) ? jz8.getDouble(0) : (fk3.getX() + (bjw2.random.nextDouble() - bjw2.random.nextDouble()) * this.spawnRange + 0.5);
                final double double8 = (integer6 >= 2) ? jz8.getDouble(1) : (fk3.getY() + bjw2.random.nextInt(3) - 1);
                final double double9 = (integer6 >= 3) ? jz8.getDouble(2) : (fk3.getZ() + (bjw2.random.nextDouble() - bjw2.random.nextDouble()) * this.spawnRange + 0.5);
                if (bjw2.noCollision(optional7.get().getAABB(double7, double8, double9))) {
                    if (SpawnPlacements.<Entity>checkSpawnRules(optional7.get(), bjw2.getLevel(), MobSpawnType.SPAWNER, new BlockPos(double7, double8, double9), bjw2.getRandom())) {
                        final Entity akq2 = EntityType.loadEntityRecursive(jt6, bjw2, akq -> {
                            akq.moveTo(double7, double8, double9, akq.yRot, akq.xRot);
                            return akq;
                        });
                        if (akq2 == null) {
                            this.delay();
                            return;
                        }
                        final int integer7 = bjw2.<Entity>getEntitiesOfClass(akq2.getClass(), new AABB(fk3.getX(), fk3.getY(), fk3.getZ(), fk3.getX() + 1, fk3.getY() + 1, fk3.getZ() + 1).inflate(this.spawnRange)).size();
                        if (integer7 >= this.maxNearbyEntities) {
                            this.delay();
                            return;
                        }
                        akq2.moveTo(akq2.getX(), akq2.getY(), akq2.getZ(), bjw2.random.nextFloat() * 360.0f, 0.0f);
                        if (akq2 instanceof Mob) {
                            final Mob ala18 = (Mob)akq2;
                            if (!ala18.checkSpawnRules(bjw2, MobSpawnType.SPAWNER)) {
                                continue;
                            }
                            if (!ala18.checkSpawnObstruction(bjw2)) {
                                continue;
                            }
                            if (this.nextSpawnData.getTag().size() == 1 && this.nextSpawnData.getTag().contains("id", 8)) {
                                ((Mob)akq2).finalizeSpawn(bjw2, bjw2.getCurrentDifficultyAt(new BlockPos(akq2)), MobSpawnType.SPAWNER, null, null);
                            }
                        }
                        this.addWithPassengers(akq2);
                        bjw2.levelEvent(2004, fk3, 0);
                        if (akq2 instanceof Mob) {
                            ((Mob)akq2).spawnAnim();
                        }
                        boolean4 = true;
                    }
                }
            }
            if (boolean4) {
                this.delay();
            }
        }
    }
    
    private void addWithPassengers(final Entity akq) {
        if (!this.getLevel().addFreshEntity(akq)) {
            return;
        }
        for (final Entity akq2 : akq.getPassengers()) {
            this.addWithPassengers(akq2);
        }
    }
    
    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 jt) {
        this.spawnDelay = jt.getShort("Delay");
        this.spawnPotentials.clear();
        if (jt.contains("SpawnPotentials", 9)) {
            final ListTag jz3 = jt.getList("SpawnPotentials", 10);
            for (int integer4 = 0; integer4 < jz3.size(); ++integer4) {
                this.spawnPotentials.add(new SpawnData(jz3.getCompound(integer4)));
            }
        }
        if (jt.contains("SpawnData", 10)) {
            this.setNextSpawnData(new SpawnData(1, jt.getCompound("SpawnData")));
        }
        else if (!this.spawnPotentials.isEmpty()) {
            this.setNextSpawnData(WeighedRandom.<SpawnData>getRandomItem(this.getLevel().random, this.spawnPotentials));
        }
        if (jt.contains("MinSpawnDelay", 99)) {
            this.minSpawnDelay = jt.getShort("MinSpawnDelay");
            this.maxSpawnDelay = jt.getShort("MaxSpawnDelay");
            this.spawnCount = jt.getShort("SpawnCount");
        }
        if (jt.contains("MaxNearbyEntities", 99)) {
            this.maxNearbyEntities = jt.getShort("MaxNearbyEntities");
            this.requiredPlayerRange = jt.getShort("RequiredPlayerRange");
        }
        if (jt.contains("SpawnRange", 99)) {
            this.spawnRange = jt.getShort("SpawnRange");
        }
        if (this.getLevel() != null) {
            this.displayEntity = null;
        }
    }
    
    public CompoundTag save(final CompoundTag jt) {
        final ResourceLocation sm3 = this.getEntityId();
        if (sm3 == null) {
            return jt;
        }
        jt.putShort("Delay", (short)this.spawnDelay);
        jt.putShort("MinSpawnDelay", (short)this.minSpawnDelay);
        jt.putShort("MaxSpawnDelay", (short)this.maxSpawnDelay);
        jt.putShort("SpawnCount", (short)this.spawnCount);
        jt.putShort("MaxNearbyEntities", (short)this.maxNearbyEntities);
        jt.putShort("RequiredPlayerRange", (short)this.requiredPlayerRange);
        jt.putShort("SpawnRange", (short)this.spawnRange);
        jt.put("SpawnData", this.nextSpawnData.getTag().copy());
        final ListTag jz4 = new ListTag();
        if (this.spawnPotentials.isEmpty()) {
            ((AbstractList<CompoundTag>)jz4).add(this.nextSpawnData.save());
        }
        else {
            for (final SpawnData bkk6 : this.spawnPotentials) {
                ((AbstractList<CompoundTag>)jz4).add(bkk6.save());
            }
        }
        jt.put("SpawnPotentials", jz4);
        return jt;
    }
    
    @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(new BlockPos(this.displayEntity)), 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 bkk) {
        this.nextSpawnData = bkk;
    }
    
    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();
    }
}
