package gregtech.api.recipe;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
import gregtech.api.util.GT_Recipe;
import gregtech.api.util.MethodsReturnNonnullByDefault;
// spotless:off spotless likes formatting @code to @code
/**
* Helper class for searching recipe. Retrieve instance with {@link RecipeMap#findRecipeQuery}.
*
* It features fluent API, so you can find recipes like this:
*
*
* {@code
* GT_Recipe recipe = recipeMap.findRecipeQuery()
* .items(inputItems)
* .fluids(inputFluids)
* .find();
* }
*
*/
// spotless:on
@SuppressWarnings("unused")
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public final class FindRecipeQuery {
private static final Predicate ALWAYS = r -> true;
private final RecipeMap> recipeMap;
@Nullable
private ItemStack[] items;
@Nullable
private FluidStack[] fluids;
@Nullable
private ItemStack specialSlot;
private Predicate filter = ALWAYS;
private long voltage = Integer.MAX_VALUE;
@Nullable
private GT_Recipe cachedRecipe;
private boolean notUnificated;
private boolean dontCheckStackSizes;
private boolean forCollisionCheck;
FindRecipeQuery(RecipeMap> recipeMap) {
this.recipeMap = recipeMap;
}
// region executors
/**
* @return The first matched recipe, or null if not found.
*/
@Nullable
public GT_Recipe find() {
return findAll().findFirst()
.orElse(null);
}
/**
* @return All the matched recipes in the form of Stream.
*/
public Stream findAll() {
if (items == null) {
items = new ItemStack[0];
}
if (fluids == null) {
fluids = new FluidStack[0];
}
return recipeMap.getBackend()
.matchRecipeStream(
items,
fluids,
specialSlot,
cachedRecipe,
notUnificated,
dontCheckStackSizes,
forCollisionCheck)
.filter(recipe -> voltage * recipeMap.getAmperage() >= recipe.mEUt && filter.test(recipe));
}
/**
* Checks if given inputs conflict with already registered recipes.
*
* @return True if collision is found.
*/
public boolean checkCollision() {
dontCheckStackSizes = true;
forCollisionCheck = true;
return findAll().findAny()
.isPresent();
}
// endregion
// region setters
/**
* @param items Item inputs.
*/
public FindRecipeQuery items(@Nullable ItemStack... items) {
this.items = items;
return this;
}
/**
* @param fluids Fluid inputs.
*/
public FindRecipeQuery fluids(@Nullable FluidStack... fluids) {
this.fluids = fluids;
return this;
}
/**
* @param specialSlot Content of the special slot. Normal recipemaps don't need this, but some do.
* Set {@link RecipeMapBuilder#specialSlotSensitive} to make it actually functional.
* Alternatively overriding {@link RecipeMapBackend#filterFindRecipe} will also work.
*/
public FindRecipeQuery specialSlot(@Nullable ItemStack specialSlot) {
this.specialSlot = specialSlot;
return this;
}
/**
* @param filter Matched recipe will be tested by this function. If it returns false, the query will attempt to
* find next recipe.
*/
public FindRecipeQuery filter(Predicate filter) {
this.filter = filter;
return this;
}
/**
* @param voltage Recipes that exceed this voltage won't match. It will be automatically multiplied by amperage
* of the recipemap.
*/
public FindRecipeQuery voltage(long voltage) {
this.voltage = voltage;
return this;
}
/**
* @param cachedRecipe If this is not null, the query tests it before all other recipes.
*/
public FindRecipeQuery cachedRecipe(@Nullable GT_Recipe cachedRecipe) {
this.cachedRecipe = cachedRecipe;
return this;
}
/**
* @param notUnificated If this is set to true, item inputs will be unificated.
*/
public FindRecipeQuery notUnificated(boolean notUnificated) {
this.notUnificated = notUnificated;
return this;
}
/**
* @param dontCheckStackSizes If this is set to true, the query won't check item count and fluid amount
* for the matched recipe.
*/
public FindRecipeQuery dontCheckStackSizes(boolean dontCheckStackSizes) {
this.dontCheckStackSizes = dontCheckStackSizes;
return this;
}
// endregion
}