package ru.dbotthepony.mc.otm.data

import com.google.gson.JsonArray
import net.minecraft.data.CachedOutput
import net.minecraft.data.DataProvider
import net.minecraft.data.PackOutput
import net.minecraft.tags.TagKey
import net.minecraft.world.level.block.Block
import net.neoforged.neoforge.data.event.GatherDataEvent
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.toJsonStrict
import ru.dbotthepony.mc.otm.core.util.WriteOnce
import ru.dbotthepony.mc.otm.matter.AbstractRegistryAction
import ru.dbotthepony.mc.otm.matter.MatterManager
import java.util.concurrent.CompletableFuture

open class FlywheelMaterialDataProvider(val modid: String, val location: String? = null) : DataProvider {
	private val entries = ArrayList<FlywheelMaterials.Entry>()

	var pathProvider: PackOutput.PathProvider by WriteOnce("You need to call bindPackOutput before registering this data provider")
		private set

	constructor(output: PackOutput, modid: String, location: String? = null) : this(modid, location) {
		bindPackOutput(output)
	}

	constructor(event: GatherDataEvent, location: String? = null) : this(event.generator.packOutput, event.modContainer.namespace, location)

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

	@JvmOverloads
	fun add(block: Block, storage: Decimal, priority: Int = 0, receiveEfficiency: Decimal = Decimal.ONE, momentumLossSpeed: Decimal = Decimal.ONE) {
		entries.add(FlywheelMaterials.Entry(block, storage, priority, receiveEfficiency, momentumLossSpeed))
	}

	@JvmOverloads
	fun add(block: TagKey<Block>, storage: Decimal, priority: Int = 0, receiveEfficiency: Decimal = Decimal.ONE, momentumLossSpeed: Decimal = Decimal.ONE) {
		entries.add(FlywheelMaterials.Entry(block, storage, priority, receiveEfficiency, momentumLossSpeed))
	}

	@JvmName("addBlocks")
	@JvmOverloads
	fun add(blocks: Collection<Block>, storage: Decimal, priority: Int = 0, receiveEfficiency: Decimal = Decimal.ONE, momentumLossSpeed: Decimal = Decimal.ONE) {
		for (block in blocks)
			entries.add(FlywheelMaterials.Entry(block, storage, priority, receiveEfficiency, momentumLossSpeed))
	}

	@JvmName("addBlocks")
	@JvmOverloads
	fun add(blocks: Array<out Block>, storage: Decimal, priority: Int = 0, receiveEfficiency: Decimal = Decimal.ONE, momentumLossSpeed: Decimal = Decimal.ONE) {
		for (block in blocks)
			entries.add(FlywheelMaterials.Entry(block, storage, priority, receiveEfficiency, momentumLossSpeed))
	}

	@JvmName("addTags")
	@JvmOverloads
	fun add(blocks: Array<out TagKey<Block>>, storage: Decimal, priority: Int = 0, receiveEfficiency: Decimal = Decimal.ONE, momentumLossSpeed: Decimal = Decimal.ONE) {
		for (block in blocks)
			entries.add(FlywheelMaterials.Entry(block, storage, priority, receiveEfficiency, momentumLossSpeed))
	}

	@JvmName("addBlocks")
	fun add(storage: Decimal, vararg blocks: Block) {
		return add(blocks, storage)
	}

	@JvmName("addBlocks")
	fun add(storage: Decimal, priority: Int, vararg blocks: Block) {
		return add(blocks, storage, priority)
	}

	@JvmName("addTags")
	fun add(storage: Decimal, vararg blocks: TagKey<Block>) {
		return add(blocks, storage)
	}

	@JvmName("addTags")
	fun add(storage: Decimal, priority: Int, vararg blocks: TagKey<Block>) {
		return add(blocks, storage, priority)
	}

	fun add(entry: FlywheelMaterials.Entry) {
		entries.add(entry)
	}

	override fun run(output: CachedOutput): CompletableFuture<*> {
		if (location == null) {
			val futures = ArrayList<CompletableFuture<*>>()

			for (entry in entries) {
				val path = ResourceLocation(modid, entry.id.map({ "tag/${it.location.namespace}/${it.location.path}" }, { "block/${it.registryName!!.namespace}/${it.registryName!!.path}" }))
				futures.add(DataProvider.saveStable(output, FlywheelMaterials.CODEC.toJsonStrict(entry), pathProvider.json(path)))
			}

			return CompletableFuture.allOf(*futures.toTypedArray())
		} else {
			val result = JsonArray()

			for (entry in entries)
				result.add(FlywheelMaterials.CODEC.toJsonStrict(entry))

			return DataProvider.saveStable(output, result, pathProvider.json(ResourceLocation(modid, location)))
		}
	}

	override fun getName(): String {
		return "Flywheel Materials for $modid"
	}
}
