package ru.dbotthepony.kstarbound 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.defs.actor.player.RecipeDefinition import java.util.* import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.ExecutorService 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() private val group2recipesInternal = Object2ObjectOpenHashMap>() private val group2recipesBacking = Object2ObjectOpenHashMap>() private val output2recipesInternal = Object2ObjectOpenHashMap>() private val output2recipesBacking = Object2ObjectOpenHashMap>() private val input2recipesInternal = Object2ObjectOpenHashMap>() private val input2recipesBacking = Object2ObjectOpenHashMap>() val recipes: List = Collections.unmodifiableList(recipesInternal) val group2recipes: Map> = Collections.unmodifiableMap(group2recipesBacking) val output2recipes: Map> = Collections.unmodifiableMap(output2recipesBacking) val input2recipes: Map> = Collections.unmodifiableMap(input2recipesBacking) private val backlog = ConcurrentLinkedQueue() private fun add(recipe: Entry) { val value = recipe.value recipesInternal.add(recipe) for (group in value.groups) { group2recipesInternal.computeIfAbsent(group, Object2ObjectFunction { p -> ArrayList(1).also { group2recipesBacking[p as String] = Collections.unmodifiableList(it) } }).add(recipe) } output2recipesInternal.computeIfAbsent(value.output.name, Object2ObjectFunction { p -> ArrayList(1).also { output2recipesBacking[p as String] = Collections.unmodifiableList(it) } }).add(recipe) for (input in value.input) { input2recipesInternal.computeIfAbsent(input.name, Object2ObjectFunction { p -> ArrayList(1).also { input2recipesBacking[p as String] = Collections.unmodifiableList(it) } }).add(recipe) } } fun finishLoad() { var next = backlog.poll() while (next != null) { add(next) next = backlog.poll() } } fun load(fileTree: Map>): List> { val files = fileTree["recipe"] ?: return emptyList() val elements = Starbound.gson.getAdapter(JsonElement::class.java) val recipes = Starbound.gson.getAdapter(RecipeDefinition::class.java) return files.map { listedFile -> Starbound.EXECUTOR.submit { try { val json = elements.read(listedFile.jsonReader()) val value = recipes.fromJsonTree(json) backlog.add(Entry(value, json, listedFile)) } catch (err: Throwable) { LOGGER.error("Loading recipe definition file $listedFile", err) } } } } }