package ru.dbotthepony.mc.otm.android

import com.google.gson.JsonObject
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import net.minecraft.data.CachedOutput
import net.minecraft.data.DataProvider
import net.minecraft.data.PackOutput
import net.minecraft.resources.ResourceLocation
import net.minecraftforge.data.event.GatherDataEvent
import ru.dbotthepony.mc.otm.core.toJson
import ru.dbotthepony.mc.otm.core.toJsonStrict
import ru.dbotthepony.mc.otm.core.util.WriteOnce
import java.util.Collections
import java.util.LinkedList
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer

@Suppress("unused")
open class AndroidResearchDataProvider() : DataProvider {
	var pathProvider: PackOutput.PathProvider by WriteOnce("You need to call bindPackOutput before registering this data provider")
		private set

	constructor(output: PackOutput) : this() {
		bindPackOutput(output)
	}

	constructor(event: GatherDataEvent) : this(event.generator.packOutput)

	fun bindPackOutput(output: PackOutput) {
		pathProvider = output.createPathProvider(PackOutput.Target.DATA_PACK, AndroidResearchManager.DIRECTORY)
	}

	protected val callbacks = LinkedList<(Consumer<AndroidResearchType>) -> Unit>()

	private val generated = LinkedList<AndroidResearchType>()
	val generatedView: List<AndroidResearchType> = Collections.unmodifiableList(generated)

	/**
	 * override this
	 */
	protected open fun addEverything(serializer: Consumer<AndroidResearchType>) {
		for (callback in callbacks) {
			callback.invoke(serializer)
		}
	}

	/**
	 * or call this
	 */
	fun exec(callback: (Consumer<AndroidResearchType>) -> Unit): AndroidResearchDataProvider {
		callbacks.add(callback)
		return this
	}

	final override fun run(output: CachedOutput): CompletableFuture<*> {
		val set = ObjectArraySet<ResourceLocation>()
		val added = LinkedList<AndroidResearchType>()
		val futures = ArrayList<CompletableFuture<*>>()

		addEverything {
			if (set.add(it.id)) {
				futures.add(DataProvider.saveStable(output, AndroidResearchType.CODEC.toJsonStrict(it).also { (it as JsonObject).remove("id") }, pathProvider.json(it.id)))
				AndroidResearchManager.put(it)
				added.add(it)
			} else {
				throw IllegalStateException("Duplicate android research with ID ${it.id}")
			}
		}

		for (value in added) {
			value.validate()
			generated.add(value)
		}

		return CompletableFuture.allOf(*futures.toTypedArray())
	}

	override fun getName(): String {
		return "Android Research"
	}
}
