// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.spawning.suppression;

import java.util.function.Supplier;
import javax.annotation.Nonnull;
import java.util.Iterator;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.spawning.suppression.component.ChunkSuppressionEntry;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.List;
import java.util.ArrayDeque;

public class SuppressionSpanHelper
{
    private static final ThreadLocal<ArrayDeque<Span>> SPAN_POOL;
    private final List<Span> optimisedSuppressionSpans;
    private int currentSpanIndex;
    
    public SuppressionSpanHelper() {
        this.optimisedSuppressionSpans = new ObjectArrayList<Span>();
        this.currentSpanIndex = 0;
    }
    
    public void optimiseSuppressedSpans(final int roleIndex, @Nullable final ChunkSuppressionEntry entry) {
        if (entry == null) {
            return;
        }
        final ArrayDeque<Span> spanPool = SuppressionSpanHelper.SPAN_POOL.get();
        final List<ChunkSuppressionEntry.SuppressionSpan> suppressionSpans = entry.getSuppressionSpans();
        final Span initialSpan = allocateSpan(spanPool);
        initialSpan.init(0, 0);
        this.optimisedSuppressionSpans.add(initialSpan);
        boolean matchedRole = false;
        for (final ChunkSuppressionEntry.SuppressionSpan suppressionSpan : suppressionSpans) {
            if (!suppressionSpan.includesRole(roleIndex)) {
                continue;
            }
            matchedRole = true;
            final int minY = suppressionSpan.getMinY();
            final int maxY = suppressionSpan.getMaxY();
            final Span latestSpan = this.optimisedSuppressionSpans.getLast();
            if (latestSpan.includes(minY)) {
                if (latestSpan.includes(maxY)) {
                    continue;
                }
                latestSpan.expandTo(maxY);
            }
            else {
                final Span span = allocateSpan(spanPool);
                span.init(minY, maxY);
                this.optimisedSuppressionSpans.add(span);
            }
        }
        if (!matchedRole) {
            final Span span2 = this.optimisedSuppressionSpans.removeFirst();
            span2.reset();
            spanPool.push(span2);
        }
    }
    
    public int adjustSpawnRangeMin(final int min) {
        if (this.optimisedSuppressionSpans.isEmpty()) {
            return min;
        }
        int maxSpanIndex;
        Span currentSpan;
        for (maxSpanIndex = this.optimisedSuppressionSpans.size() - 1, currentSpan = this.optimisedSuppressionSpans.get(this.currentSpanIndex); min >= currentSpan.max && this.currentSpanIndex < maxSpanIndex; currentSpan = this.optimisedSuppressionSpans.get(this.currentSpanIndex)) {
            ++this.currentSpanIndex;
        }
        if (currentSpan.includes(min)) {
            if (this.currentSpanIndex < maxSpanIndex) {
                ++this.currentSpanIndex;
            }
            return currentSpan.max;
        }
        return min;
    }
    
    public int adjustSpawnRangeMax(final int min, final int max) {
        if (this.optimisedSuppressionSpans.isEmpty()) {
            return max;
        }
        final Span currentSpan = this.optimisedSuppressionSpans.get(this.currentSpanIndex);
        if (max < currentSpan.min) {
            return max;
        }
        if (currentSpan.includes(max)) {
            return currentSpan.min;
        }
        if (min < currentSpan.min && max >= currentSpan.max) {
            return currentSpan.min;
        }
        return max;
    }
    
    public void reset() {
        final ArrayDeque<Span> spanPool = SuppressionSpanHelper.SPAN_POOL.get();
        for (int i = this.optimisedSuppressionSpans.size() - 1; i >= 0; --i) {
            final Span span = this.optimisedSuppressionSpans.remove(i);
            span.reset();
            spanPool.push(span);
        }
        this.currentSpanIndex = 0;
    }
    
    @Nonnull
    private static Span allocateSpan(@Nonnull final ArrayDeque<Span> spanPool) {
        if (spanPool.isEmpty()) {
            return new Span();
        }
        return spanPool.pop();
    }
    
    static {
        SPAN_POOL = ThreadLocal.withInitial((Supplier<? extends ArrayDeque<Span>>)ArrayDeque::new);
    }
    
    private static class Span
    {
        private int min;
        private int max;
        
        private Span() {
            this.min = -1;
            this.max = -1;
        }
        
        public void init(final int min, final int max) {
            this.min = min;
            this.max = max;
        }
        
        public void expandTo(final int max) {
            this.max = max;
        }
        
        public boolean includes(final int value) {
            return value >= this.min && value <= this.max;
        }
        
        public void reset() {
            final int n = -1;
            this.max = n;
            this.min = n;
        }
    }
}
