package reborncore.common.crafting;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.tag.ItemTags;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import org.apache.commons.lang3.Validate;

import java.util.HashMap;
import java.util.function.Function;

public final class ConditionManager {

	private static final HashMap<Identifier, RecipeCondition<?>> RECIPE_CONDITIONS = new HashMap<>();
	private static final HashMap<Identifier, Class<?>> RECIPE_CONDITION_TYPES = new HashMap<>();

	static {
		//Only loads the recipe in a development env
		register("development", Boolean.class, (bool) -> bool == FabricLoader.getInstance().isDevelopmentEnvironment());

		//Only loads the recipe when the item is registered
		register("item", Identifier.class, Registry.ITEM::containsId);

		//Only loads the recipe when the fluid is registered
		register("fluid", Identifier.class, Registry.FLUID::containsId);

		//Only loads the recipe when the tag is loaded
		register("tag", Identifier.class, s -> ItemTags.getContainer().getEntries().containsKey(s));

		//Only load the recipe if the provided mod is loaded
		register("mod", String.class, s -> FabricLoader.getInstance().isModLoaded(s));
	}

	public static <T> void register(String name, Class<T> type, RecipeCondition<T> recipeCondition){
		register(new Identifier("reborncore", name), type, recipeCondition);
	}

	public static <T> void register(Identifier identifier, Class<T> type, RecipeCondition<T> recipeCondition){
		Validate.isTrue(!RECIPE_CONDITIONS.containsKey(identifier), "Recipe condition already registered");
		RECIPE_CONDITIONS.put(identifier, recipeCondition);
		RECIPE_CONDITION_TYPES.put(identifier, type);
	}

	public static RecipeCondition<?> getRecipeCondition(Identifier identifier){
		RecipeCondition<?> condition = RECIPE_CONDITIONS.get(identifier);
		if(condition == null){
			throw new UnsupportedOperationException("Could not find recipe condition for " + identifier.toString());
		}
		return condition;
	}

	public static boolean shouldLoadRecipe(JsonObject jsonObject){
		if(!jsonObject.has("conditions")) return true;
		return jsonObject.get("conditions").getAsJsonObject().entrySet().stream()
			.allMatch(entry -> shouldLoad(entry.getKey(), entry.getValue()));
	}

	public static boolean shouldLoad(String ident, JsonElement jsonElement){
		Identifier identifier = parseIdent(ident);
		RecipeCondition<?> recipeCondition = getRecipeCondition(identifier);
		Class<?> type = RECIPE_CONDITION_TYPES.get(identifier);
		return shouldLoad(type, jsonElement, recipeCondition);
	}

	@SuppressWarnings("unchecked")
	private static boolean shouldLoad(Class type, JsonElement jsonElement, RecipeCondition recipeCondition){
		Object val = TypeHelper.getValue(type, jsonElement);
		return recipeCondition.shouldLoad(val);
	}

	private static Identifier parseIdent(String string) {
		if(string.contains(":")){
			return new Identifier(string);
		}
		return new Identifier("reborncore", string);
	}

	@FunctionalInterface
	public interface RecipeCondition<T> {
		boolean shouldLoad(T t);
	}

	private static class TypeHelper {

		private static final HashMap<Class<?>, Function<JsonElement, ?>> FUNCTIONS = new HashMap<>();

		static {
			register(String.class, JsonElement::getAsString);
			register(Boolean.class, JsonElement::getAsBoolean);

			register(Identifier.class, element -> new Identifier(element.getAsString()));
		}

		private static <T> void register(Class<T> type, Function<JsonElement, T> function){
			Validate.isTrue(!FUNCTIONS.containsKey(type), "Function for this class is already registered");
			FUNCTIONS.put(type, function);
		}

		public static <T> T getValue(Class<T> type, JsonElement jsonElement){
			Validate.isTrue(FUNCTIONS.containsKey(type), "Function for this class could not be found");
			//noinspection unchecked
			return (T) FUNCTIONS.get(type).apply(jsonElement);
		}

	}
}

@@ -30,16 +30,79 @@
 fting;%0A%0A
+import java.util.HashMap;%0Aimport java.util.function.Function;%0A%0A
 import c
@@ -179,40 +179,30 @@
 net.
-fabricmc.loader.api.FabricLoader
+minecraft.tag.ItemTags
 ;%0Aim
@@ -216,36 +216,39 @@
 t.minecraft.
-tag.ItemTags
+util.Identifier
 ;%0Aimport net
@@ -259,34 +259,41 @@
 ecraft.util.
-Identifier
+registry.Registry
 ;%0Aimport net
@@ -309,32 +309,38 @@
 t.util.registry.
+Simple
 Registry;%0Aimport
@@ -387,61 +387,44 @@
 ort 
-java.util.HashMap;%0Aimport java.util.function.Function
+net.fabricmc.loader.api.FabricLoader
 ;%0A%0Ap
@@ -923,33 +923,51 @@
 ss, 
-Registry.ITEM::containsId
+(id) -%3E registryContains(Registry.ITEM, id)
 );%0A%0A
@@ -1063,34 +1063,52 @@
 ss, 
-Registry.FLUID::containsId
+(id) -%3E registryContains(Registry.FLUID, id)
 );%0A%0A
@@ -1384,32 +1384,163 @@
 Loaded(s));%0A%09%7D%0A%0A
+%09private static boolean registryContains(SimpleRegistry%3C?%3E registry, Identifier ident) %7B%0A%09%09return registry.get(ident) != null;%0A%09%7D%0A%0A
 %09public static %3C

Fix server issues, thanks mojang for making containsId client side only...