package ru.dbotthepony.mc.otm.compat.jei

import it.unimi.dsi.fastutil.ints.Int2IntArrayMap
import it.unimi.dsi.fastutil.ints.IntArraySet
import mezz.jei.api.constants.RecipeTypes
import mezz.jei.api.gui.ingredient.IRecipeSlotView
import mezz.jei.api.gui.ingredient.IRecipeSlotsView
import mezz.jei.api.recipe.RecipeIngredientRole
import mezz.jei.api.recipe.transfer.IRecipeTransferError
import mezz.jei.api.recipe.transfer.IRecipeTransferHandler
import mezz.jei.api.recipe.transfer.IRecipeTransferHandlerHelper
import mezz.jei.api.recipe.transfer.IRecipeTransferInfo
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.crafting.CraftingRecipe
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.toImmutableList
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
import java.util.*
import kotlin.collections.ArrayList

class ExopackInventoryTransferHandler(private val helper: IRecipeTransferHandlerHelper) : IRecipeTransferHandler<ExopackInventoryMenu, CraftingRecipe> {
	private val transfer = helper.createUnregisteredRecipeTransferHandler(object : IRecipeTransferInfo<ExopackInventoryMenu, CraftingRecipe> {
		override fun getContainerClass(): Class<out ExopackInventoryMenu> {
			return ExopackInventoryMenu::class.java
		}

		override fun getMenuType(): Optional<MenuType<ExopackInventoryMenu>> {
			return Optional.empty()
		}

		override fun getRecipeType(): mezz.jei.api.recipe.RecipeType<CraftingRecipe> {
			return RecipeTypes.CRAFTING
		}

		override fun canHandle(container: ExopackInventoryMenu, recipe: CraftingRecipe): Boolean {
			return true
		}

		override fun getRecipeSlots(
			container: ExopackInventoryMenu,
			recipe: CraftingRecipe
		): List<Slot> {
			return container.craftingSlots
		}

		override fun getInventorySlots(
			container: ExopackInventoryMenu,
			recipe: CraftingRecipe
		): List<Slot> {
			return container.playerInventorySlots
		}
	})

	override fun getContainerClass(): Class<out ExopackInventoryMenu> {
		return ExopackInventoryMenu::class.java
	}

	override fun getMenuType(): Optional<MenuType<ExopackInventoryMenu>> {
		return Optional.empty()
	}

	override fun getRecipeType(): mezz.jei.api.recipe.RecipeType<CraftingRecipe> {
		return RecipeTypes.CRAFTING
	}

	private val validSlots = IntArraySet().also {
		it.add(0)
		it.add(1)
		it.add(3)
		it.add(4)
	}

	private val directMap = Int2IntArrayMap().also {
		for (i in 0 .. 8) {
			it.put(i, i)
		}
	}

	private val smallMap = Int2IntArrayMap().also {
		var i = 0
		it.put(0, i++)
		it.put(1, i++)
		it.put(3, i++)
		it.put(4, i)
	}

	override fun transferRecipe(
		container: ExopackInventoryMenu,
		recipe: CraftingRecipe,
		recipeSlots: IRecipeSlotsView,
		player: Player,
		maxTransfer: Boolean,
		doTransfer: Boolean
	): IRecipeTransferError? {
		val inputs = recipeSlots.getSlotViews(RecipeIngredientRole.INPUT)

		if (inputs.size != 9) {
			return IRecipeTransferError { IRecipeTransferError.Type.INTERNAL }
		}

		if (container.craftingSlots.size != 9) {
			for ((i, ingredient) in inputs.withIndex()) {
				if (!ingredient.isEmpty && i !in validSlots) {
					return helper.createUserErrorWithTooltip(TranslatableComponent("jei.tooltip.error.recipe.transfer.too.large.player.inventory"))
				}
			}

			val filteredInputs = ArrayList<IRecipeSlotView>()

			for ((i, ingredient) in inputs.withIndex()) {
				if (i in validSlots) {
					filteredInputs.add(ingredient)
				}
			}

			val outputs = recipeSlots.getSlotViews(RecipeIngredientRole.OUTPUT).toImmutableList()
			val catalysts = recipeSlots.getSlotViews(RecipeIngredientRole.CATALYST).toImmutableList()
			val render = recipeSlots.getSlotViews(RecipeIngredientRole.RENDER_ONLY).toImmutableList()

			val combine = ArrayList<IRecipeSlotView>(filteredInputs.size + outputs.size + render.size)

			combine.addAll(filteredInputs)
			combine.addAll(outputs)
			combine.addAll(render)

			val combined = combine.toImmutableList()

			val newView = object : IRecipeSlotsView {
				override fun getSlotViews(): List<IRecipeSlotView> {
					return combined
				}

				override fun getSlotViews(role: RecipeIngredientRole): List<IRecipeSlotView> {
					return when (role) {
						RecipeIngredientRole.INPUT -> filteredInputs
						RecipeIngredientRole.OUTPUT -> outputs
						RecipeIngredientRole.CATALYST -> catalysts
						RecipeIngredientRole.RENDER_ONLY -> render
					}
				}

				override fun findSlotByName(slotName: String): Optional<IRecipeSlotView> {
					return combined.stream().filter { it.slotName.orElse(null) == slotName }.findAny()
				}
			}

			return this.transfer.transferRecipe(container, recipe, newView, player, maxTransfer, doTransfer)
		}

		return this.transfer.transferRecipe(container, recipe, recipeSlots, player, maxTransfer, doTransfer)
	}
}
