package ru.dbotthepony.mc.otm.menu.storage

import com.google.common.collect.ImmutableList
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.capabilities.ForgeCapabilities
import ru.dbotthepony.mc.otm.block.entity.storage.DriveViewerBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.registry.MMenus
import ru.dbotthepony.mc.otm.storage.ITEM_STORAGE
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import ru.dbotthepony.mc.otm.storage.PoweredVirtualComponent

class DriveViewerMenu @JvmOverloads constructor(
	containerID: Int,
	inventory: Inventory,
	tile: DriveViewerBlockEntity? = null
) : MatteryPoweredMenu(
	MMenus.DRIVE_VIEWER, containerID, inventory, tile
), INetworkedItemViewProvider {
	override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
	val driveSlot: MatterySlot

	private val powered: PoweredVirtualComponent<ItemStackWrapper>?
	private var lastDrive: IMatteryDrive<ItemStackWrapper>? = null

	init {
		val container = tile?.container ?: SimpleContainer(1)

		driveSlot = object : MatterySlot(container, 0) {
			override fun mayPlace(itemStack: ItemStack): Boolean {
				return itemStack.getCapability(MatteryCapability.DRIVE).isPresent
			}
		}

		if (tile != null) {
			powered = PoweredVirtualComponent(
				ItemStackWrapper::class.java,
				tile.getCapability(MatteryCapability.ENERGY).resolve().get()
			)

			this.networkedItemView.setComponent(powered)
		} else {
			powered = null
		}

		addStorageSlot(driveSlot)
		addInventorySlots()
	}

	val driveFilter = ItemFilter(PortableCondensationDriveItem.MAX_FILTERS) { self, _, _, _ ->
		if (tile?.container?.get(0)?.item is PortableCondensationDriveItem) {
			tile.container[0].getOrCreateTag().put(PortableCondensationDriveItem.FILTER_PATH, self.serializeNBT())
		}
	}

	val driveFilterSlots = addFilterSlots(driveFilter)

	val isWhitelist = BooleanInputWithFeedback(this)
	val matchTag = BooleanInputWithFeedback(this)
	val matchNBT = BooleanInputWithFeedback(this)

	override fun broadcastChanges() {
		super.broadcastChanges()

		if (tile != null) {
			val itemStack = (tile as DriveViewerBlockEntity).container.getItem(0)

			val prev = lastDrive
			lastDrive = null

			if (!itemStack.isEmpty) {
				itemStack.getCapability(MatteryCapability.DRIVE).ifPresentK {
					if (it.storageType === ITEM_STORAGE) {
						lastDrive = it as IMatteryDrive<ItemStackWrapper>
					}
				}
			}

			if (prev != lastDrive) {
				prev?.let(powered!!::remove)
				this.networkedItemView.clear()
				lastDrive?.let(powered!!::add)

				if (lastDrive != null) {
					val filter = (itemStack.item as? PortableCondensationDriveItem)?.getFilterSettings(itemStack)

					if (filter != null) {
						driveFilter.copyFrom(filter)
						driveFilter.isLocked = false

						isWhitelist.with(driveFilter::isWhitelist)
						matchTag.with(driveFilter::matchTag)
						matchNBT.with(driveFilter::matchNBT)
					} else {
						driveFilter.clear()
						driveFilter.isLocked = true

						isWhitelist.clear()
						matchTag.clear()
						matchNBT.clear()
					}
				} else {
					driveFilter.clear()
					driveFilter.isLocked = true

					isWhitelist.clear()
					matchTag.clear()
					matchNBT.clear()
				}
			}

			this.networkedItemView.network()
		}
	}

	override fun removed(p_38940_: Player) {
		super.removed(p_38940_)

		if (lastDrive != null)
			powered?.remove(lastDrive!!)

		this.networkedItemView.removed()
	}

	override fun quickMoveStack(ply: Player, slotIndex: Int): ItemStack {
		val slot = slots[slotIndex]
		val item = slot.item

		if (item.isEmpty || item.getCapability(MatteryCapability.DRIVE).isPresent || item.getCapability(ForgeCapabilities.ENERGY).isPresent)
			return super.quickMoveStack(ply, slotIndex)

		val powered = powered
		if (lastDrive == null || powered == null)
			return ItemStack.EMPTY

		val remaining = powered.insertStack(ItemStackWrapper(item), false)

		if (remaining.count.toInt() == item.count)
			return ItemStack.EMPTY

		if (remaining.isEmpty) {
			val copy = item.copy()
			slot.set(ItemStack.EMPTY)
			return copy
		}

		val copy = item.copy()
		item.shrink(remaining.count.toInt())
		slot.setChanged()
		return copy
	}
}
