package ru.dbotthepony.mc.otm.recipe

import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.*
import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.network.StreamCodecs
import ru.dbotthepony.mc.otm.network.streamCodec
import ru.dbotthepony.mc.otm.registry.game.MItems
import ru.dbotthepony.mc.otm.registry.game.MRecipes

abstract class MatteryCookingRecipe(
	val input: Ingredient,
	val output: Ingredient,
	val count: Int = 1,
	val workTime: Int = 200,
	val experience: FloatProvider = ConstantFloat.ZERO
) : Recipe<SingleRecipeInput> {
	override fun matches(container: SingleRecipeInput, level: Level): Boolean {
		if (isIncomplete)
			return false

		return input.test(container.item())
	}

	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.hasNoItems() || output.hasNoItems()

	override fun assemble(container: SingleRecipeInput, registry: HolderLookup.Provider): ItemStack = outputStack.copy()

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

	override fun getResultItem(registry: HolderLookup.Provider): ItemStack = outputStack
}

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

	companion object : RecipeSerializer<MicrowaveRecipe> {
		private val codec = RecordCodecBuilder.mapCodec {
			it.group(
				Ingredient.CODEC_NONEMPTY.fieldOf("input").forGetter(MicrowaveRecipe::input),
				Ingredient.CODEC_NONEMPTY.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, ::MicrowaveRecipe)
		}

		private val streamCodec = StreamCodec.composite(
			Ingredient.CONTENTS_STREAM_CODEC, MicrowaveRecipe::input,
			Ingredient.CONTENTS_STREAM_CODEC, MicrowaveRecipe::output,
			StreamCodecs.INT, MicrowaveRecipe::count,
			StreamCodecs.INT, MicrowaveRecipe::workTime,
			FloatProvider.CODEC.streamCodec(), MicrowaveRecipe::experience,
			::MicrowaveRecipe,
		)

		override fun codec(): MapCodec<MicrowaveRecipe> {
			return codec
		}

		override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, MicrowaveRecipe> {
			return streamCodec
		}
	}
}
