package net.minecraft.tags;

import org.apache.logging.log4j.LogManager;
import java.io.InputStream;
import java.io.Closeable;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import net.minecraft.Util;
import net.minecraft.util.GsonHelper;
import com.google.gson.JsonObject;
import java.io.Reader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.io.BufferedReader;
import net.minecraft.server.packs.resources.Resource;
import com.google.common.collect.Maps;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import net.minecraft.server.packs.resources.ResourceManager;
import java.util.Iterator;
import java.util.List;
import com.google.common.collect.Lists;
import java.util.Collection;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import java.util.Optional;
import java.util.function.Function;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;
import com.google.gson.Gson;
import org.apache.logging.log4j.Logger;

public class TagCollection<T> {
    private static final Logger LOGGER;
    private static final Gson GSON;
    private static final int PATH_SUFFIX_LENGTH;
    private Map<ResourceLocation, Tag<T>> tags;
    private final Function<ResourceLocation, Optional<T>> idToValue;
    private final String directory;
    private final boolean ordered;
    private final String name;
    
    public TagCollection(final Function<ResourceLocation, Optional<T>> function, final String string2, final boolean boolean3, final String string4) {
        this.tags = ImmutableMap.of();
        this.idToValue = function;
        this.directory = string2;
        this.ordered = boolean3;
        this.name = string4;
    }
    
    @Nullable
    public Tag<T> getTag(final ResourceLocation sm) {
        return this.tags.get(sm);
    }
    
    public Tag<T> getTagOrEmpty(final ResourceLocation sm) {
        final Tag<T> aaz3 = this.tags.get(sm);
        if (aaz3 == null) {
            return new Tag<T>(sm);
        }
        return aaz3;
    }
    
    public Collection<ResourceLocation> getAvailableTags() {
        return this.tags.keySet();
    }
    
    public Collection<ResourceLocation> getMatchingTags(final T object) {
        final List<ResourceLocation> list3 = Lists.newArrayList();
        for (final Map.Entry<ResourceLocation, Tag<T>> entry5 : this.tags.entrySet()) {
            if (entry5.getValue().contains(object)) {
                list3.add(entry5.getKey());
            }
        }
        return list3;
    }
    
    public CompletableFuture<Map<ResourceLocation, Tag.Builder<T>>> prepare(final ResourceManager zb, final Executor executor) {
        final Map<ResourceLocation, Tag.Builder<T>> map3;
        final Iterator<ResourceLocation> iterator;
        ResourceLocation sm5;
        String string2;
        ResourceLocation sm6;
        final Iterator<Resource> iterator2;
        Resource za9;
        InputStream inputStream10;
        final BufferedReader bufferedReader;
        Reader reader12;
        JsonObject jsonObject14;
        final Throwable t2;
        final Throwable t4;
        return CompletableFuture.<Map<ResourceLocation, Tag.Builder<T>>>supplyAsync(() -> {
            map3 = Maps.newHashMap();
            zb.listResources(this.directory, string -> string.endsWith(".json")).iterator();
            while (iterator.hasNext()) {
                sm5 = iterator.next();
                string2 = sm5.getPath();
                sm6 = new ResourceLocation(sm5.getNamespace(), string2.substring(this.directory.length() + 1, string2.length() - TagCollection.PATH_SUFFIX_LENGTH));
                try {
                    zb.getResources(sm5).iterator();
                    while (iterator2.hasNext()) {
                        za9 = iterator2.next();
                        try {
                            inputStream10 = za9.getInputStream();
                            try {
                                new BufferedReader(new InputStreamReader(inputStream10, StandardCharsets.UTF_8));
                                reader12 = bufferedReader;
                                try {
                                    jsonObject14 = GsonHelper.<JsonObject>fromJson(TagCollection.GSON, reader12, JsonObject.class);
                                    if (jsonObject14 == null) {
                                        TagCollection.LOGGER.error("Couldn't load {} tag list {} from {} in data pack {} as it's empty or null", this.name, sm6, sm5, za9.getSourceName());
                                    }
                                    else {
                                        map3.computeIfAbsent(sm6, sm -> Util.<Tag.Builder<Object>>make(Tag.Builder.tag(), a -> a.keepOrder(this.ordered))).addFromJson(this.idToValue, jsonObject14);
                                    }
                                }
                                catch (Throwable t) {
                                    throw t;
                                }
                                finally {
                                    if (reader12 != null) {
                                        if (t2 != null) {
                                            try {
                                                reader12.close();
                                            }
                                            catch (Throwable exception11) {
                                                t2.addSuppressed(exception11);
                                            }
                                        }
                                        else {
                                            reader12.close();
                                        }
                                    }
                                }
                            }
                            catch (Throwable t3) {
                                throw t3;
                            }
                            finally {
                                if (inputStream10 != null) {
                                    if (t4 != null) {
                                        try {
                                            inputStream10.close();
                                        }
                                        catch (Throwable exception12) {
                                            t4.addSuppressed(exception12);
                                        }
                                    }
                                    else {
                                        inputStream10.close();
                                    }
                                }
                            }
                        }
                        catch (IOException ex) {}
                        catch (RuntimeException exception10) {
                            TagCollection.LOGGER.error("Couldn't read {} tag list {} from {} in data pack {}", this.name, sm6, sm5, za9.getSourceName(), exception10);
                        }
                        finally {
                            IOUtils.closeQuietly((Closeable)za9);
                        }
                    }
                }
                catch (IOException iOException8) {
                    TagCollection.LOGGER.error("Couldn't read {} tag list {} from {}", this.name, sm6, sm5, iOException8);
                }
            }
            return map3;
        }, executor);
    }
    
    public void load(final Map<ResourceLocation, Tag.Builder<T>> map) {
        final Map<ResourceLocation, Tag<T>> map2 = Maps.newHashMap();
        while (!map.isEmpty()) {
            boolean boolean4 = false;
            final Iterator<Map.Entry<ResourceLocation, Tag.Builder<T>>> iterator5 = map.entrySet().iterator();
            while (iterator5.hasNext()) {
                final Map.Entry<ResourceLocation, Tag.Builder<T>> entry6 = iterator5.next();
                final Tag.Builder<T> a2 = entry6.getValue();
                if (a2.canBuild(map2::get)) {
                    boolean4 = true;
                    final ResourceLocation sm2 = entry6.getKey();
                    map2.put(sm2, a2.build(sm2));
                    iterator5.remove();
                }
            }
            if (!boolean4) {
                map.forEach((sm, a) -> TagCollection.LOGGER.error("Couldn't load {} tag {} as it either references another tag that doesn't exist, or ultimately references itself", this.name, sm));
                break;
            }
        }
        final Tag<T> tag;
        map.forEach((sm, a) -> tag = map2.put(sm, a.build(sm)));
        this.replace(map2);
    }
    
    protected void replace(final Map<ResourceLocation, Tag<T>> map) {
        this.tags = ImmutableMap.copyOf(map);
    }
    
    public Map<ResourceLocation, Tag<T>> getAllTags() {
        return this.tags;
    }
    
    static {
        LOGGER = LogManager.getLogger();
        GSON = new Gson();
        PATH_SUFFIX_LENGTH = ".json".length();
    }
}
