package net.minecraft.world.entity.ai.behavior;

import java.util.Random;
import net.minecraft.world.entity.LivingEntity;
import java.util.Iterator;
import net.minecraft.world.level.pathfinder.Path;
import java.util.function.Predicate;
import net.minecraft.network.protocol.game.DebugPackets;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.core.BlockPos;
import java.util.Set;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.ai.memory.MemoryStatus;
import com.google.common.collect.ImmutableMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.Map;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minecraft.core.GlobalPos;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.ai.village.poi.PoiType;
import net.minecraft.world.entity.PathfinderMob;

public class AcquirePoi extends Behavior<PathfinderMob> {
    private final PoiType poiType;
    private final MemoryModuleType<GlobalPos> memoryToAcquire;
    private final boolean onlyIfAdult;
    private long nextScheduledStart;
    private final Long2ObjectMap<JitteredLinearRetry> batchCache;
    
    public AcquirePoi(final PoiType ayc, final MemoryModuleType<GlobalPos> awp2, final MemoryModuleType<GlobalPos> awp3, final boolean boolean4) {
        super(constructEntryConditionMap(awp2, awp3));
        this.batchCache = (Long2ObjectMap<JitteredLinearRetry>)new Long2ObjectOpenHashMap();
        this.poiType = ayc;
        this.memoryToAcquire = awp3;
        this.onlyIfAdult = boolean4;
    }
    
    public AcquirePoi(final PoiType ayc, final MemoryModuleType<GlobalPos> awp, final boolean boolean3) {
        this(ayc, awp, awp, boolean3);
    }
    
    private static ImmutableMap<MemoryModuleType<?>, MemoryStatus> constructEntryConditionMap(final MemoryModuleType<GlobalPos> awp1, final MemoryModuleType<GlobalPos> awp2) {
        final ImmutableMap.Builder<MemoryModuleType<?>, MemoryStatus> builder3 = ImmutableMap.<MemoryModuleType<?>, MemoryStatus>builder();
        builder3.put(awp1, MemoryStatus.VALUE_ABSENT);
        if (awp2 != awp1) {
            builder3.put(awp2, MemoryStatus.VALUE_ABSENT);
        }
        return builder3.build();
    }
    
    @Override
    protected boolean checkExtraStartConditions(final ServerLevel zd, final PathfinderMob apg) {
        if (this.onlyIfAdult && apg.isBaby()) {
            return false;
        }
        if (this.nextScheduledStart == 0L) {
            this.nextScheduledStart = apg.level.getGameTime() + zd.random.nextInt(20);
            return false;
        }
        return zd.getGameTime() >= this.nextScheduledStart;
    }
    
    @Override
    protected void start(final ServerLevel zd, final PathfinderMob apg, final long long3) {
        this.nextScheduledStart = long3 + 20L + zd.getRandom().nextInt(20);
        final PoiManager axz6 = zd.getPoiManager();
        this.batchCache.long2ObjectEntrySet().removeIf(entry -> !((JitteredLinearRetry)entry.getValue()).isStillValid(long3));
        final JitteredLinearRetry a5;
        final Predicate<BlockPos> predicate7 = fu -> {
            a5 = (JitteredLinearRetry)this.batchCache.get(fu.asLong());
            if (a5 == null) {
                return true;
            }
            else if (!a5.shouldRetry(long3)) {
                return false;
            }
            else {
                a5.markAttempt(long3);
                return true;
            }
        };
        final Set<BlockPos> set8 = axz6.findAll(this.poiType.getPredicate(), predicate7, apg.blockPosition(), 48, PoiManager.Occupancy.HAS_SPACE).limit(5L).collect(Collectors.toSet());
        final Path czf9 = apg.getNavigation().createPath(set8, this.poiType.getValidRange());
        if (czf9 != null && czf9.canReach()) {
            final BlockPos fu3 = czf9.getTarget();
            final PoiManager poiManager;
            final BlockPos blockPos;
            axz6.getType(fu3).ifPresent(ayc -> {
                poiManager.take(this.poiType.getPredicate(), fu2 -> fu2.equals(blockPos), blockPos, 1);
                apg.getBrain().<GlobalPos>setMemory(this.memoryToAcquire, GlobalPos.of(zd.dimension(), blockPos));
                this.batchCache.clear();
                DebugPackets.sendPoiTicketCountPacket(zd, blockPos);
            });
        }
        else {
            for (final BlockPos fu4 : set8) {
                this.batchCache.computeIfAbsent(fu4.asLong(), long3 -> new JitteredLinearRetry(apg.level.random, long3));
            }
        }
    }
    
    static class JitteredLinearRetry {
        private final Random random;
        private long previousAttemptTimestamp;
        private long nextScheduledAttemptTimestamp;
        private int currentDelay;
        
        JitteredLinearRetry(final Random random, final long long2) {
            this.random = random;
            this.markAttempt(long2);
        }
        
        public void markAttempt(final long long1) {
            this.previousAttemptTimestamp = long1;
            final int integer4 = this.currentDelay + this.random.nextInt(40) + 40;
            this.currentDelay = Math.min(integer4, 400);
            this.nextScheduledAttemptTimestamp = long1 + this.currentDelay;
        }
        
        public boolean isStillValid(final long long1) {
            return long1 - this.previousAttemptTimestamp < 400L;
        }
        
        public boolean shouldRetry(final long long1) {
            return long1 >= this.nextScheduledAttemptTimestamp;
        }
        
        @Override
        public String toString() {
            return "RetryMarker{, previousAttemptAt=" + this.previousAttemptTimestamp + ", nextScheduledAttemptAt=" + this.nextScheduledAttemptTimestamp + ", currentDelay=" + this.currentDelay + '}';
        }
    }
}
