<commit_before>package com.raoulvdberge.refinedstorage.apiimpl.autocrafting.task;

import com.raoulvdberge.refinedstorage.RSUtils;
import com.raoulvdberge.refinedstorage.api.autocrafting.ICraftingPattern;
import com.raoulvdberge.refinedstorage.api.autocrafting.ICraftingPatternContainer;
import com.raoulvdberge.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorElement;
import com.raoulvdberge.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorElementList;
import com.raoulvdberge.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement;
import com.raoulvdberge.refinedstorage.api.autocrafting.task.ICraftingStep;
import com.raoulvdberge.refinedstorage.api.autocrafting.task.ICraftingTask;
import com.raoulvdberge.refinedstorage.api.network.INetworkMaster;
import com.raoulvdberge.refinedstorage.api.util.IComparer;
import com.raoulvdberge.refinedstorage.api.util.IFluidStackList;
import com.raoulvdberge.refinedstorage.api.util.IItemStackList;
import com.raoulvdberge.refinedstorage.apiimpl.API;
import com.raoulvdberge.refinedstorage.apiimpl.autocrafting.craftingmonitor.*;
import com.raoulvdberge.refinedstorage.apiimpl.autocrafting.preview.CraftingPreviewElementFluidStack;
import com.raoulvdberge.refinedstorage.apiimpl.autocrafting.preview.CraftingPreviewElementItemStack;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.items.ItemHandlerHelper;

import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;

public class CraftingTask implements ICraftingTask {
    protected static final int DEFAULT_COMPARE = IComparer.COMPARE_DAMAGE | IComparer.COMPARE_NBT;

    public static final String NBT_STEPS = "Steps";
    public static final String NBT_TO_TAKE_FLUIDS = "ToTakeFluids";
    public static final String NBT_TO_INSERT_ITEMS = "ToInsertItems";
    public static final String NBT_TO_INSERT_FLUIDS = "ToInsertFluids";
    public static final String NBT_TOOK_FLUIDS = "TookFluids";

    private INetworkMaster network;
    @Nullable
    private ItemStack requested;
    private ICraftingPattern pattern;
    private int quantity;
    private List<ICraftingStep> steps = new ArrayList<>();
    private IItemStackList toTake = API.instance().createItemStackList();
    private IItemStackList toCraft = API.instance().createItemStackList();
    private IItemStackList missing = API.instance().createItemStackList();
    private Set<ICraftingPattern> usedPatterns = new HashSet<>();
    private boolean recurseFound = false;
    private Deque<ItemStack> toInsertItems = new ArrayDeque<>();
    private Deque<FluidStack> toInsertFluids = new ArrayDeque<>();
    private IFluidStackList toTakeFluids = API.instance().createFluidStackList();
    private IFluidStackList tookFluids = API.instance().createFluidStackList();

    public CraftingTask(INetworkMaster network, @Nullable ItemStack requested, ICraftingPattern pattern, int quantity) {
        this.network = network;
        this.requested = requested;
        this.pattern = pattern;
        this.quantity = quantity;
    }

    public CraftingTask(INetworkMaster network, @Nullable ItemStack requested, ICraftingPattern pattern, int quantity, List<ICraftingStep> steps, Deque<ItemStack> toInsertItems, IFluidStackList toTakeFluids, IFluidStackList tookFluids, Deque<FluidStack> toInsertFluids) {
        this(network, requested, pattern, quantity);
        this.steps = steps;
        this.toInsertItems = toInsertItems;
        this.toTakeFluids = toTakeFluids;
        this.tookFluids = tookFluids;
        this.toInsertFluids = toInsertFluids;
    }

    @Override
    public void calculate() {
        // Copy here might be expensive but since it is only executed once it isn't a big impact
        IItemStackList networkList = network.getItemStorageCache().getList().copy();
        networkList.clean(); // Remove the zero stacks
        networkList = networkList.getOredicted();
        IFluidStackList networkFluidList = network.getFluidStorageCache().getList().copy();
        IItemStackList toInsert = API.instance().createItemStackList();

        ItemStack requested = this.requested != null ? this.requested : pattern.getOutputs().get(0);
        toCraft.add(ItemHandlerHelper.copyStackWithSize(requested, quantity));

        int quantity = this.quantity;

        while (quantity > 0 && !recurseFound) {
            calculate(networkList, networkFluidList, pattern, toInsert);
            quantity -= pattern.getQuantityPerRequest(requested);
        }

        usedPatterns.clear();
    }

    private void calculate(IItemStackList networkList, IFluidStackList networkFluidList, ICraftingPattern pattern, IItemStackList toInsert) {
        recurseFound |= !usedPatterns.add(pattern);
        if (recurseFound) {
            return;
        }

        int compare = DEFAULT_COMPARE | (pattern.isOredict() ? IComparer.COMPARE_OREDICT : 0);

        IItemStackList inputs = API.instance().createItemStackList();
        IItemStackList actualInputs = API.instance().createItemStackList();
        List<ItemStack> usedStacks = new LinkedList<>();

        for (List<ItemStack> oreInputs : pattern.getOreInputs()) {
            boolean added = false;
            for (ItemStack input : oreInputs) {
                int oreCompare = IComparer.COMPARE_NBT | (input.isItemStackDamageable() ? 0 : IComparer.COMPARE_DAMAGE);
                if (network.getItemStorageCache().getList().get(input, oreCompare) != null) {
                    usedStacks.add(input.copy());
                    inputs.add(input.copy());
                    added = true;
                    break;
                }
            }
            if (!added) {
                ItemStack choice = null;
                if (!oreInputs.isEmpty()) {
                    choice = oreInputs.get(0);
                    inputs.add(choice);
                }
                usedStacks.add(choice);
            }
        }

        for (ItemStack input : inputs.getStacks()) {
            // This will be a tool, like a hammer
            if (input.isItemStackDamageable()) {
                compare &= ~IComparer.COMPARE_DAMAGE;
            } else {
                compare |= IComparer.COMPARE_DAMAGE;
            }

            ItemStack extraStack = toInsert.get(input, compare);
            ItemStack networkStack = networkList.get(input, compare);

            // This handles recipes that use the output as input for the sub recipe
            final int lambdaCompare = compare;
            ICraftingPattern inputPattern = null;
            int available = (extraStack == null ? 0 : extraStack.stackSize) + (networkStack == null ? 0 : networkStack.stackSize);
            if (available < input.stackSize) {
                inputPattern = network.getPattern(input, compare);
                if (inputPattern != null) {
                    if (inputPattern.getInputs().stream().anyMatch(s -> API.instance().getComparer().isEqual(s, input, lambdaCompare))) {
                        int craftQuantity = inputPattern.getQuantityPerRequest(input, compare);
                        // The needed amount is the actual needed amount of extraStacks + the needed input (twice so you can keep repeating it)
                        long needed = (networkStack == null ? 0 : -networkStack.stackSize) + input.stackSize + inputPattern.getInputs().stream().filter(s -> API.instance().getComparer().isEqual(s, input, lambdaCompare)).count() * 2;
                        do {
                            calculate(networkList, networkFluidList, inputPattern, toInsert);
                            toCraft.add(ItemHandlerHelper.copyStackWithSize(input, craftQuantity));
                            extraStack = toInsert.get(input, compare);
                        } while (extraStack != null && extraStack.stackSize < needed);
                    }
                }
            }

            while (input.stackSize > 0) {
                if (extraStack != null && extraStack.stackSize > 0) {
                    int takeQuantity = Math.min(extraStack.stackSize, input.stackSize);
                    ItemStack inputStack = ItemHandlerHelper.copyStackWithSize(extraStack, takeQuantity);
                    actualInputs.add(inputStack.copy());
                    input.stackSize -= takeQuantity;
                    if (!inputStack.isItemStackDamageable() || !inputStack.isItemDamaged()) {
                        toCraft.add(inputStack);
                    }
                    toInsert.remove(inputStack, true);
                } else if (networkStack != null && networkStack.stackSize > 0) {
                    int takeQuantity = Math.min(networkStack.stackSize, input.stackSize);
                    ItemStack inputStack = ItemHandlerHelper.copyStackWithSize(networkStack, takeQuantity);
                    toTake.add(inputStack.copy());
                    actualInputs.add(inputStack.copy());
                    input.stackSize -= takeQuantity;
                    networkList.remove(inputStack, true);
                } else {
                    if (inputPattern == null) {
                        inputPattern = network.getPattern(input, compare);<commit_msg>get next stack from storages when first type wasn't enough, should fix #643<commit_after>package com.raoulvdberge.refinedstorage.apiimpl.autocrafting.task;

import com.raoulvdberge.refinedstorage.RSUtils;
import com.raoulvdberge.refinedstorage.api.autocrafting.ICraftingPattern;
import com.raoulvdberge.refinedstorage.api.autocrafting.ICraftingPatternContainer;
import com.raoulvdberge.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorElement;
import com.raoulvdberge.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorElementList;
import com.raoulvdberge.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement;
import com.raoulvdberge.refinedstorage.api.autocrafting.task.ICraftingStep;
import com.raoulvdberge.refinedstorage.api.autocrafting.task.ICraftingTask;
import com.raoulvdberge.refinedstorage.api.network.INetworkMaster;
import com.raoulvdberge.refinedstorage.api.util.IComparer;
import com.raoulvdberge.refinedstorage.api.util.IFluidStackList;
import com.raoulvdberge.refinedstorage.api.util.IItemStackList;
import com.raoulvdberge.refinedstorage.apiimpl.API;
import com.raoulvdberge.refinedstorage.apiimpl.autocrafting.craftingmonitor.*;
import com.raoulvdberge.refinedstorage.apiimpl.autocrafting.preview.CraftingPreviewElementFluidStack;
import com.raoulvdberge.refinedstorage.apiimpl.autocrafting.preview.CraftingPreviewElementItemStack;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.items.ItemHandlerHelper;

import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;

public class CraftingTask implements ICraftingTask {
    protected static final int DEFAULT_COMPARE = IComparer.COMPARE_DAMAGE | IComparer.COMPARE_NBT;

    public static final String NBT_STEPS = "Steps";
    public static final String NBT_TO_TAKE_FLUIDS = "ToTakeFluids";
    public static final String NBT_TO_INSERT_ITEMS = "ToInsertItems";
    public static final String NBT_TO_INSERT_FLUIDS = "ToInsertFluids";
    public static final String NBT_TOOK_FLUIDS = "TookFluids";

    private INetworkMaster network;
    @Nullable
    private ItemStack requested;
    private ICraftingPattern pattern;
    private int quantity;
    private List<ICraftingStep> steps = new ArrayList<>();
    private IItemStackList toTake = API.instance().createItemStackList();
    private IItemStackList toCraft = API.instance().createItemStackList();
    private IItemStackList missing = API.instance().createItemStackList();
    private Set<ICraftingPattern> usedPatterns = new HashSet<>();
    private boolean recurseFound = false;
    private Deque<ItemStack> toInsertItems = new ArrayDeque<>();
    private Deque<FluidStack> toInsertFluids = new ArrayDeque<>();
    private IFluidStackList toTakeFluids = API.instance().createFluidStackList();
    private IFluidStackList tookFluids = API.instance().createFluidStackList();

    public CraftingTask(INetworkMaster network, @Nullable ItemStack requested, ICraftingPattern pattern, int quantity) {
        this.network = network;
        this.requested = requested;
        this.pattern = pattern;
        this.quantity = quantity;
    }

    public CraftingTask(INetworkMaster network, @Nullable ItemStack requested, ICraftingPattern pattern, int quantity, List<ICraftingStep> steps, Deque<ItemStack> toInsertItems, IFluidStackList toTakeFluids, IFluidStackList tookFluids, Deque<FluidStack> toInsertFluids) {
        this(network, requested, pattern, quantity);
        this.steps = steps;
        this.toInsertItems = toInsertItems;
        this.toTakeFluids = toTakeFluids;
        this.tookFluids = tookFluids;
        this.toInsertFluids = toInsertFluids;
    }

    @Override
    public void calculate() {
        // Copy here might be expensive but since it is only executed once it isn't a big impact
        IItemStackList networkList = network.getItemStorageCache().getList().copy();
        networkList.clean(); // Remove the zero stacks
        networkList = networkList.getOredicted();
        IFluidStackList networkFluidList = network.getFluidStorageCache().getList().copy();
        IItemStackList toInsert = API.instance().createItemStackList();

        ItemStack requested = this.requested != null ? this.requested : pattern.getOutputs().get(0);
        toCraft.add(ItemHandlerHelper.copyStackWithSize(requested, quantity));

        int quantity = this.quantity;

        while (quantity > 0 && !recurseFound) {
            calculate(networkList, networkFluidList, pattern, toInsert);
            quantity -= pattern.getQuantityPerRequest(requested);
        }

        usedPatterns.clear();
    }

    private void calculate(IItemStackList networkList, IFluidStackList networkFluidList, ICraftingPattern pattern, IItemStackList toInsert) {
        recurseFound |= !usedPatterns.add(pattern);
        if (recurseFound) {
            return;
        }

        int compare = DEFAULT_COMPARE | (pattern.isOredict() ? IComparer.COMPARE_OREDICT : 0);

        IItemStackList inputs = API.instance().createItemStackList();
        IItemStackList actualInputs = API.instance().createItemStackList();
        List<ItemStack> usedStacks = new LinkedList<>();

        for (List<ItemStack> oreInputs : pattern.getOreInputs()) {
            boolean added = false;
            for (ItemStack input : oreInputs) {
                int oreCompare = IComparer.COMPARE_NBT | (input.isItemStackDamageable() ? 0 : IComparer.COMPARE_DAMAGE);
                if (network.getItemStorageCache().getList().get(input, oreCompare) != null) {
                    usedStacks.add(input.copy());
                    inputs.add(input.copy());
                    added = true;
                    break;
                }
            }
            if (!added) {
                ItemStack choice = null;
                if (!oreInputs.isEmpty()) {
                    choice = oreInputs.get(0);
                    inputs.add(choice);
                }
                usedStacks.add(choice);
            }
        }

        for (ItemStack input : inputs.getStacks()) {
            // This will be a tool, like a hammer
            if (input.isItemStackDamageable()) {
                compare &= ~IComparer.COMPARE_DAMAGE;
            } else {
                compare |= IComparer.COMPARE_DAMAGE;
            }

            ItemStack extraStack = toInsert.get(input, compare);
            ItemStack networkStack = networkList.get(input, compare);

            // This handles recipes that use the output as input for the sub recipe
            final int lambdaCompare = compare;
            ICraftingPattern inputPattern = null;
            int available = (extraStack == null ? 0 : extraStack.stackSize) + (networkStack == null ? 0 : networkStack.stackSize);
            if (available < input.stackSize) {
                inputPattern = network.getPattern(input, compare);
                if (inputPattern != null) {
                    if (inputPattern.getInputs().stream().anyMatch(s -> API.instance().getComparer().isEqual(s, input, lambdaCompare))) {
                        int craftQuantity = inputPattern.getQuantityPerRequest(input, compare);
                        // The needed amount is the actual needed amount of extraStacks + the needed input (twice so you can keep repeating it)
                        long needed = (networkStack == null ? 0 : -networkStack.stackSize) + input.stackSize + inputPattern.getInputs().stream().filter(s -> API.instance().getComparer().isEqual(s, input, lambdaCompare)).count() * 2;
                        do {
                            calculate(networkList, networkFluidList, inputPattern, toInsert);
                            toCraft.add(ItemHandlerHelper.copyStackWithSize(input, craftQuantity));
                            extraStack = toInsert.get(input, compare);
                        } while (extraStack != null && extraStack.stackSize < needed);
                    }
                }
            }

            while (input.stackSize > 0) {
                if (extraStack != null && extraStack.stackSize > 0) {
                    int takeQuantity = Math.min(extraStack.stackSize, input.stackSize);
                    ItemStack inputStack = ItemHandlerHelper.copyStackWithSize(extraStack, takeQuantity);
                    actualInputs.add(inputStack.copy());
                    input.stackSize -= takeQuantity;
                    if (!inputStack.isItemStackDamageable() || !inputStack.isItemDamaged()) {
                        toCraft.add(inputStack);
                    }
                    toInsert.remove(inputStack, true);
                    if (input.stackSize > 0) {
                        extraStack = toInsert.get(input, compare);
                    }
                } else if (networkStack != null && networkStack.stackSize > 0) {
                    int takeQuantity = Math.min(networkStack.stackSize, input.stackSize);
                    ItemStack inputStack = ItemHandlerHelper.copyStackWithSize(networkStack, takeQuantity);
                    toTake.add(inputStack.copy());
                    actualInputs.add(inputStack.copy());
                    input.stackSize -= takeQuantity;
                    networkList.remove(inputStack, true);
                    if (input.stackSize > 0) {
                        networkStack = networkList.get(inputStack, compare);
                    }
                } else {
                    if (inputPattern == null) {
                        inputPattern = network.getPattern(input, compare);
{'fraction_non_alphanumeric': 0.09197889182058047, 'fraction_numerical': 0.0015303430079155673, 'mean_word_length': 2.7020902520023444, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5532222', 'n_tokens_mistral': 5227, 'n_tokens_neox': 5037, 'n_words': 1027}