package ru.dbotthepony.kstarbound.item import com.google.gson.JsonElement import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import org.apache.logging.log4j.LogManager import ru.dbotthepony.kstarbound.IStarboundFile import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.defs.actor.player.RecipeDefinition import ru.dbotthepony.kstarbound.json.JsonPatch import java.util.* import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.Future import kotlin.collections.ArrayList object RecipeRegistry { private val LOGGER = LogManager.getLogger() data class Entry(val value: RecipeDefinition, val json: JsonElement, val file: IStarboundFile) private val recipesInternal = ArrayList<Entry>() private val group2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>() private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<Entry>>() private val output2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>() private val output2recipesBacking = Object2ObjectOpenHashMap<String, List<Entry>>() private val input2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>() private val input2recipesBacking = Object2ObjectOpenHashMap<String, List<Entry>>() val recipes: List<Entry> = Collections.unmodifiableList(recipesInternal) val group2recipes: Map<String, List<Entry>> = Collections.unmodifiableMap(group2recipesBacking) val output2recipes: Map<String, List<Entry>> = Collections.unmodifiableMap(output2recipesBacking) val input2recipes: Map<String, List<Entry>> = Collections.unmodifiableMap(input2recipesBacking) private val tasks = ConcurrentLinkedQueue<Entry>() private fun add(recipe: Entry) { val value = recipe.value recipesInternal.add(recipe) for (group in value.groups) { group2recipesInternal.computeIfAbsent(group, Object2ObjectFunction { p -> ArrayList<Entry>(1).also { group2recipesBacking[p as String] = Collections.unmodifiableList(it) } }).add(recipe) } output2recipesInternal.computeIfAbsent(value.output.name, Object2ObjectFunction { p -> ArrayList<Entry>(1).also { output2recipesBacking[p as String] = Collections.unmodifiableList(it) } }).add(recipe) for (input in value.input) { input2recipesInternal.computeIfAbsent(input.name, Object2ObjectFunction { p -> ArrayList<Entry>(1).also { input2recipesBacking[p as String] = Collections.unmodifiableList(it) } }).add(recipe) } } fun finishLoad() { tasks.forEach { add(it) } tasks.clear() } fun load(fileTree: Map<String, Collection<IStarboundFile>>, patchTree: Map<String, List<IStarboundFile>>): List<Future<*>> { val files = fileTree["recipe"] ?: return emptyList() val recipes by lazy { Starbound.gson.getAdapter(RecipeDefinition::class.java) } return files.map { listedFile -> Starbound.EXECUTOR.submit { try { val json = JsonPatch.apply(Starbound.ELEMENTS_ADAPTER.read(listedFile.jsonReader()), patchTree[listedFile.computeFullPath()]) val value = recipes.fromJsonTree(json) tasks.add(Entry(value, json, listedFile)) } catch (err: Throwable) { LOGGER.error("Loading recipe definition file $listedFile", err) } } } } }