package net.minecraft.world.item.crafting;

import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.Container;
import net.minecraft.world.level.ItemLike;
import com.google.gson.JsonParseException;
import net.minecraft.core.Registry;
import net.minecraft.world.item.Item;
import java.util.Iterator;
import com.google.gson.JsonElement;
import com.google.common.collect.Maps;
import com.google.gson.JsonObject;
import net.minecraft.util.GsonHelper;
import com.google.gson.JsonArray;
import com.google.common.annotations.VisibleForTesting;
import java.util.Set;
import com.google.gson.JsonSyntaxException;
import com.google.common.collect.Sets;
import java.util.Map;
import net.minecraft.world.level.Level;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.core.NonNullList;

public class ShapedRecipe implements CraftingRecipe {
    private final int width;
    private final int height;
    private final NonNullList<Ingredient> recipeItems;
    private final ItemStack result;
    private final ResourceLocation id;
    private final String group;
    
    public ShapedRecipe(final ResourceLocation sm, final String string, final int integer3, final int integer4, final NonNullList<Ingredient> fy, final ItemStack ben) {
        this.id = sm;
        this.group = string;
        this.width = integer3;
        this.height = integer4;
        this.recipeItems = fy;
        this.result = ben;
    }
    
    @Override
    public ResourceLocation getId() {
        return this.id;
    }
    
    @Override
    public RecipeSerializer<?> getSerializer() {
        return RecipeSerializer.SHAPED_RECIPE;
    }
    
    @Override
    public String getGroup() {
        return this.group;
    }
    
    @Override
    public ItemStack getResultItem() {
        return this.result;
    }
    
    @Override
    public NonNullList<Ingredient> getIngredients() {
        return this.recipeItems;
    }
    
    @Override
    public boolean canCraftInDimensions(final int integer1, final int integer2) {
        return integer1 >= this.width && integer2 >= this.height;
    }
    
    @Override
    public boolean matches(final CraftingContainer baz, final Level bjw) {
        for (int integer4 = 0; integer4 <= baz.getWidth() - this.width; ++integer4) {
            for (int integer5 = 0; integer5 <= baz.getHeight() - this.height; ++integer5) {
                if (this.matches(baz, integer4, integer5, true)) {
                    return true;
                }
                if (this.matches(baz, integer4, integer5, false)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    private boolean matches(final CraftingContainer baz, final int integer2, final int integer3, final boolean boolean4) {
        for (int integer4 = 0; integer4 < baz.getWidth(); ++integer4) {
            for (int integer5 = 0; integer5 < baz.getHeight(); ++integer5) {
                final int integer6 = integer4 - integer2;
                final int integer7 = integer5 - integer3;
                Ingredient bgt10 = Ingredient.EMPTY;
                if (integer6 >= 0 && integer7 >= 0 && integer6 < this.width && integer7 < this.height) {
                    if (boolean4) {
                        bgt10 = this.recipeItems.get(this.width - integer6 - 1 + integer7 * this.width);
                    }
                    else {
                        bgt10 = this.recipeItems.get(integer6 + integer7 * this.width);
                    }
                }
                if (!bgt10.test(baz.getItem(integer4 + integer5 * baz.getWidth()))) {
                    return false;
                }
            }
        }
        return true;
    }
    
    @Override
    public ItemStack assemble(final CraftingContainer baz) {
        return this.getResultItem().copy();
    }
    
    public int getWidth() {
        return this.width;
    }
    
    public int getHeight() {
        return this.height;
    }
    
    private static NonNullList<Ingredient> dissolvePattern(final String[] arr, final Map<String, Ingredient> map, final int integer3, final int integer4) {
        final NonNullList<Ingredient> fy5 = NonNullList.<Ingredient>withSize(integer3 * integer4, Ingredient.EMPTY);
        final Set<String> set6 = Sets.newHashSet(map.keySet());
        set6.remove(" ");
        for (int integer5 = 0; integer5 < arr.length; ++integer5) {
            for (int integer6 = 0; integer6 < arr[integer5].length(); ++integer6) {
                final String string9 = arr[integer5].substring(integer6, integer6 + 1);
                final Ingredient bgt10 = map.get(string9);
                if (bgt10 == null) {
                    throw new JsonSyntaxException("Pattern references symbol '" + string9 + "' but it's not defined in the key");
                }
                set6.remove(string9);
                fy5.set(integer6 + integer3 * integer5, bgt10);
            }
        }
        if (!set6.isEmpty()) {
            throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + set6);
        }
        return fy5;
    }
    
    @VisibleForTesting
    static String[] shrink(final String... arr) {
        int integer2 = Integer.MAX_VALUE;
        int integer3 = 0;
        int integer4 = 0;
        int integer5 = 0;
        for (int integer6 = 0; integer6 < arr.length; ++integer6) {
            final String string7 = arr[integer6];
            integer2 = Math.min(integer2, firstNonSpace(string7));
            final int integer7 = lastNonSpace(string7);
            integer3 = Math.max(integer3, integer7);
            if (integer7 < 0) {
                if (integer4 == integer6) {
                    ++integer4;
                }
                ++integer5;
            }
            else {
                integer5 = 0;
            }
        }
        if (arr.length == integer5) {
            return new String[0];
        }
        final String[] arr2 = new String[arr.length - integer5 - integer4];
        for (int integer8 = 0; integer8 < arr2.length; ++integer8) {
            arr2[integer8] = arr[integer8 + integer4].substring(integer2, integer3 + 1);
        }
        return arr2;
    }
    
    private static int firstNonSpace(final String string) {
        int integer2;
        for (integer2 = 0; integer2 < string.length() && string.charAt(integer2) == ' '; ++integer2) {}
        return integer2;
    }
    
    private static int lastNonSpace(final String string) {
        int integer2;
        for (integer2 = string.length() - 1; integer2 >= 0 && string.charAt(integer2) == ' '; --integer2) {}
        return integer2;
    }
    
    private static String[] patternFromJson(final JsonArray jsonArray) {
        final String[] arr2 = new String[jsonArray.size()];
        if (arr2.length > 3) {
            throw new JsonSyntaxException("Invalid pattern: too many rows, 3 is maximum");
        }
        if (arr2.length == 0) {
            throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed");
        }
        for (int integer3 = 0; integer3 < arr2.length; ++integer3) {
            final String string4 = GsonHelper.convertToString(jsonArray.get(integer3), "pattern[" + integer3 + "]");
            if (string4.length() > 3) {
                throw new JsonSyntaxException("Invalid pattern: too many columns, 3 is maximum");
            }
            if (integer3 > 0 && arr2[0].length() != string4.length()) {
                throw new JsonSyntaxException("Invalid pattern: each row must be the same width");
            }
            arr2[integer3] = string4;
        }
        return arr2;
    }
    
    private static Map<String, Ingredient> keyFromJson(final JsonObject jsonObject) {
        final Map<String, Ingredient> map2 = Maps.newHashMap();
        for (final Map.Entry<String, JsonElement> entry4 : jsonObject.entrySet()) {
            if (entry4.getKey().length() != 1) {
                throw new JsonSyntaxException("Invalid key entry: '" + entry4.getKey() + "' is an invalid symbol (must be 1 character only).");
            }
            if (" ".equals(entry4.getKey())) {
                throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol.");
            }
            map2.put(entry4.getKey(), Ingredient.fromJson(entry4.getValue()));
        }
        map2.put(" ", Ingredient.EMPTY);
        return map2;
    }
    
    public static ItemStack itemFromJson(final JsonObject jsonObject) {
        final String string2 = GsonHelper.getAsString(jsonObject, "item");
        final Object o;
        final String str;
        final Item bei3 = Registry.ITEM.getOptional(new ResourceLocation(string2)).<Throwable>orElseThrow(() -> {
            new JsonSyntaxException("Unknown item '" + str + "'");
            return o;
        });
        if (jsonObject.has("data")) {
            throw new JsonParseException("Disallowed data tag found");
        }
        final int integer4 = GsonHelper.getAsInt(jsonObject, "count", 1);
        return new ItemStack(bei3, integer4);
    }
    
    public static class Serializer implements RecipeSerializer<ShapedRecipe> {
        @Override
        public ShapedRecipe fromJson(final ResourceLocation sm, final JsonObject jsonObject) {
            final String string4 = GsonHelper.getAsString(jsonObject, "group", "");
            final Map<String, Ingredient> map5 = keyFromJson(GsonHelper.getAsJsonObject(jsonObject, "key"));
            final String[] arr6 = ShapedRecipe.shrink(patternFromJson(GsonHelper.getAsJsonArray(jsonObject, "pattern")));
            final int integer7 = arr6[0].length();
            final int integer8 = arr6.length;
            final NonNullList<Ingredient> fy9 = dissolvePattern(arr6, map5, integer7, integer8);
            final ItemStack ben10 = ShapedRecipe.itemFromJson(GsonHelper.getAsJsonObject(jsonObject, "result"));
            return new ShapedRecipe(sm, string4, integer7, integer8, fy9, ben10);
        }
        
        @Override
        public ShapedRecipe fromNetwork(final ResourceLocation sm, final FriendlyByteBuf kv) {
            final int integer4 = kv.readVarInt();
            final int integer5 = kv.readVarInt();
            final String string6 = kv.readUtf(32767);
            final NonNullList<Ingredient> fy7 = NonNullList.<Ingredient>withSize(integer4 * integer5, Ingredient.EMPTY);
            for (int integer6 = 0; integer6 < fy7.size(); ++integer6) {
                fy7.set(integer6, Ingredient.fromNetwork(kv));
            }
            final ItemStack ben8 = kv.readItem();
            return new ShapedRecipe(sm, string6, integer4, integer5, fy7, ben8);
        }
        
        @Override
        public void toNetwork(final FriendlyByteBuf kv, final ShapedRecipe bhb) {
            kv.writeVarInt(bhb.width);
            kv.writeVarInt(bhb.height);
            kv.writeUtf(bhb.group);
            for (final Ingredient bgt5 : bhb.recipeItems) {
                bgt5.toNetwork(kv);
            }
            kv.writeItem(bhb.result);
        }
    }
}
