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

import com.google.common.collect.Streams
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import net.minecraftforge.api.distmarker.Dist
import net.minecraftforge.fml.ModList
import net.minecraftforge.fml.loading.FMLEnvironment
import net.minecraftforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matteryPlayer
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.core.orNull
import ru.dbotthepony.mc.otm.menu.PlayerSlot
import top.theillusivec4.curios.api.CuriosApi
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.NetworkHandler
import top.theillusivec4.curios.common.network.client.CPacketOpenCurios
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) NetworkHandler.INSTANCE.send(PacketDistributor.SERVER.noArg(), CPacketOpenCurios(carriedStack))
}

private fun Player.getCuriosSlotsImpl(): List<PlayerSlot<Slot, Slot>> {
	val handler = getCapability(MatteryCapability.CURIOS_INVENTORY).orNull() ?: 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() {
	return listOf()

	if (!isCuriosLoaded) {
		return listOf()
	}

	return getCuriosSlotsImpl()
}

private fun Player.curiosStreamImpl(includeCosmetics: Boolean): Stream<out ItemStack> {
	val handler = getCapability(MatteryCapability.CURIOS_INVENTORY).orNull() ?: return Stream.empty()

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

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

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

	return result.stream().flatMap { it }
}

fun Player.curiosStream(includeCosmetics: Boolean = true): Stream<out ItemStack> {
	return Stream.empty()

	if (!isCuriosLoaded) {
		return Stream.empty()
	}

	return curiosStreamImpl(includeCosmetics)
}

private fun Player.curiosAwareStreamImpl(includeCosmetics: Boolean): Stream<out AwareItemStack> {
	val handler = getCapability(MatteryCapability.CURIOS_INVENTORY).orNull() ?: 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> {
	return Stream.empty()

	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
}
