package ru.dbotthepony.mc.otm.recipe

import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.NonNullList
import net.minecraft.core.RegistryAccess
import net.minecraft.data.recipes.FinishedRecipe
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.world.Container
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.crafting.*
import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.core.isActuallyEmpty
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.data.Codec2RecipeSerializer
import ru.dbotthepony.mc.otm.data.minRange
import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRecipes

abstract class MatteryCookingRecipe(
	private val id: ResourceLocation,
	val input: Ingredient,
	val output: Ingredient,
	val count: Int = 1,
	val workTime: Int = 200,
	val experience: FloatProvider = ConstantFloat.ZERO
) : Recipe<Container> {
	override fun getId(): ResourceLocation {
		return id
	}

	override fun matches(container: Container, level: Level): Boolean {
		if (isIncomplete)
			return false

		return input.test(container[0])
	}

	fun matches(container: Container, slot: Int): Boolean {
		if (isIncomplete)
			return false

		return input.test(container[slot])
	}

	private val outputStack: ItemStack by lazy {
		if (isIncomplete) {
			ItemStack.EMPTY
		} else {
			val items = output.items
			val bestMatch = items.firstOrNull { it.item.registryName?.namespace == OverdriveThatMatters.MOD_ID } ?: items[0]
			bestMatch.copy().also { it.count = this.count }
		}
	}

	override fun getIngredients(): NonNullList<Ingredient> {
		if (isIncomplete)
			return super.getIngredients()

		return NonNullList.of(Ingredient.EMPTY, input)
	}

	override fun isIncomplete(): Boolean = input.isActuallyEmpty || output.isActuallyEmpty

	override fun assemble(container: Container, registry: RegistryAccess): ItemStack = outputStack.copy()

	override fun canCraftInDimensions(width: Int, height: Int): Boolean = true

	override fun getResultItem(registry: RegistryAccess): ItemStack = outputStack

	abstract fun toFinished(): FinishedRecipe
}

class MicrowaveRecipe(
	id: ResourceLocation,
	input: Ingredient,
	output: Ingredient,
	count: Int = 1,
	workTime: Int = 200,
	experience: FloatProvider = ConstantFloat.ZERO
) : MatteryCookingRecipe(id, input, output, count, workTime, experience) {
	override fun getType(): RecipeType<*> = MRecipes.MICROWAVE
	override fun getToastSymbol(): ItemStack = ItemStack(MItems.POWERED_SMOKER[null]!!)
	override fun getSerializer(): RecipeSerializer<*> = SERIALIZER
	override fun toFinished(): FinishedRecipe = SERIALIZER.toFinished(this)

	companion object {
		val SERIALIZER = Codec2RecipeSerializer<MicrowaveRecipe> { context ->
			RecordCodecBuilder.create {
				it.group(
					context.ingredients.fieldOf("input").forGetter(MicrowaveRecipe::input),
					context.ingredients.fieldOf("output").forGetter(MicrowaveRecipe::output),
					Codec.INT.minRange(1).optionalFieldOf("count", 1).forGetter(MicrowaveRecipe::count),
					Codec.INT.minRange(0).optionalFieldOf("workTime", 200).forGetter(MicrowaveRecipe::workTime),
					FloatProvider.CODEC.optionalFieldOf("experience", ConstantFloat.ZERO).forGetter(MicrowaveRecipe::experience)
				).apply(it) { a, b, c, d, e -> MicrowaveRecipe(context.id, a, b, c, d, e) }
			}
		}
	}
}
