package net.minecraft.data.tags;

import java.util.stream.Stream;
import com.google.gson.GsonBuilder;
import org.apache.logging.log4j.LogManager;
import java.io.BufferedWriter;
import java.nio.file.Path;
import com.google.gson.JsonObject;
import java.io.IOException;
import java.nio.file.OpenOption;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import com.google.gson.JsonElement;
import java.util.function.Function;
import java.util.Objects;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.List;
import net.minecraft.tags.SetTag;
import net.minecraft.data.HashCache;
import com.google.common.collect.Maps;
import net.minecraft.tags.Tag;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;
import net.minecraft.core.Registry;
import net.minecraft.data.DataGenerator;
import com.google.gson.Gson;
import org.apache.logging.log4j.Logger;
import net.minecraft.data.DataProvider;

public abstract class TagsProvider<T> implements DataProvider {
    private static final Logger LOGGER;
    private static final Gson GSON;
    protected final DataGenerator generator;
    protected final Registry<T> registry;
    private final Map<ResourceLocation, Tag.Builder> builders;
    
    protected TagsProvider(final DataGenerator hk, final Registry<T> gl) {
        this.builders = Maps.newLinkedHashMap();
        this.generator = hk;
        this.registry = gl;
    }
    
    protected abstract void addTags();
    
    @Override
    public void run(final HashCache hm) {
        this.builders.clear();
        this.addTags();
        final Tag<T> adf3 = SetTag.empty();
        final Object o;
        final Function<ResourceLocation, Tag<T>> function4 = (Function<ResourceLocation, Tag<T>>)(uh -> this.builders.containsKey(uh) ? o : null);
        final Function<ResourceLocation, T> function5 = (Function<ResourceLocation, T>)(uh -> this.registry.getOptional(uh).orElse(null));
        final List<Tag.BuilderEntry> list7;
        final IllegalArgumentException ex;
        JsonObject jsonObject8;
        Path path9;
        String string10;
        String string11;
        BufferedWriter bufferedWriter12;
        final Throwable t2;
        this.builders.forEach((uh, a) -> {
            list7 = a.<T>getUnresolvedEntries(function4, function5).collect(Collectors.toList());
            if (!list7.isEmpty()) {
                new IllegalArgumentException(String.format("Couldn't define tag %s as it is missing following references: %s", uh, list7.stream().map(Objects::toString).collect(Collectors.joining(","))));
                throw ex;
            }
            else {
                jsonObject8 = a.serializeToJson();
                path9 = this.getPath(uh);
                try {
                    string10 = TagsProvider.GSON.toJson(jsonObject8);
                    string11 = TagsProvider.SHA1.hashUnencodedChars(string10).toString();
                    if (!Objects.equals(hm.getHash(path9), string11) || !Files.exists(path9)) {
                        Files.createDirectories(path9.getParent(), new FileAttribute[0]);
                        bufferedWriter12 = Files.newBufferedWriter(path9);
                        try {
                            bufferedWriter12.write(string10);
                        }
                        catch (Throwable t) {
                            throw t;
                        }
                        finally {
                            if (bufferedWriter12 != null) {
                                if (t2 != null) {
                                    try {
                                        bufferedWriter12.close();
                                    }
                                    catch (Throwable exception) {
                                        t2.addSuppressed(exception);
                                    }
                                }
                                else {
                                    bufferedWriter12.close();
                                }
                            }
                        }
                    }
                    hm.putNew(path9, string11);
                }
                catch (IOException iOException10) {
                    TagsProvider.LOGGER.error("Couldn't save tags to {}", path9, iOException10);
                }
            }
        });
    }
    
    protected abstract Path getPath(final ResourceLocation uh);
    
    protected TagAppender<T> tag(final Tag.Named<T> e) {
        final Tag.Builder a3 = this.getOrCreateRawBuilder(e);
        return new TagAppender<T>(a3, (Registry)this.registry, "vanilla");
    }
    
    protected Tag.Builder getOrCreateRawBuilder(final Tag.Named<T> e) {
        return this.builders.computeIfAbsent(e.getName(), uh -> new Tag.Builder());
    }
    
    static {
        LOGGER = LogManager.getLogger();
        GSON = new GsonBuilder().setPrettyPrinting().create();
    }
    
    public static class TagAppender<T> {
        private final Tag.Builder builder;
        private final Registry<T> registry;
        private final String source;
        
        private TagAppender(final Tag.Builder a, final Registry<T> gl, final String string) {
            this.builder = a;
            this.registry = gl;
            this.source = string;
        }
        
        public TagAppender<T> add(final T object) {
            this.builder.addElement(this.registry.getKey(object), this.source);
            return this;
        }
        
        public TagAppender<T> addTag(final Tag.Named<T> e) {
            this.builder.addTag(e.getName(), this.source);
            return this;
        }
        
        @SafeVarargs
        public final TagAppender<T> add(final T... arr) {
            Stream.<T>of(arr).map(this.registry::getKey).forEach(uh -> this.builder.addElement(uh, this.source));
            return this;
        }
    }
}
