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

package com.hypixel.hytale.server.core.asset.monitor;

import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import java.util.Set;
import java.util.Iterator;
import java.util.HashSet;
import java.util.Comparator;
import java.util.AbstractMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.logging.Level;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import java.util.concurrent.ScheduledFuture;
import java.nio.file.Path;
import com.hypixel.hytale.logger.HytaleLogger;

public class DirectoryHandlerChangeTask implements Runnable
{
    public static final HytaleLogger LOGGER;
    private static final long ACCUMULATION_DELAY_MILLIS = 1000L;
    private final AssetMonitor assetMonitor;
    private final Path parent;
    private final AssetMonitorHandler handler;
    @Nonnull
    private final ScheduledFuture<?> task;
    private final AtomicBoolean changed;
    private final Map<Path, PathEvent> paths;
    
    public DirectoryHandlerChangeTask(final AssetMonitor assetMonitor, final Path parent, final AssetMonitorHandler handler) {
        this.changed = new AtomicBoolean(true);
        this.paths = new Object2ObjectOpenHashMap<Path, PathEvent>();
        this.assetMonitor = assetMonitor;
        this.parent = parent;
        this.handler = handler;
        this.task = AssetMonitor.runTask(this, 1000L);
    }
    
    @Override
    public void run() {
        if (!this.changed.getAndSet(false)) {
            this.cancelSchedule();
            try {
                DirectoryHandlerChangeTask.LOGGER.at(Level.FINER).log("run: %s", this.paths);
                final ObjectArrayList<Map.Entry<Path, PathEvent>> entries = new ObjectArrayList<Map.Entry<Path, PathEvent>>(this.paths.size());
                for (final Map.Entry<Path, PathEvent> entry : this.paths.entrySet()) {
                    entries.add(new AbstractMap.SimpleEntry<Path, PathEvent>(entry.getKey(), entry.getValue()));
                }
                this.paths.clear();
                entries.sort(Comparator.comparingLong(value -> value.getValue().getTimestamp()));
                final Set<String> fileNames = new HashSet<String>();
                Map<Path, EventKind> eventPaths = new Object2ObjectOpenHashMap<Path, EventKind>();
                for (final Map.Entry<Path, PathEvent> entry2 : entries) {
                    if (!fileNames.add(entry2.getKey().getFileName().toString())) {
                        DirectoryHandlerChangeTask.LOGGER.at(Level.FINER).log("run handler.accept(%s)", eventPaths);
                        this.handler.accept(eventPaths);
                        eventPaths = new Object2ObjectOpenHashMap<Path, EventKind>();
                        fileNames.clear();
                    }
                    eventPaths.put(entry2.getKey(), entry2.getValue().getEventKind());
                }
                if (!eventPaths.isEmpty()) {
                    DirectoryHandlerChangeTask.LOGGER.at(Level.FINER).log("run handler.accept(%s)", eventPaths);
                    this.handler.accept(eventPaths);
                }
            }
            catch (final Exception e) {
                DirectoryHandlerChangeTask.LOGGER.at(Level.SEVERE).withCause(e).log("Failed to run: %s", this);
            }
        }
    }
    
    public AssetMonitor getAssetMonitor() {
        return this.assetMonitor;
    }
    
    public Path getParent() {
        return this.parent;
    }
    
    public AssetMonitorHandler getHandler() {
        return this.handler;
    }
    
    public void addPath(final Path path, final PathEvent pathEvent) {
        DirectoryHandlerChangeTask.LOGGER.at(Level.FINEST).log("addPath(%s, %s): %s", path, pathEvent, this);
        this.paths.put(path, pathEvent);
        this.changed.set(true);
    }
    
    public void removePath(final Path path) {
        DirectoryHandlerChangeTask.LOGGER.at(Level.FINEST).log("removePath(%s, %s): %s", path, this);
        this.paths.remove(path);
        if (this.paths.isEmpty()) {
            this.cancelSchedule();
        }
        else {
            this.changed.set(true);
        }
    }
    
    public void markChanged() {
        AssetMonitor.LOGGER.at(Level.FINEST).log("markChanged(): %s", this);
        this.changed.set(true);
    }
    
    public void cancelSchedule() {
        DirectoryHandlerChangeTask.LOGGER.at(Level.FINEST).log("cancelSchedule(): %s", this);
        this.assetMonitor.removeHookChangeTask(this);
        if (this.task != null && !this.task.isDone()) {
            this.task.cancel(false);
        }
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "DirectoryHandlerChangeTask{parent=" + String.valueOf(this.parent) + ", handler=" + String.valueOf(this.handler) + ", changed=" + String.valueOf(this.changed) + ", paths=" + String.valueOf(this.paths);
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
    }
}
