package net.minecraft.tags;

import org.apache.logging.log4j.LogManager;
import com.google.common.collect.ImmutableBiMap;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.Objects;
import java.io.InputStream;
import java.io.Closeable;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
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 java.util.Map;
import com.google.common.collect.Lists;
import java.util.Collection;
import javax.annotation.Nullable;
import com.google.common.collect.HashBiMap;
import java.util.Optional;
import java.util.function.Function;
import net.minecraft.resources.ResourceLocation;
import com.google.common.collect.BiMap;
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 final Tag<T> empty;
    private volatile BiMap<ResourceLocation, Tag<T>> tags;
    private final Function<ResourceLocation, Optional<T>> idToValue;
    private final String directory;
    private final String name;
    
    public TagCollection(final Function<ResourceLocation, Optional<T>> function, final String string2, final String string3) {
        this.empty = SetTag.empty();
        this.tags = HashBiMap.create();
        this.idToValue = function;
        this.directory = string2;
        this.name = string3;
    }
    
    @Nullable
    public Tag<T> getTag(final ResourceLocation uh) {
        return this.tags.get(uh);
    }
    
    public Tag<T> getTagOrEmpty(final ResourceLocation uh) {
        return this.tags.getOrDefault(uh, this.empty);
    }
    
    @Nullable
    public ResourceLocation getId(final Tag<T> adf) {
        if (adf instanceof Tag.Named) {
            return ((Tag.Named)adf).getName();
        }
        return this.tags.inverse().get(adf);
    }
    
    public ResourceLocation getIdOrThrow(final Tag<T> adf) {
        final ResourceLocation uh3 = this.getId(adf);
        if (uh3 == null) {
            throw new IllegalStateException("Unrecognized tag");
        }
        return uh3;
    }
    
    public Collection<ResourceLocation> getAvailableTags() {
        return (Collection<ResourceLocation>)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>> prepare(final ResourceManager abc, final Executor executor) {
        final Map<ResourceLocation, Tag.Builder> map3;
        final Iterator<ResourceLocation> iterator;
        ResourceLocation uh5;
        String string2;
        ResourceLocation uh6;
        final Iterator<Resource> iterator2;
        Resource abb9;
        InputStream inputStream10;
        final BufferedReader bufferedReader;
        Reader reader12;
        JsonObject jsonObject14;
        final Throwable t2;
        final Throwable t4;
        return CompletableFuture.<Map<ResourceLocation, Tag.Builder>>supplyAsync(() -> {
            map3 = Maps.newHashMap();
            abc.listResources(this.directory, string -> string.endsWith(".json")).iterator();
            while (iterator.hasNext()) {
                uh5 = iterator.next();
                string2 = uh5.getPath();
                uh6 = new ResourceLocation(uh5.getNamespace(), string2.substring(this.directory.length() + 1, string2.length() - TagCollection.PATH_SUFFIX_LENGTH));
                try {
                    abc.getResources(uh5).iterator();
                    while (iterator2.hasNext()) {
                        abb9 = iterator2.next();
                        try {
                            inputStream10 = abb9.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 is empty or null", this.name, uh6, uh5, abb9.getSourceName());
                                    }
                                    else {
                                        map3.computeIfAbsent(uh6, uh -> Tag.Builder.tag()).addFromJson(jsonObject14, abb9.getSourceName());
                                    }
                                }
                                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, uh6, uh5, abb9.getSourceName(), exception10);
                        }
                        finally {
                            IOUtils.closeQuietly((Closeable)abb9);
                        }
                    }
                }
                catch (IOException iOException8) {
                    TagCollection.LOGGER.error("Couldn't read {} tag list {} from {}", this.name, uh6, uh5, iOException8);
                }
            }
            return map3;
        }, executor);
    }
    
    public void load(final Map<ResourceLocation, Tag.Builder> map) {
        final Map<ResourceLocation, Tag<T>> map2 = Maps.newHashMap();
        final Function<ResourceLocation, Tag<T>> function4 = map2::get;
        final Function<ResourceLocation, T> function5 = (Function<ResourceLocation, T>)(uh -> this.idToValue.apply(uh).orElse(null));
        while (!map.isEmpty()) {
            boolean boolean6 = false;
            final Iterator<Map.Entry<ResourceLocation, Tag.Builder>> iterator7 = map.entrySet().iterator();
            while (iterator7.hasNext()) {
                final Map.Entry<ResourceLocation, Tag.Builder> entry8 = iterator7.next();
                final Optional<Tag<T>> optional9 = entry8.getValue().<T>build(function4, function5);
                if (optional9.isPresent()) {
                    map2.put(entry8.getKey(), optional9.get());
                    iterator7.remove();
                    boolean6 = true;
                }
            }
            if (!boolean6) {
                break;
            }
        }
        map.forEach((uh, a) -> TagCollection.LOGGER.error("Couldn't load {} tag {} as it is missing following references: {}", this.name, uh, a.<T>getUnresolvedEntries(function4, function5).map(Objects::toString).collect(Collectors.joining(","))));
        this.replace(map2);
    }
    
    protected void replace(final Map<ResourceLocation, Tag<T>> map) {
        this.tags = ImmutableBiMap.copyOf(map);
    }
    
    public Map<ResourceLocation, Tag<T>> getAllTags() {
        return this.tags;
    }
    
    static {
        LOGGER = LogManager.getLogger();
        GSON = new Gson();
        PATH_SUFFIX_LENGTH = ".json".length();
    }
}
