package net.minecraft.world.level.timers;

import org.apache.logging.log4j.LogManager;
import java.util.function.Function;
import java.util.Iterator;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.CompoundTag;
import java.util.Collections;
import java.util.Set;
import java.util.Collection;
import java.util.function.Consumer;
import com.google.common.collect.HashBasedTable;
import java.util.PriorityQueue;
import java.util.Comparator;
import com.google.common.collect.Table;
import com.google.common.primitives.UnsignedLong;
import java.util.Queue;
import org.apache.logging.log4j.Logger;

public class TimerQueue<T> {
    private static final Logger LOGGER;
    private final TimerCallbacks<T> callbacksRegistry;
    private final Queue<Event<T>> queue;
    private UnsignedLong sequentialId;
    private final Table<String, Long, Event<T>> events;
    
    private static <T> Comparator<Event<T>> createComparator() {
        return Comparator.<Event<T>>comparingLong(a -> a.triggerTime).<Comparable>thenComparing(a -> a.sequentialId);
    }
    
    public TimerQueue(final TimerCallbacks<T> cvb) {
        this.queue = new PriorityQueue<Event<T>>(TimerQueue.createComparator());
        this.sequentialId = UnsignedLong.ZERO;
        this.events = HashBasedTable.create();
        this.callbacksRegistry = cvb;
    }
    
    public void tick(final T object, final long long2) {
        while (true) {
            final Event<T> a5 = this.queue.peek();
            if (a5 == null || a5.triggerTime > long2) {
                break;
            }
            this.queue.remove();
            this.events.remove(a5.id, long2);
            a5.callback.handle(object, this, long2);
        }
    }
    
    public void schedule(final String string, final long long2, final TimerCallback<T> cva) {
        if (this.events.contains(string, long2)) {
            return;
        }
        this.sequentialId = this.sequentialId.plus(UnsignedLong.ONE);
        final Event<T> a6 = new Event<T>(long2, this.sequentialId, string, (TimerCallback)cva);
        this.events.put(string, long2, a6);
        this.queue.add(a6);
    }
    
    public int remove(final String string) {
        final Collection<Event<T>> collection3 = this.events.row(string).values();
        collection3.forEach(this.queue::remove);
        final int integer4 = collection3.size();
        collection3.clear();
        return integer4;
    }
    
    public Set<String> getEventsIds() {
        return Collections.<String>unmodifiableSet(this.events.rowKeySet());
    }
    
    private void loadEvent(final CompoundTag jt) {
        final CompoundTag jt2 = jt.getCompound("Callback");
        final TimerCallback<T> cva4 = this.callbacksRegistry.deserialize(jt2);
        if (cva4 != null) {
            final String string5 = jt.getString("Name");
            final long long6 = jt.getLong("TriggerTime");
            this.schedule(string5, long6, cva4);
        }
    }
    
    public void load(final ListTag jz) {
        this.queue.clear();
        this.events.clear();
        this.sequentialId = UnsignedLong.ZERO;
        if (jz.isEmpty()) {
            return;
        }
        if (jz.getElementType() != 10) {
            TimerQueue.LOGGER.warn("Invalid format of events: " + jz);
            return;
        }
        for (final Tag kj4 : jz) {
            this.loadEvent((CompoundTag)kj4);
        }
    }
    
    private CompoundTag storeEvent(final Event<T> a) {
        final CompoundTag jt3 = new CompoundTag();
        jt3.putString("Name", a.id);
        jt3.putLong("TriggerTime", a.triggerTime);
        jt3.put("Callback", this.callbacksRegistry.<TimerCallback<T>>serialize(a.callback));
        return jt3;
    }
    
    public ListTag store() {
        final ListTag jz2 = new ListTag();
        this.queue.stream().sorted(TimerQueue.createComparator()).map(this::storeEvent).forEach(jz2::add);
        return jz2;
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
    
    public static class Event<T> {
        public final long triggerTime;
        public final UnsignedLong sequentialId;
        public final String id;
        public final TimerCallback<T> callback;
        
        private Event(final long long1, final UnsignedLong unsignedLong, final String string, final TimerCallback<T> cva) {
            this.triggerTime = long1;
            this.sequentialId = unsignedLong;
            this.id = string;
            this.callback = cva;
        }
    }
}
