package net.minecraft.world.item.crafting;

import java.util.Iterator;
import java.util.List;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.Collection;
import net.minecraft.tags.SerializationTags;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import com.google.gson.JsonParseException;
import com.google.gson.JsonObject;
import net.minecraft.util.GsonHelper;
import java.util.stream.StreamSupport;
import com.google.gson.JsonSyntaxException;
import net.minecraft.world.item.Item;
import net.minecraft.tags.Tag;
import java.util.function.Function;
import net.minecraft.world.level.ItemLike;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import net.minecraft.network.FriendlyByteBuf;
import java.util.Comparator;
import it.unimi.dsi.fastutil.ints.IntComparators;
import net.minecraft.world.entity.player.StackedContents;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraft.world.item.ItemStack;
import java.util.function.Predicate;

public final class Ingredient implements Predicate<ItemStack> {
    public static final Ingredient EMPTY;
    private final Value[] values;
    private ItemStack[] itemStacks;
    private IntList stackingIds;
    
    private Ingredient(final Stream<? extends Value> stream) {
        this.values = stream.<Value>toArray(Value[]::new);
    }
    
    public ItemStack[] getItems() {
        this.dissolve();
        return this.itemStacks;
    }
    
    private void dissolve() {
        if (this.itemStacks == null) {
            this.itemStacks = Arrays.<Value>stream(this.values).flatMap(c -> c.getItems().stream()).distinct().<ItemStack>toArray(ItemStack[]::new);
        }
    }
    
    @Override
    public boolean test(@Nullable final ItemStack bki) {
        if (bki == null) {
            return false;
        }
        this.dissolve();
        if (this.itemStacks.length == 0) {
            return bki.isEmpty();
        }
        for (final ItemStack bki2 : this.itemStacks) {
            if (bki2.getItem() == bki.getItem()) {
                return true;
            }
        }
        return false;
    }
    
    public IntList getStackingIds() {
        if (this.stackingIds == null) {
            this.dissolve();
            this.stackingIds = (IntList)new IntArrayList(this.itemStacks.length);
            for (final ItemStack bki5 : this.itemStacks) {
                this.stackingIds.add(StackedContents.getStackingIndex(bki5));
            }
            this.stackingIds.sort((Comparator)IntComparators.NATURAL_COMPARATOR);
        }
        return this.stackingIds;
    }
    
    public void toNetwork(final FriendlyByteBuf mg) {
        this.dissolve();
        mg.writeVarInt(this.itemStacks.length);
        for (int integer3 = 0; integer3 < this.itemStacks.length; ++integer3) {
            mg.writeItem(this.itemStacks[integer3]);
        }
    }
    
    public JsonElement toJson() {
        if (this.values.length == 1) {
            return this.values[0].serialize();
        }
        final JsonArray jsonArray2 = new JsonArray();
        for (final Value c6 : this.values) {
            jsonArray2.add(c6.serialize());
        }
        return jsonArray2;
    }
    
    public boolean isEmpty() {
        return this.values.length == 0 && (this.itemStacks == null || this.itemStacks.length == 0) && (this.stackingIds == null || this.stackingIds.isEmpty());
    }
    
    private static Ingredient fromValues(final Stream<? extends Value> stream) {
        final Ingredient bmr2 = new Ingredient(stream);
        return (bmr2.values.length == 0) ? Ingredient.EMPTY : bmr2;
    }
    
    public static Ingredient of(final ItemLike... arr) {
        return of(Arrays.<ItemLike>stream(arr).<ItemStack>map(ItemStack::new));
    }
    
    public static Ingredient of(final ItemStack... arr) {
        return of(Arrays.<ItemStack>stream(arr));
    }
    
    public static Ingredient of(final Stream<ItemStack> stream) {
        return fromValues(stream.filter(bki -> !bki.isEmpty()).map(bki -> new ItemValue(bki)));
    }
    
    public static Ingredient of(final Tag<Item> adf) {
        return fromValues(Stream.<TagValue>of(new TagValue((Tag)adf)));
    }
    
    public static Ingredient fromNetwork(final FriendlyByteBuf mg) {
        final int integer2 = mg.readVarInt();
        return fromValues(Stream.generate(() -> new ItemValue(mg.readItem())).limit(integer2));
    }
    
    public static Ingredient fromJson(@Nullable final JsonElement jsonElement) {
        if (jsonElement == null || jsonElement.isJsonNull()) {
            throw new JsonSyntaxException("Item cannot be null");
        }
        if (jsonElement.isJsonObject()) {
            return fromValues(Stream.<Value>of(valueFromJson(jsonElement.getAsJsonObject())));
        }
        if (!jsonElement.isJsonArray()) {
            throw new JsonSyntaxException("Expected item to be object or array of objects");
        }
        final JsonArray jsonArray2 = jsonElement.getAsJsonArray();
        if (jsonArray2.size() == 0) {
            throw new JsonSyntaxException("Item array cannot be empty, at least one item must be defined");
        }
        return fromValues(StreamSupport.<JsonElement>stream(jsonArray2.spliterator(), false).map(jsonElement -> valueFromJson(GsonHelper.convertToJsonObject(jsonElement, "item"))));
    }
    
    private static Value valueFromJson(final JsonObject jsonObject) {
        if (jsonObject.has("item") && jsonObject.has("tag")) {
            throw new JsonParseException("An ingredient entry is either a tag or an item, not both");
        }
        if (jsonObject.has("item")) {
            final ResourceLocation uh2 = new ResourceLocation(GsonHelper.getAsString(jsonObject, "item"));
            final Object o;
            final Object obj;
            final Item bke3 = Registry.ITEM.getOptional(uh2).<Throwable>orElseThrow(() -> {
                new JsonSyntaxException("Unknown item '" + obj + "'");
                return o;
            });
            return new ItemValue(new ItemStack(bke3));
        }
        if (!jsonObject.has("tag")) {
            throw new JsonParseException("An ingredient entry needs either a tag or an item");
        }
        final ResourceLocation uh2 = new ResourceLocation(GsonHelper.getAsString(jsonObject, "tag"));
        final Tag<Item> adf3 = SerializationTags.getInstance().getItems().getTag(uh2);
        if (adf3 == null) {
            throw new JsonSyntaxException("Unknown item tag '" + uh2 + "'");
        }
        return new TagValue((Tag)adf3);
    }
    
    static {
        EMPTY = new Ingredient(Stream.empty());
    }
    
    static class ItemValue implements Value {
        private final ItemStack item;
        
        private ItemValue(final ItemStack bki) {
            this.item = bki;
        }
        
        @Override
        public Collection<ItemStack> getItems() {
            return Collections.<ItemStack>singleton(this.item);
        }
        
        @Override
        public JsonObject serialize() {
            final JsonObject jsonObject2 = new JsonObject();
            jsonObject2.addProperty("item", Registry.ITEM.getKey(this.item.getItem()).toString());
            return jsonObject2;
        }
    }
    
    static class TagValue implements Value {
        private final Tag<Item> tag;
        
        private TagValue(final Tag<Item> adf) {
            this.tag = adf;
        }
        
        @Override
        public Collection<ItemStack> getItems() {
            final List<ItemStack> list2 = Lists.newArrayList();
            for (final Item bke4 : this.tag.getValues()) {
                list2.add(new ItemStack(bke4));
            }
            return list2;
        }
        
        @Override
        public JsonObject serialize() {
            final JsonObject jsonObject2 = new JsonObject();
            jsonObject2.addProperty("tag", SerializationTags.getInstance().getItems().getIdOrThrow(this.tag).toString());
            return jsonObject2;
        }
    }
    
    interface Value {
        Collection<ItemStack> getItems();
        
        JsonObject serialize();
    }
}
