package net.minecraft.tags;

import java.util.function.Consumer;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import net.minecraft.util.GsonHelper;
import com.google.gson.JsonObject;
import java.util.stream.Stream;
import java.util.Iterator;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.util.Set;
import java.util.Random;
import java.util.List;
import java.util.function.Function;
import java.util.Optional;
import com.mojang.serialization.DataResult;
import net.minecraft.resources.ResourceLocation;
import com.mojang.serialization.Codec;
import java.util.function.Supplier;

public interface Tag<T> {
    default <T> Codec<Tag<T>> codec(final Supplier<TagCollection<T>> supplier) {
        return (Codec<Tag<T>>)ResourceLocation.CODEC.flatXmap(uh -> Optional.<Tag<T>>ofNullable(supplier.get().getTag(uh)).map(DataResult::success).orElseGet(() -> DataResult.error("Unknown tag: " + uh)), adf -> Optional.<ResourceLocation>ofNullable(supplier.get().getId(adf)).map(DataResult::success).orElseGet(() -> DataResult.error("Unknown tag: " + adf)));
    }
    
    boolean contains(final T object);
    
    List<T> getValues();
    
    default T getRandomElement(final Random random) {
        final List<T> list3 = this.getValues();
        return list3.get(random.nextInt(list3.size()));
    }
    
    default <T> Tag<T> fromSet(final Set<T> set) {
        return SetTag.<T>create(set);
    }
    
    public static class BuilderEntry {
        private final Entry entry;
        private final String source;
        
        private BuilderEntry(final Entry d, final String string) {
            this.entry = d;
            this.source = string;
        }
        
        public Entry getEntry() {
            return this.entry;
        }
        
        @Override
        public String toString() {
            return this.entry.toString() + " (from " + this.source + ")";
        }
    }
    
    public static class Builder {
        private final List<BuilderEntry> entries;
        
        public Builder() {
            this.entries = Lists.newArrayList();
        }
        
        public static Builder tag() {
            return new Builder();
        }
        
        public Builder add(final BuilderEntry b) {
            this.entries.add(b);
            return this;
        }
        
        public Builder add(final Entry d, final String string) {
            return this.add(new BuilderEntry(d, string));
        }
        
        public Builder addElement(final ResourceLocation uh, final String string) {
            return this.add(new ElementEntry(uh), string);
        }
        
        public Builder addTag(final ResourceLocation uh, final String string) {
            return this.add(new TagEntry(uh), string);
        }
        
        public <T> Optional<Tag<T>> build(final Function<ResourceLocation, Tag<T>> function1, final Function<ResourceLocation, T> function2) {
            final ImmutableSet.Builder<T> builder4 = ImmutableSet.<T>builder();
            for (final BuilderEntry b6 : this.entries) {
                if (!b6.getEntry().<T>build(function1, function2, builder4::add)) {
                    return Optional.<Tag<T>>empty();
                }
            }
            return Optional.<Tag<T>>of(Tag.<T>fromSet(builder4.build()));
        }
        
        public Stream<BuilderEntry> getEntries() {
            return this.entries.stream();
        }
        
        public <T> Stream<BuilderEntry> getUnresolvedEntries(final Function<ResourceLocation, Tag<T>> function1, final Function<ResourceLocation, T> function2) {
            return this.getEntries().filter(b -> !b.getEntry().<T>build(function1, function2, object -> {}));
        }
        
        public Builder addFromJson(final JsonObject jsonObject, final String string) {
            final JsonArray jsonArray4 = GsonHelper.getAsJsonArray(jsonObject, "values");
            final List<Entry> list5 = Lists.newArrayList();
            for (final JsonElement jsonElement7 : jsonArray4) {
                final String string2 = GsonHelper.convertToString(jsonElement7, "value");
                if (string2.startsWith("#")) {
                    list5.add(new TagEntry(new ResourceLocation(string2.substring(1))));
                }
                else {
                    list5.add(new ElementEntry(new ResourceLocation(string2)));
                }
            }
            if (GsonHelper.getAsBoolean(jsonObject, "replace", false)) {
                this.entries.clear();
            }
            list5.forEach(d -> this.entries.add(new BuilderEntry(d, string)));
            return this;
        }
        
        public JsonObject serializeToJson() {
            final JsonObject jsonObject2 = new JsonObject();
            final JsonArray jsonArray3 = new JsonArray();
            for (final BuilderEntry b5 : this.entries) {
                b5.getEntry().serializeTo(jsonArray3);
            }
            jsonObject2.addProperty("replace", false);
            jsonObject2.add("values", jsonArray3);
            return jsonObject2;
        }
    }
    
    public static class ElementEntry implements Entry {
        private final ResourceLocation id;
        
        public ElementEntry(final ResourceLocation uh) {
            this.id = uh;
        }
        
        @Override
        public <T> boolean build(final Function<ResourceLocation, Tag<T>> function1, final Function<ResourceLocation, T> function2, final Consumer<T> consumer) {
            final T object5 = function2.apply(this.id);
            if (object5 == null) {
                return false;
            }
            consumer.accept(object5);
            return true;
        }
        
        @Override
        public void serializeTo(final JsonArray jsonArray) {
            jsonArray.add(this.id.toString());
        }
        
        @Override
        public String toString() {
            return this.id.toString();
        }
    }
    
    public static class TagEntry implements Entry {
        private final ResourceLocation id;
        
        public TagEntry(final ResourceLocation uh) {
            this.id = uh;
        }
        
        @Override
        public <T> boolean build(final Function<ResourceLocation, Tag<T>> function1, final Function<ResourceLocation, T> function2, final Consumer<T> consumer) {
            final Tag<T> adf5 = function1.apply(this.id);
            if (adf5 == null) {
                return false;
            }
            adf5.getValues().forEach(consumer);
            return true;
        }
        
        @Override
        public void serializeTo(final JsonArray jsonArray) {
            jsonArray.add("#" + this.id);
        }
        
        @Override
        public String toString() {
            return "#" + this.id;
        }
    }
    
    public interface Named<T> extends Tag<T> {
        ResourceLocation getName();
    }
    
    public interface Entry {
         <T> boolean build(final Function<ResourceLocation, Tag<T>> function1, final Function<ResourceLocation, T> function2, final Consumer<T> consumer);
        
        void serializeTo(final JsonArray jsonArray);
    }
}
