package net.minecraft.world.level.levelgen.feature;

import org.apache.logging.log4j.LogManager;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.chunk.FeatureAccess;
import net.minecraft.world.level.chunk.ChunkAccess;
import com.google.common.collect.Lists;
import javax.annotation.Nullable;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.Level;
import java.util.Iterator;
import java.util.List;
import net.minecraft.world.level.levelgen.structure.StructurePiece;
import net.minecraft.core.Vec3i;
import it.unimi.dsi.fastutil.longs.LongIterator;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.core.BlockPos;
import java.util.Random;
import net.minecraft.world.level.levelgen.ChunkGeneratorSettings;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.LevelAccessor;
import com.mojang.datafixers.Dynamic;
import java.util.function.Function;
import org.apache.logging.log4j.Logger;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;

public abstract class StructureFeature<C extends FeatureConfiguration> extends Feature<C> {
    private static final Logger LOGGER;
    
    public StructureFeature(final Function<Dynamic<?>, ? extends C> function) {
        super(function);
    }
    
    @Override
    public ConfiguredFeature<C, ? extends StructureFeature<C>> configured(final C cgn) {
        return new ConfiguredFeature<C, StructureFeature<C>>((StructureFeature<C>)this, cgn);
    }
    
    @Override
    public boolean place(final LevelAccessor bjx, final ChunkGenerator<? extends ChunkGeneratorSettings> caa, final Random random, final BlockPos fk, final C cgn) {
        if (!bjx.getLevelData().isGenerateMapFeatures()) {
            return false;
        }
        final int integer7 = fk.getX() >> 4;
        final int integer8 = fk.getZ() >> 4;
        final int integer9 = integer7 << 4;
        final int integer10 = integer8 << 4;
        boolean boolean11 = false;
        for (final Long long13 : bjx.getChunk(integer7, integer8).getReferencesForFeature(this.getFeatureName())) {
            final ChunkPos bjh14 = new ChunkPos(long13);
            final StructureStart clv15 = bjx.getChunk(bjh14.x, bjh14.z).getStartForFeature(this.getFeatureName());
            if (clv15 != null && clv15 != StructureStart.INVALID_START) {
                clv15.postProcess(bjx, caa, random, new BoundingBox(integer9, integer10, integer9 + 15, integer10 + 15), new ChunkPos(integer7, integer8));
                boolean11 = true;
            }
        }
        return boolean11;
    }
    
    protected StructureStart getStructureAt(final LevelAccessor bjx, final BlockPos fk, final boolean boolean3) {
        final List<StructureStart> list5 = this.dereferenceStructureStarts(bjx, fk.getX() >> 4, fk.getZ() >> 4);
        for (final StructureStart clv7 : list5) {
            if (clv7.isValid() && clv7.getBoundingBox().isInside(fk)) {
                if (!boolean3) {
                    return clv7;
                }
                for (final StructurePiece clu9 : clv7.getPieces()) {
                    if (clu9.getBoundingBox().isInside(fk)) {
                        return clv7;
                    }
                }
            }
        }
        return StructureStart.INVALID_START;
    }
    
    public boolean isInsideBoundingFeature(final LevelAccessor bjx, final BlockPos fk) {
        return this.getStructureAt(bjx, fk, false).isValid();
    }
    
    public boolean isInsideFeature(final LevelAccessor bjx, final BlockPos fk) {
        return this.getStructureAt(bjx, fk, true).isValid();
    }
    
    @Nullable
    public BlockPos getNearestGeneratedFeature(final Level bjw, final ChunkGenerator<? extends ChunkGeneratorSettings> caa, final BlockPos fk, final int integer, final boolean boolean5) {
        if (!caa.getBiomeSource().canGenerateStructure(this)) {
            return null;
        }
        final int integer2 = fk.getX() >> 4;
        final int integer3 = fk.getZ() >> 4;
        int integer4 = 0;
        final WorldgenRandom ccf10 = new WorldgenRandom();
        while (integer4 <= integer) {
            for (int integer5 = -integer4; integer5 <= integer4; ++integer5) {
                final boolean boolean6 = integer5 == -integer4 || integer5 == integer4;
                for (int integer6 = -integer4; integer6 <= integer4; ++integer6) {
                    final boolean boolean7 = integer6 == -integer4 || integer6 == integer4;
                    if (boolean6 || boolean7) {
                        final ChunkPos bjh15 = this.getPotentialFeatureChunkFromLocationWithOffset(caa, ccf10, integer2, integer3, integer5, integer6);
                        final StructureStart clv16 = bjw.getChunk(bjh15.x, bjh15.z, ChunkStatus.STRUCTURE_STARTS).getStartForFeature(this.getFeatureName());
                        if (clv16 != null && clv16.isValid()) {
                            if (boolean5 && clv16.canBeReferenced()) {
                                clv16.addReference();
                                return clv16.getLocatePos();
                            }
                            if (!boolean5) {
                                return clv16.getLocatePos();
                            }
                        }
                        if (integer4 == 0) {
                            break;
                        }
                    }
                }
                if (integer4 == 0) {
                    break;
                }
            }
            ++integer4;
        }
        return null;
    }
    
    private List<StructureStart> dereferenceStructureStarts(final LevelAccessor bjx, final int integer2, final int integer3) {
        final List<StructureStart> list5 = Lists.newArrayList();
        final ChunkAccess bzy6 = bjx.getChunk(integer2, integer3, ChunkStatus.STRUCTURE_REFERENCES);
        final LongIterator longIterator7 = bzy6.getReferencesForFeature(this.getFeatureName()).iterator();
        while (longIterator7.hasNext()) {
            final long long8 = longIterator7.nextLong();
            final FeatureAccess cah10 = bjx.getChunk(ChunkPos.getX(long8), ChunkPos.getZ(long8), ChunkStatus.STRUCTURE_STARTS);
            final StructureStart clv11 = cah10.getStartForFeature(this.getFeatureName());
            if (clv11 != null) {
                list5.add(clv11);
            }
        }
        return list5;
    }
    
    protected ChunkPos getPotentialFeatureChunkFromLocationWithOffset(final ChunkGenerator<?> caa, final Random random, final int integer3, final int integer4, final int integer5, final int integer6) {
        return new ChunkPos(integer3 + integer5, integer4 + integer6);
    }
    
    public abstract boolean isFeatureChunk(final BiomeManager bkv, final ChunkGenerator<?> caa, final Random random, final int integer4, final int integer5, final Biome bkt);
    
    public abstract StructureStartFactory getStartFactory();
    
    public abstract String getFeatureName();
    
    public abstract int getLookupRange();
    
    static {
        LOGGER = LogManager.getLogger();
    }
    
    public interface StructureStartFactory {
        StructureStart create(final StructureFeature<?> cfl, final int integer2, final int integer3, final BoundingBox clb, final int integer5, final long long6);
    }
}
