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

import com.google.common.collect.ImmutableList;
import net.minecraft.world.level.levelgen.structure.NetherFossilFeature;
import net.minecraft.world.level.levelgen.structure.OceanRuinFeature;
import org.apache.logging.log4j.LogManager;
import com.google.common.collect.Maps;
import com.google.common.collect.HashBiMap;
import java.util.Collections;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.FeatureAccess;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.levelgen.feature.configurations.StructureFeatureConfiguration;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.LevelReader;
import javax.annotation.Nullable;
import net.minecraft.world.level.levelgen.structure.StructurePiece;
import net.minecraft.nbt.ListTag;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import net.minecraft.core.Registry;
import java.util.Locale;
import com.mojang.serialization.Codec;
import java.util.List;
import net.minecraft.world.level.levelgen.feature.configurations.MultiJigsawConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.JigsawConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.BuriedTreasureConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.OceanRuinConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.ShipwreckConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.RuinedPortalConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.MineshaftConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
import org.apache.logging.log4j.Logger;
import net.minecraft.world.level.levelgen.GenerationStep;
import java.util.Map;
import com.google.common.collect.BiMap;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;

public abstract class StructureFeature<C extends FeatureConfiguration> {
    public static final BiMap<String, StructureFeature<?>> STRUCTURES_REGISTRY;
    private static final Map<StructureFeature<?>, GenerationStep.Decoration> STEP;
    private static final Logger LOGGER;
    public static final StructureFeature<NoneFeatureConfiguration> PILLAGER_OUTPOST;
    public static final StructureFeature<MineshaftConfiguration> MINESHAFT;
    public static final StructureFeature<NoneFeatureConfiguration> WOODLAND_MANSION;
    public static final StructureFeature<NoneFeatureConfiguration> JUNGLE_TEMPLE;
    public static final StructureFeature<NoneFeatureConfiguration> DESERT_PYRAMID;
    public static final StructureFeature<NoneFeatureConfiguration> IGLOO;
    public static final StructureFeature<RuinedPortalConfiguration> RUINED_PORTAL;
    public static final StructureFeature<ShipwreckConfiguration> SHIPWRECK;
    public static final SwamplandHutFeature SWAMP_HUT;
    public static final StructureFeature<NoneFeatureConfiguration> STRONGHOLD;
    public static final StructureFeature<NoneFeatureConfiguration> OCEAN_MONUMENT;
    public static final StructureFeature<OceanRuinConfiguration> OCEAN_RUIN;
    public static final StructureFeature<NoneFeatureConfiguration> NETHER_BRIDGE;
    public static final StructureFeature<NoneFeatureConfiguration> END_CITY;
    public static final StructureFeature<BuriedTreasureConfiguration> BURIED_TREASURE;
    public static final StructureFeature<JigsawConfiguration> VILLAGE;
    public static final StructureFeature<NoneFeatureConfiguration> NETHER_FOSSIL;
    public static final StructureFeature<MultiJigsawConfiguration> BASTION_REMNANT;
    public static final List<StructureFeature<?>> NOISE_AFFECTING_FEATURES;
    private final Codec<ConfiguredStructureFeature<C, StructureFeature<C>>> configuredStructureCodec;
    
    private static <F extends StructureFeature<?>> F register(final String string, final F cml, final GenerationStep.Decoration b) {
        StructureFeature.STRUCTURES_REGISTRY.put(string.toLowerCase(Locale.ROOT), cml);
        StructureFeature.STEP.put(cml, b);
        return Registry.<F>register(Registry.STRUCTURE_FEATURE, string.toLowerCase(Locale.ROOT), cml);
    }
    
    public StructureFeature(final Codec<C> codec) {
        this.configuredStructureCodec = (Codec<ConfiguredStructureFeature<C, StructureFeature<C>>>)codec.fieldOf("config").xmap(cnr -> new ConfiguredStructureFeature(this, cnr), ckc -> ckc.config).codec();
    }
    
    public GenerationStep.Decoration step() {
        return StructureFeature.STEP.get(this);
    }
    
    public static void bootstrap() {
    }
    
    @Nullable
    public static StructureStart<?> loadStaticStart(final StructureManager cva, final CompoundTag le, final long long3) {
        final String string5 = le.getString("id");
        if ("INVALID".equals(string5)) {
            return StructureStart.INVALID_START;
        }
        final StructureFeature<?> cml6 = Registry.STRUCTURE_FEATURE.get(new ResourceLocation(string5.toLowerCase(Locale.ROOT)));
        if (cml6 == null) {
            StructureFeature.LOGGER.error("Unknown feature id: {}", string5);
            return null;
        }
        final int integer7 = le.getInt("ChunkX");
        final int integer8 = le.getInt("ChunkZ");
        final int integer9 = le.getInt("references");
        final BoundingBox ctd10 = le.contains("BB") ? new BoundingBox(le.getIntArray("BB")) : BoundingBox.getUnknownBox();
        final ListTag lk11 = le.getList("Children", 10);
        try {
            final StructureStart<?> ctz12 = cml6.createStart(integer7, integer8, ctd10, integer9, long3);
            for (int integer10 = 0; integer10 < lk11.size(); ++integer10) {
                final CompoundTag le2 = lk11.getCompound(integer10);
                final String string6 = le2.getString("id");
                final StructurePieceType cmm16 = Registry.STRUCTURE_PIECE.get(new ResourceLocation(string6.toLowerCase(Locale.ROOT)));
                if (cmm16 == null) {
                    StructureFeature.LOGGER.error("Unknown structure piece id: {}", string6);
                }
                else {
                    try {
                        final StructurePiece cty17 = cmm16.load(cva, le2);
                        ctz12.getPieces().add(cty17);
                    }
                    catch (Exception exception17) {
                        StructureFeature.LOGGER.error("Exception loading structure piece with id {}", string6, exception17);
                    }
                }
            }
            return ctz12;
        }
        catch (Exception exception18) {
            StructureFeature.LOGGER.error("Failed Start with id {}", string5, exception18);
            return null;
        }
    }
    
    public Codec<ConfiguredStructureFeature<C, StructureFeature<C>>> configuredStructureCodec() {
        return this.configuredStructureCodec;
    }
    
    public ConfiguredStructureFeature<C, ? extends StructureFeature<C>> configured(final C cnr) {
        return new ConfiguredStructureFeature<C, StructureFeature<C>>((StructureFeature<C>)this, cnr);
    }
    
    @Nullable
    public BlockPos getNearestGeneratedFeature(final LevelReader bqd, final StructureFeatureManager bqq, final BlockPos fu, final int integer, final boolean boolean5, final long long6, final StructureFeatureConfiguration cot) {
        final int integer2 = cot.spacing();
        final int integer3 = fu.getX() >> 4;
        final int integer4 = fu.getZ() >> 4;
        int integer5 = 0;
        final WorldgenRandom ciy14 = new WorldgenRandom();
        while (integer5 <= integer) {
            for (int integer6 = -integer5; integer6 <= integer5; ++integer6) {
                final boolean boolean6 = integer6 == -integer5 || integer6 == integer5;
                for (int integer7 = -integer5; integer7 <= integer5; ++integer7) {
                    final boolean boolean7 = integer7 == -integer5 || integer7 == integer5;
                    if (boolean6 || boolean7) {
                        final int integer8 = integer3 + integer2 * integer6;
                        final int integer9 = integer4 + integer2 * integer7;
                        final ChunkPos bph21 = this.getPotentialFeatureChunk(cot, long6, ciy14, integer8, integer9);
                        final ChunkAccess cgy22 = bqd.getChunk(bph21.x, bph21.z, ChunkStatus.STRUCTURE_STARTS);
                        final StructureStart<?> ctz23 = bqq.getStartForFeature(SectionPos.of(cgy22.getPos(), 0), this, cgy22);
                        if (ctz23 != null && ctz23.isValid()) {
                            if (boolean5 && ctz23.canBeReferenced()) {
                                ctz23.addReference();
                                return ctz23.getLocatePos();
                            }
                            if (!boolean5) {
                                return ctz23.getLocatePos();
                            }
                        }
                        if (integer5 == 0) {
                            break;
                        }
                    }
                }
                if (integer5 == 0) {
                    break;
                }
            }
            ++integer5;
        }
        return null;
    }
    
    protected boolean linearSeparation() {
        return true;
    }
    
    public final ChunkPos getPotentialFeatureChunk(final StructureFeatureConfiguration cot, final long long2, final WorldgenRandom ciy, final int integer4, final int integer5) {
        final int integer6 = cot.spacing();
        final int integer7 = cot.separation();
        final int integer8 = Math.floorDiv(integer4, integer6);
        final int integer9 = Math.floorDiv(integer5, integer6);
        ciy.setLargeFeatureWithSalt(long2, integer8, integer9, cot.salt());
        int integer10;
        int integer11;
        if (this.linearSeparation()) {
            integer10 = ciy.nextInt(integer6 - integer7);
            integer11 = ciy.nextInt(integer6 - integer7);
        }
        else {
            integer10 = (ciy.nextInt(integer6 - integer7) + ciy.nextInt(integer6 - integer7)) / 2;
            integer11 = (ciy.nextInt(integer6 - integer7) + ciy.nextInt(integer6 - integer7)) / 2;
        }
        return new ChunkPos(integer8 * integer6 + integer10, integer9 * integer6 + integer11);
    }
    
    protected boolean isFeatureChunk(final ChunkGenerator cha, final BiomeSource brh, final long long3, final WorldgenRandom ciy, final int integer5, final int integer6, final Biome bre, final ChunkPos bph, final C cnr) {
        return true;
    }
    
    private StructureStart<C> createStart(final int integer1, final int integer2, final BoundingBox ctd, final int integer4, final long long5) {
        return this.getStartFactory().create(this, integer1, integer2, ctd, integer4, long5);
    }
    
    public StructureStart<?> generate(final ChunkGenerator cha, final BiomeSource brh, final StructureManager cva, final long long4, final ChunkPos bph, final Biome bre, final int integer, final WorldgenRandom ciy, final StructureFeatureConfiguration cot, final C cnr) {
        final ChunkPos bph2 = this.getPotentialFeatureChunk(cot, long4, ciy, bph.x, bph.z);
        if (bph.x == bph2.x && bph.z == bph2.z && this.isFeatureChunk(cha, brh, long4, ciy, bph.x, bph.z, bre, bph2, cnr)) {
            final StructureStart<C> ctz14 = this.createStart(bph.x, bph.z, BoundingBox.getUnknownBox(), integer, long4);
            ctz14.generatePieces(cha, cva, bph.x, bph.z, bre, cnr);
            if (ctz14.isValid()) {
                return ctz14;
            }
        }
        return StructureStart.INVALID_START;
    }
    
    public abstract StructureStartFactory<C> getStartFactory();
    
    public String getFeatureName() {
        return StructureFeature.STRUCTURES_REGISTRY.inverse().get(this);
    }
    
    public List<Biome.SpawnerData> getSpecialEnemies() {
        return Collections.<Biome.SpawnerData>emptyList();
    }
    
    public List<Biome.SpawnerData> getSpecialAnimals() {
        return Collections.<Biome.SpawnerData>emptyList();
    }
    
    static {
        STRUCTURES_REGISTRY = HashBiMap.create();
        STEP = Maps.newHashMap();
        LOGGER = LogManager.getLogger();
        PILLAGER_OUTPOST = StructureFeature.<PillagerOutpostFeature>register("Pillager_Outpost", new PillagerOutpostFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        MINESHAFT = StructureFeature.<MineshaftFeature>register("Mineshaft", new MineshaftFeature(MineshaftConfiguration.CODEC), GenerationStep.Decoration.UNDERGROUND_STRUCTURES);
        WOODLAND_MANSION = StructureFeature.<WoodlandMansionFeature>register("Mansion", new WoodlandMansionFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        JUNGLE_TEMPLE = StructureFeature.<JunglePyramidFeature>register("Jungle_Pyramid", new JunglePyramidFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        DESERT_PYRAMID = StructureFeature.<DesertPyramidFeature>register("Desert_Pyramid", new DesertPyramidFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        IGLOO = StructureFeature.<IglooFeature>register("Igloo", new IglooFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        RUINED_PORTAL = StructureFeature.<RuinedPortalFeature>register("Ruined_Portal", new RuinedPortalFeature(RuinedPortalConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        SHIPWRECK = StructureFeature.<ShipwreckFeature>register("Shipwreck", new ShipwreckFeature(ShipwreckConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        SWAMP_HUT = StructureFeature.<SwamplandHutFeature>register("Swamp_Hut", new SwamplandHutFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        STRONGHOLD = StructureFeature.<StrongholdFeature>register("Stronghold", new StrongholdFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.STRONGHOLDS);
        OCEAN_MONUMENT = StructureFeature.<OceanMonumentFeature>register("Monument", new OceanMonumentFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        OCEAN_RUIN = StructureFeature.<OceanRuinFeature>register("Ocean_Ruin", new OceanRuinFeature(OceanRuinConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        NETHER_BRIDGE = StructureFeature.<NetherFortressFeature>register("Fortress", new NetherFortressFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.UNDERGROUND_DECORATION);
        END_CITY = StructureFeature.<EndCityFeature>register("EndCity", new EndCityFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        BURIED_TREASURE = StructureFeature.<BuriedTreasureFeature>register("Buried_Treasure", new BuriedTreasureFeature(BuriedTreasureConfiguration.CODEC), GenerationStep.Decoration.UNDERGROUND_STRUCTURES);
        VILLAGE = StructureFeature.<VillageFeature>register("Village", new VillageFeature(JigsawConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        NETHER_FOSSIL = StructureFeature.<NetherFossilFeature>register("Nether_Fossil", new NetherFossilFeature(NoneFeatureConfiguration.CODEC), GenerationStep.Decoration.UNDERGROUND_DECORATION);
        BASTION_REMNANT = StructureFeature.<BastionFeature>register("Bastion_Remnant", new BastionFeature(MultiJigsawConfiguration.CODEC), GenerationStep.Decoration.SURFACE_STRUCTURES);
        NOISE_AFFECTING_FEATURES = ImmutableList.<StructureFeature<NoneFeatureConfiguration>>of(StructureFeature.PILLAGER_OUTPOST, (StructureFeature<NoneFeatureConfiguration>)StructureFeature.VILLAGE, StructureFeature.NETHER_FOSSIL);
    }
    
    public interface StructureStartFactory<C extends FeatureConfiguration> {
        StructureStart<C> create(final StructureFeature<C> cml, final int integer2, final int integer3, final BoundingBox ctd, final int integer5, final long long6);
    }
}
