package ru.dbotthepony.mc.otm.compat.cos

import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.vertex.PoseStack
import lain.mods.cos.impl.ModObjects
import lain.mods.cos.impl.client.PlayerRenderHandler
import lain.mods.cos.impl.client.gui.GuiCosArmorInventory
import lain.mods.cos.impl.network.packet.PacketSetSkinArmor
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.InventoryMenu
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import net.minecraftforge.fml.ModList
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.MatterySprite
import ru.dbotthepony.mc.otm.client.render.sprite
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.RectangleButtonPanel
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_ACTIVE
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton.Companion.BUTTON_INACTIVE
import ru.dbotthepony.mc.otm.container.awareStream
import ru.dbotthepony.mc.otm.container.stream
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.menu.MatterySlot
import java.util.stream.Stream

val isCosmeticArmorLoaded by lazy {
	ModList.get().isLoaded("cosmeticarmorreworked")
}

val Player.cosmeticArmorSlots: Map<EquipmentSlot, Slot>? get() {
	if (!isCosmeticArmorLoaded) {
		return null
	}

	return cosmeticArmorSlotsImpl
}

private class CosmeticSlot(container: Container, private val slot: EquipmentSlot, private val player: Player) : MatterySlot(container, when (slot) {
	EquipmentSlot.FEET -> 0
	EquipmentSlot.LEGS -> 1
	EquipmentSlot.CHEST -> 2
	EquipmentSlot.HEAD -> 3
	else -> throw IllegalArgumentException()
}) {
	override fun mayPlace(itemStack: ItemStack): Boolean {
		return super.mayPlace(itemStack) && itemStack.canEquip(slot, player)
	}

	override fun getMaxStackSize(): Int {
		return 1
	}

	override fun canTakeItemForPickAll(): Boolean {
		return false
	}

	override fun getNoItemIcon(): com.mojang.datafixers.util.Pair<ResourceLocation, ResourceLocation>? {
		return when (slot) {
			EquipmentSlot.FEET -> com.mojang.datafixers.util.Pair.of(InventoryMenu.BLOCK_ATLAS, InventoryMenu.EMPTY_ARMOR_SLOT_BOOTS)
			EquipmentSlot.LEGS -> com.mojang.datafixers.util.Pair.of(InventoryMenu.BLOCK_ATLAS, InventoryMenu.EMPTY_ARMOR_SLOT_LEGGINGS)
			EquipmentSlot.CHEST -> com.mojang.datafixers.util.Pair.of(InventoryMenu.BLOCK_ATLAS, InventoryMenu.EMPTY_ARMOR_SLOT_CHESTPLATE)
			EquipmentSlot.HEAD -> com.mojang.datafixers.util.Pair.of(InventoryMenu.BLOCK_ATLAS, InventoryMenu.EMPTY_ARMOR_SLOT_HELMET)
			else -> null
		}
	}
}

fun Player.cosmeticArmorStream(): Stream<out ItemStack> {
	if (!isCosmeticArmorLoaded) {
		return Stream.empty()
	}

	return cosmeticArmorStreamImpl()
}

private fun Player.cosmeticArmorStreamImpl(): Stream<out ItemStack> {
	val manager = ModObjects.invMan ?: return Stream.empty()

	val container = if (this !is ServerPlayer) {
		manager.getCosArmorInventoryClient(uuid)
	} else {
		manager.getCosArmorInventory(uuid)
	}

	return (container as Container).stream()
}

fun Player.cosmeticArmorAwareStream(): Stream<out AwareItemStack> {
	if (!isCosmeticArmorLoaded) {
		return Stream.empty()
	}

	return cosmeticArmorAwareStreamImpl()
}

private fun Player.cosmeticArmorAwareStreamImpl(): Stream<out AwareItemStack> {
	val manager = ModObjects.invMan ?: return Stream.empty()

	val container = if (this !is ServerPlayer) {
		manager.getCosArmorInventoryClient(uuid)
	} else {
		manager.getCosArmorInventory(uuid)
	}

	return (container as Container).awareStream()
}

private val Player.cosmeticArmorSlotsImpl: Map<EquipmentSlot, Slot>? get() {
	val manager = ModObjects.invMan ?: return null

	val container = if (this !is ServerPlayer) {
		manager.getCosArmorInventoryClient(uuid)
	} else {
		manager.getCosArmorInventory(uuid)
	}

	return mapOf(
		EquipmentSlot.HEAD to CosmeticSlot(container, EquipmentSlot.HEAD, this),
		EquipmentSlot.CHEST to CosmeticSlot(container, EquipmentSlot.CHEST, this),
		EquipmentSlot.LEGS to CosmeticSlot(container, EquipmentSlot.LEGS, this),
		EquipmentSlot.FEET to CosmeticSlot(container, EquipmentSlot.FEET, this),
	)
}

val Screen.isCosmeticArmorScreen: Boolean get() {
	if (!isCosmeticArmorLoaded) {
		return false
	}

	return isCosmeticArmorScreenImpl
}

private val Screen.isCosmeticArmorScreenImpl: Boolean get() {
	return this is GuiCosArmorInventory
}

class CosmeticToggleButton<out S : Screen>(
	screen: S,
	parent: EditablePanel<*>?,
	index: EquipmentSlot,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = 5f,
	height: Float = 5f
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height) {
	override val PRESSED: MatterySprite
		get() = BUTTON_ACTIVE
	override val HOVERED: MatterySprite
		get() = BUTTON_ACTIVE
	override val IDLE: MatterySprite
		get() = BUTTON_INACTIVE
	override val DISABLED: MatterySprite
		get() = BUTTON_INACTIVE

	private val index = when (index) {
		EquipmentSlot.FEET -> 0
		EquipmentSlot.LEGS -> 1
		EquipmentSlot.CHEST -> 2
		EquipmentSlot.HEAD -> 3
		else -> throw IllegalArgumentException()
	}

	override fun onClick(clickButton: Int) {
		if (clickButton == InputConstants.MOUSE_BUTTON_LEFT) {
			val inv = ModObjects.invMan.getCosArmorInventoryClient(minecraft.player?.uuid ?: throw ConcurrentModificationException())
			inv.setSkinArmor(index, !inv.isSkinArmor(index))
			ModObjects.network.sendToServer(PacketSetSkinArmor(index, inv.isSkinArmor(index)))
		}
	}

	override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
		val inv = ModObjects.invMan.getCosArmorInventoryClient(minecraft.player?.uuid ?: throw ConcurrentModificationException())

		if (inv.isSkinArmor(index)) {
			BUTTON_ACTIVE.render(graphics, width = width, height = height)
		} else {
			BUTTON_INACTIVE.render(graphics, width = width, height = height)
		}
	}

	companion object {
		val BUTTON_INACTIVE = GuiCosArmorInventory.TEXTURE.sprite(0f, 176f, 5f, 5f)
		val BUTTON_ACTIVE = GuiCosArmorInventory.TEXTURE.sprite(5f, 176f, 5f, 5f)
	}
}

class CosmeticToggleRenderButton<out S : Screen>(
	screen: S,
	parent: EditablePanel<*>?,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = 5f,
	height: Float = 5f
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height) {
	override val PRESSED: MatterySprite
		get() = BUTTON_ACTIVE
	override val HOVERED: MatterySprite
		get() = BUTTON_ACTIVE
	override val IDLE: MatterySprite
		get() = BUTTON_INACTIVE
	override val DISABLED: MatterySprite
		get() = BUTTON_INACTIVE

	override fun onClick(clickButton: Int) {
		if (clickButton == InputConstants.MOUSE_BUTTON_LEFT) {
			PlayerRenderHandler.Disabled = !PlayerRenderHandler.Disabled
		}
	}

	override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
		if (PlayerRenderHandler.Disabled) {
			BUTTON_ACTIVE.render(graphics, width = width, height = height)
		} else {
			BUTTON_INACTIVE.render(graphics, width = width, height = height)
		}
	}
}
