package ru.dbotthepony.mc.otm.compat.curios

import com.google.common.collect.Streams
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import net.neoforged.fml.ModList
import net.neoforged.fml.loading.FMLEnvironment
import net.neoforged.neoforge.network.PacketDistributor
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.render.sprites.MatterySprite
import ru.dbotthepony.mc.otm.client.render.sprites.sprite
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.container.util.awareStream
import ru.dbotthepony.mc.otm.container.util.iterator
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.collect.concatIterators
import ru.dbotthepony.mc.otm.core.collect.emptyIterator
import ru.dbotthepony.mc.otm.menu.PlayerSlot
import top.theillusivec4.curios.api.CuriosApi
import top.theillusivec4.curios.api.CuriosCapability
import top.theillusivec4.curios.api.event.SlotModifiersUpdatedEvent
import top.theillusivec4.curios.common.inventory.CosmeticCurioSlot
import top.theillusivec4.curios.common.inventory.CurioSlot
import top.theillusivec4.curios.common.network.client.CPacketOpenCurios
import top.theillusivec4.curios.common.network.client.CPacketToggleRender
import java.util.stream.Stream
import kotlin.collections.ArrayList

val isCuriosLoaded by lazy {
	ModList.get().isLoaded(CuriosApi.MODID)
}

fun onCuriosSlotModifiersUpdated(event: SlotModifiersUpdatedEvent) {
	check(isCuriosLoaded) { "Curios is not loaded!" }
	event.entity.matteryPlayer?.recreateExoPackMenu()
}

fun openCuriosScreen(carriedStack: ItemStack = ItemStack.EMPTY) {
	if (FMLEnvironment.dist.isClient) PacketDistributor.sendToServer(CPacketOpenCurios(carriedStack))
}

private fun Player.getCuriosSlotsImpl(): List<PlayerSlot<Slot, Slot>> {
	val handler = getCapability(CuriosCapability.INVENTORY) ?: return listOf()

	val result = ArrayList<PlayerSlot<Slot, Slot>>()

	val sortedIdentifiers = ArrayList<String>(handler.curios.keys.size)
	sortedIdentifiers.addAll(handler.curios.keys)

	//if (handler.curios !is LinkedHashMap) {
	//	sortedIdentifiers.sort()
	//}

	for (identifier in sortedIdentifiers) {
		val curio = handler.curios[identifier]!!

		if (curio.isVisible) {
			val stacks = curio.stacks

			for (slot in 0 until stacks.slots) {
				val regular = CurioSlot(this, stacks, slot, identifier, 0, 0, curio.renders, curio.canToggleRendering())

				if (curio.hasCosmetic()) {
					val cosmetic = CosmeticCurioSlot(this, curio.cosmeticStacks, slot, identifier, 0, 0)

					result.add(PlayerSlot(regular, cosmetic))
				} else {
					result.add(PlayerSlot(regular))
				}
			}
		}
	}

	return result
}

/**
 * [Pair]<Regular, Cosmetic>
 */
val Player.curiosSlots: List<PlayerSlot<Slot, Slot>> get() {
	if (!isCuriosLoaded) {
		return listOf()
	}

	return getCuriosSlotsImpl()
}

private fun Player.curiosStreamImpl(includeCosmetics: Boolean): Iterator<ItemStack> {
	val handler = getCapability(CuriosCapability.INVENTORY) ?: return emptyIterator()

	val result = ArrayList<Iterator<ItemStack>>()

	for ((identifier, curio) in handler.curios) {
		result.add(curio.stacks.iterator())

		if (includeCosmetics && curio.hasCosmetic()) {
			result.add(curio.cosmeticStacks.iterator())
		}
	}

	return concatIterators(result)
}

fun Player.curiosStream(includeCosmetics: Boolean = true): Iterator<ItemStack> {
	if (!isCuriosLoaded) {
		return emptyIterator()
	}

	return curiosStreamImpl(includeCosmetics)
}

private fun Player.curiosAwareStreamImpl(includeCosmetics: Boolean): Stream<out AwareItemStack> {
	val handler = getCapability(CuriosCapability.INVENTORY) ?: return Stream.empty()

	val result = ArrayList<Stream<out AwareItemStack>>()

	for ((identifier, curio) in handler.curios) {
		result.add(curio.stacks.awareStream())

		if (includeCosmetics && curio.hasCosmetic()) {
			result.add(curio.cosmeticStacks.awareStream())
		}
	}

	return Streams.concat(*result.toTypedArray())
}

fun Player.curiosAwareStream(includeCosmetics: Boolean = true): Stream<out AwareItemStack> {
	if (!isCuriosLoaded) {
		return Stream.empty()
	}

	return curiosAwareStreamImpl(includeCosmetics)
}

val Slot.isCurioSlot: Boolean get() {
	if (!isCuriosLoaded) {
		return false
	}

	return isCurioSlotImpl
}

private val Slot.isCurioSlotImpl: Boolean get() {
	return this is CurioSlot || this is CosmeticCurioSlot
}

class CurioSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
	screen: S,
	parent: EditablePanel<*>?,
	slot: T,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = SIZE,
	height: Float = SIZE,
) : SlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
	override fun innerRenderTooltips(
		graphics: MGUIGraphics,
		mouseX: Float,
		mouseY: Float,
		partialTick: Float
	): Boolean {
		if (slot.isCurioSlot && !slot.hasItem()) {
			graphics.parent.renderTooltip(
				font,
				Component.literal((slot as CurioSlot).getSlotName()),
				mouseX.toInt(),
				mouseY.toInt()
			)

			return true
		}

		return super.innerRenderTooltips(graphics, mouseX, mouseY, partialTick)
	}
}

class CurioToggleButton<out S : Screen>(
	screen: S,
	parent: EditablePanel<*>?,
	val slot: Slot,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = 8f,
	height: Float = 8f
) : ButtonPanel<S>(screen, parent, x, y, width, height) {
	override val pressedSprite: MatterySprite
		get() = BUTTON_ACTIVE
	override val hoveredSprite: MatterySprite
		get() = BUTTON_ACTIVE
	override val idleSprite: MatterySprite
		get() = BUTTON_INACTIVE
	override val disabledSprite: MatterySprite
		get() = BUTTON_INACTIVE

	override fun onClick(mouseButton: Int) {
		if (!slot.isCurioSlot) return

		if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
			PacketDistributor.sendToServer(CPacketToggleRender((slot as CurioSlot).identifier, slot.slotIndex))
		}
	}

	override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
		if (!slot.isCurioSlot) return

		val color = if (isHovered) RGBAColor.WHITE else RGBAColor.HALF_TRANSPARENT
		if ((slot as CurioSlot).getRenderStatus()) {
			BUTTON_ACTIVE.render(graphics, canvasWidth = width, canvasHeight = height, color = color)
		} else {
			BUTTON_INACTIVE.render(graphics, canvasWidth = width, canvasHeight = height, color = color)
		}
	}

	companion object {
		private val CURIO_INVENTORY = ResourceLocation.fromNamespaceAndPath(CuriosApi.MODID, "textures/gui/curios/inventory.png")

		val BUTTON_ACTIVE = CURIO_INVENTORY.sprite(75f, 0f, 8f, 8f)
		val BUTTON_INACTIVE = CURIO_INVENTORY.sprite(75f + 8f, 0f, 8f, 8f)
	}
}
