package net.minecraft.server;

import org.apache.logging.log4j.LogManager;
import net.minecraft.server.packs.resources.Resource;
import java.io.IOException;
import java.util.concurrent.CompletionException;
import org.apache.commons.io.IOUtils;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Iterator;
import java.util.function.Function;
import java.util.function.BiFunction;
import java.util.concurrent.CompletionStage;
import com.mojang.datafixers.util.Pair;
import net.minecraft.world.entity.Entity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import net.minecraft.commands.CommandSource;
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.server.packs.resources.ResourceManager;
import com.google.common.collect.ImmutableMap;
import net.minecraft.tags.Tag;
import java.util.Optional;
import net.minecraft.commands.CommandSourceStack;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.tags.TagCollection;
import net.minecraft.commands.CommandFunction;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;
import org.apache.logging.log4j.Logger;
import net.minecraft.server.packs.resources.PreparableReloadListener;

public class ServerFunctionLibrary implements PreparableReloadListener {
    private static final Logger LOGGER;
    private static final int PATH_PREFIX_LENGTH;
    private static final int PATH_SUFFIX_LENGTH;
    private volatile Map<ResourceLocation, CommandFunction> functions;
    private final TagCollection<CommandFunction> tags;
    private final int functionCompilationLevel;
    private final CommandDispatcher<CommandSourceStack> dispatcher;
    
    public Optional<CommandFunction> getFunction(final ResourceLocation uh) {
        return Optional.<CommandFunction>ofNullable(this.functions.get(uh));
    }
    
    public Map<ResourceLocation, CommandFunction> getFunctions() {
        return this.functions;
    }
    
    public TagCollection<CommandFunction> getTags() {
        return this.tags;
    }
    
    public Tag<CommandFunction> getTag(final ResourceLocation uh) {
        return this.tags.getTagOrEmpty(uh);
    }
    
    public ServerFunctionLibrary(final int integer, final CommandDispatcher<CommandSourceStack> commandDispatcher) {
        this.functions = ImmutableMap.of();
        this.tags = new TagCollection<CommandFunction>(this::getFunction, "tags/functions", "function");
        this.functionCompilationLevel = integer;
        this.dispatcher = commandDispatcher;
    }
    
    @Override
    public CompletableFuture<Void> reload(final PreparationBarrier a, final ResourceManager abc, final ProfilerFiller ami3, final ProfilerFiller ami4, final Executor executor5, final Executor executor6) {
        final CompletableFuture<Map<ResourceLocation, Tag.Builder>> completableFuture2 = this.tags.prepare(abc, executor5);
        final Map<ResourceLocation, CompletableFuture<CommandFunction>> map5;
        final CommandSourceStack cz6;
        final Iterator<ResourceLocation> iterator;
        ResourceLocation uh2;
        String string2;
        ResourceLocation uh3;
        final ResourceLocation uh4;
        List<String> list6;
        final ResourceLocation uh5;
        final CommandSourceStack cz7;
        final CompletableFuture<?>[] arr7;
        final CompletableFuture<Map<ResourceLocation, CompletableFuture<CommandFunction>>> completableFuture3 = CompletableFuture.<Collection>supplyAsync(() -> abc.listResources("functions", string -> string.endsWith(".mcfunction")), executor5).<Map<ResourceLocation, CompletableFuture<CommandFunction>>>thenCompose(collection -> {
            map5 = Maps.newHashMap();
            cz6 = new CommandSourceStack(CommandSource.NULL, Vec3.ZERO, Vec2.ZERO, null, this.functionCompilationLevel, "", TextComponent.EMPTY, null, null);
            collection.iterator();
            while (iterator.hasNext()) {
                uh2 = iterator.next();
                string2 = uh2.getPath();
                uh3 = new ResourceLocation(uh2.getNamespace(), string2.substring(ServerFunctionLibrary.PATH_PREFIX_LENGTH, string2.length() - ServerFunctionLibrary.PATH_SUFFIX_LENGTH));
                map5.put(uh3, CompletableFuture.<CommandFunction>supplyAsync(() -> {
                    list6 = readLines(abc, uh4);
                    return CommandFunction.fromLines(uh5, this.dispatcher, cz7, list6);
                }, executor5));
            }
            arr7 = map5.values().<CompletableFuture<?>>toArray(new CompletableFuture[0]);
            return CompletableFuture.allOf(arr7).handle((void2, throwable) -> map5);
        });
        final Map<ResourceLocation, CompletableFuture<CommandFunction>> map6;
        final ImmutableMap.Builder<ResourceLocation, CommandFunction> builder4;
        final ImmutableMap.Builder<ResourceLocation, CommandFunction> builder5;
        return completableFuture2.thenCombine(completableFuture3, Pair::of).thenCompose(a::wait).thenAcceptAsync(pair -> {
            map6 = (Map<ResourceLocation, CompletableFuture<CommandFunction>>)pair.getSecond();
            builder4 = ImmutableMap.<ResourceLocation, CommandFunction>builder();
            map6.forEach((uh, completableFuture) -> completableFuture.handle((cw, throwable) -> {
                if (throwable != null) {
                    ServerFunctionLibrary.LOGGER.error("Failed to load function {}", uh, throwable);
                }
                else {
                    builder5.put(uh, cw);
                }
                return null;
            }).join());
            this.functions = builder4.build();
            this.tags.load((Map<ResourceLocation, Tag.Builder>)pair.getFirst());
        }, executor6);
    }
    
    private static List<String> readLines(final ResourceManager abc, final ResourceLocation uh) {
        try (final Resource abb3 = abc.getResource(uh)) {
            return (List<String>)IOUtils.readLines(abb3.getInputStream(), StandardCharsets.UTF_8);
        }
        catch (IOException iOException3) {
            throw new CompletionException(iOException3);
        }
    }
    
    static {
        LOGGER = LogManager.getLogger();
        PATH_PREFIX_LENGTH = "functions/".length();
        PATH_SUFFIX_LENGTH = ".mcfunction".length();
    }
}
