package moe.nea.firmament.rei

import me.shedaniel.rei.api.client.plugins.REIClientPlugin
import me.shedaniel.rei.api.client.registry.category.CategoryRegistry
import me.shedaniel.rei.api.client.registry.display.DisplayRegistry
import me.shedaniel.rei.api.client.registry.entry.CollapsibleEntryRegistry
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry
import me.shedaniel.rei.api.client.registry.screen.ExclusionZones
import me.shedaniel.rei.api.client.registry.screen.OverlayDecider
import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry
import me.shedaniel.rei.api.client.registry.transfer.TransferHandler
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry
import me.shedaniel.rei.api.common.entry.EntryStack
import me.shedaniel.rei.api.common.entry.type.EntryTypeRegistry
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.item.ItemStack
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import net.minecraft.util.Identifier
import moe.nea.firmament.events.HandledScreenPushREIEvent
import moe.nea.firmament.features.inventory.CraftingOverlay
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen
import moe.nea.firmament.rei.recipes.SBCraftingRecipe
import moe.nea.firmament.rei.recipes.SBEssenceUpgradeRecipe
import moe.nea.firmament.rei.recipes.SBForgeRecipe
import moe.nea.firmament.rei.recipes.SBKatRecipe
import moe.nea.firmament.rei.recipes.SBMobDropRecipe
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.ScreenUtil
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.guessRecipeId
import moe.nea.firmament.util.skyblockId
import moe.nea.firmament.util.unformattedString


class FirmamentReiPlugin : REIClientPlugin {

	companion object {
		fun EntryStack<SBItemStack>.asItemEntry(): EntryStack<ItemStack> {
			return EntryStack.of(VanillaEntryTypes.ITEM, value.asImmutableItemStack())
		}

		val SKYBLOCK_ITEM_TYPE_ID = Identifier.of("firmament", "skyblockitems")
	}

	override fun registerTransferHandlers(registry: TransferHandlerRegistry) {
		registry.register(TransferHandler { context ->
			val screen = context.containerScreen
			val display = context.display
			if (display !is SBCraftingRecipe) return@TransferHandler TransferHandler.Result.createNotApplicable()
			val neuItem = RepoManager.getNEUItem(SkyblockId(display.neuRecipe.output.itemId))
				?: error("Could not find neu item ${display.neuRecipe.output.itemId} which is used in a recipe output")
			val useSuperCraft = context.isStackedCrafting || RepoManager.Config.alwaysSuperCraft
			if (neuItem.isVanilla && useSuperCraft) return@TransferHandler TransferHandler.Result.createFailed(Text.translatable(
				"firmament.recipe.novanilla"))
			var shouldReturn = true
			if (context.isActuallyCrafting && !useSuperCraft) {
				if (screen !is GenericContainerScreen || screen.title?.unformattedString != CraftingOverlay.CRAFTING_SCREEN_NAME) {
					MC.sendCommand("craft")
					shouldReturn = false
				}
				CraftingOverlay.setOverlay(screen as? GenericContainerScreen, display)
			}
			if (context.isActuallyCrafting && useSuperCraft) {
				shouldReturn = false
				MC.sendCommand("viewrecipe ${neuItem.guessRecipeId()}")
			}
			return@TransferHandler TransferHandler.Result.createSuccessful().blocksFurtherHandling(shouldReturn)
		})
	}

	override fun registerEntryTypes(registry: EntryTypeRegistry) {
		registry.register(SKYBLOCK_ITEM_TYPE_ID, SBItemEntryDefinition)
	}

	override fun registerCategories(registry: CategoryRegistry) {
		registry.add(SBCraftingRecipe.Category)
		registry.add(SBForgeRecipe.Category)
		registry.add(SBMobDropRecipe.Category)
		registry.add(SBKatRecipe.Category)
		registry.add(SBEssenceUpgradeRecipe.Category)
	}

	override fun registerExclusionZones(zones: ExclusionZones) {
		zones.register(HandledScreen::class.java) { HandledScreenPushREIEvent.publish(HandledScreenPushREIEvent(it)).rectangles }
		zones.register(StorageOverlayScreen::class.java) { it.getBounds() }
	}

	override fun registerDisplays(registry: DisplayRegistry) {
		registry.registerDisplayGenerator(
			SBCraftingRecipe.Category.catIdentifier,
			SkyblockCraftingRecipeDynamicGenerator)
		registry.registerDisplayGenerator(
			SBForgeRecipe.Category.categoryIdentifier,
			SkyblockForgeRecipeDynamicGenerator)
		registry.registerDisplayGenerator(
			SBMobDropRecipe.Category.categoryIdentifier,
			SkyblockMobDropRecipeDynamicGenerator)
		registry.registerDisplayGenerator(
			SBKatRecipe.Category.categoryIdentifier,
			SkyblockKatRecipeDynamicGenerator)
		registry.registerDisplayGenerator(
			SBEssenceUpgradeRecipe.Category.categoryIdentifier,
			SkyblockEssenceRecipeDynamicGenerator
		)
	}

	override fun registerCollapsibleEntries(registry: CollapsibleEntryRegistry) {
		if (!RepoManager.Config.disableItemGroups)
			RepoManager.neuRepo.constants.parents.parents
				.forEach { (parent, children) ->
					registry.group(
						SkyblockId(parent).identifier,
						Text.literal(RepoManager.getNEUItem(SkyblockId(parent))?.displayName ?: parent),
						(children + parent).map { SBItemEntryDefinition.getEntry(SkyblockId(it)) })
				}
	}

	override fun registerScreens(registry: ScreenRegistry) {
		registry.registerDecider(object : OverlayDecider {
			override fun <R : Screen?> isHandingScreen(screen: Class<R>?): Boolean {
				return screen == StorageOverlayScreen::class.java
			}

			override fun <R : Screen?> shouldScreenBeOverlaid(screen: R): ActionResult {
				return ActionResult.SUCCESS
			}
		})
		registry.registerFocusedStack(SkyblockItemIdFocusedStackProvider)
	}

	override fun registerEntries(registry: EntryRegistry) {
		registry.removeEntryIf { true }
		RepoManager.neuRepo.items?.items?.values?.forEach { neuItem ->
			registry.addEntry(SBItemEntryDefinition.getEntry(neuItem.skyblockId))
		}
	}
}