package net.minecraft.world.entity.ai.village.poi;

import org.apache.logging.log4j.LogManager;
import com.mojang.datafixers.kinds.Applicative;
import java.util.function.BiConsumer;
import java.util.Optional;
import com.google.common.collect.Sets;
import net.minecraft.core.SectionPos;
import org.apache.logging.log4j.util.Supplier;
import net.minecraft.core.BlockPos;
import java.util.stream.Stream;
import java.util.function.Predicate;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import java.util.function.Consumer;
import java.util.List;
import net.minecraft.Util;
import com.mojang.datafixers.kinds.App;
import java.util.Collection;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.mojang.serialization.Codec;
import java.util.Set;
import java.util.Map;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
import org.apache.logging.log4j.Logger;

public class PoiSection {
    private static final Logger LOGGER;
    private final Short2ObjectMap<PoiRecord> records;
    private final Map<PoiType, Set<PoiRecord>> byType;
    private final Runnable setDirty;
    private boolean isValid;
    
    public static Codec<PoiSection> codec(final Runnable runnable) {
        return (Codec<PoiSection>)RecordCodecBuilder.create(instance -> ((RecordCodecBuilder.Instance)instance).group((App)RecordCodecBuilder.point(runnable), (App)Codec.BOOL.optionalFieldOf("Valid", false).forGetter(ayb -> ayb.isValid), (App)PoiRecord.codec(runnable).listOf().fieldOf("Records").forGetter(ayb -> ImmutableList.copyOf(ayb.records.values()))).apply(instance, PoiSection::new)).withDefault((Consumer)Util.prefix("Failed to read POI section: ", PoiSection.LOGGER::error), () -> new PoiSection(runnable, false, ImmutableList.of()));
    }
    
    public PoiSection(final Runnable runnable) {
        this(runnable, true, ImmutableList.of());
    }
    
    private PoiSection(final Runnable runnable, final boolean boolean2, final List<PoiRecord> list) {
        this.records = (Short2ObjectMap<PoiRecord>)new Short2ObjectOpenHashMap();
        this.byType = Maps.newHashMap();
        this.setDirty = runnable;
        this.isValid = boolean2;
        list.forEach(this::add);
    }
    
    public Stream<PoiRecord> getRecords(final Predicate<PoiType> predicate, final PoiManager.Occupancy b) {
        return this.byType.entrySet().stream().filter(entry -> predicate.test(entry.getKey())).<PoiRecord>flatMap(entry -> entry.getValue().stream()).filter(b.getTest());
    }
    
    public void add(final BlockPos fu, final PoiType ayc) {
        if (this.add(new PoiRecord(fu, ayc, this.setDirty))) {
            PoiSection.LOGGER.debug("Added POI of type {} @ {}", new Supplier[] { () -> ayc, () -> fu });
            this.setDirty.run();
        }
    }
    
    private boolean add(final PoiRecord aya) {
        final BlockPos fu3 = aya.getPos();
        final PoiType ayc4 = aya.getPoiType();
        final short short5 = SectionPos.sectionRelativePos(fu3);
        final PoiRecord aya2 = (PoiRecord)this.records.get(short5);
        if (aya2 == null) {
            this.records.put(short5, aya);
            this.byType.computeIfAbsent(ayc4, ayc -> Sets.newHashSet()).add(aya);
            return true;
        }
        if (ayc4.equals(aya2.getPoiType())) {
            return false;
        }
        throw Util.<IllegalStateException>pauseInIde(new IllegalStateException("POI data mismatch: already registered at " + fu3));
    }
    
    public void remove(final BlockPos fu) {
        final PoiRecord aya3 = (PoiRecord)this.records.remove(SectionPos.sectionRelativePos(fu));
        if (aya3 == null) {
            PoiSection.LOGGER.error("POI data mismatch: never registered at " + fu);
            return;
        }
        this.byType.get(aya3.getPoiType()).remove(aya3);
        PoiSection.LOGGER.debug("Removed POI of type {} @ {}", new Supplier[] { aya3::getPoiType, aya3::getPos });
        this.setDirty.run();
    }
    
    public boolean release(final BlockPos fu) {
        final PoiRecord aya3 = (PoiRecord)this.records.get(SectionPos.sectionRelativePos(fu));
        if (aya3 == null) {
            throw Util.<IllegalStateException>pauseInIde(new IllegalStateException("POI never registered at " + fu));
        }
        final boolean boolean4 = aya3.releaseTicket();
        this.setDirty.run();
        return boolean4;
    }
    
    public boolean exists(final BlockPos fu, final Predicate<PoiType> predicate) {
        final short short4 = SectionPos.sectionRelativePos(fu);
        final PoiRecord aya5 = (PoiRecord)this.records.get(short4);
        return aya5 != null && predicate.test(aya5.getPoiType());
    }
    
    public Optional<PoiType> getType(final BlockPos fu) {
        final short short3 = SectionPos.sectionRelativePos(fu);
        final PoiRecord aya4 = (PoiRecord)this.records.get(short3);
        return (aya4 != null) ? Optional.<PoiType>of(aya4.getPoiType()) : Optional.<PoiType>empty();
    }
    
    public void refresh(final Consumer<BiConsumer<BlockPos, PoiType>> consumer) {
        if (!this.isValid) {
            final Short2ObjectMap<PoiRecord> short2ObjectMap3 = (Short2ObjectMap<PoiRecord>)new Short2ObjectOpenHashMap((Short2ObjectMap)this.records);
            this.clear();
            final short short5;
            final Short2ObjectMap short2ObjectMap4;
            final PoiRecord aya6;
            consumer.accept((fu, ayc) -> {
                short5 = SectionPos.sectionRelativePos(fu);
                aya6 = (PoiRecord)short2ObjectMap4.computeIfAbsent(short5, integer -> new PoiRecord(fu, ayc, this.setDirty));
                this.add(aya6);
                return;
            });
            this.isValid = true;
            this.setDirty.run();
        }
    }
    
    private void clear() {
        this.records.clear();
        this.byType.clear();
    }
    
    boolean isValid() {
        return this.isValid;
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
