package ru.dbotthepony.mc.otm.container

import net.minecraft.world.item.ItemStack
import net.minecraftforge.items.IItemHandler

class ContainerHandler(
	private val container: IMatteryContainer,
	private val filter: HandlerFilter = HandlerFilter.Both,
) : IItemHandler {
	override fun getSlots() = container.containerSize
	override fun getStackInSlot(slot: Int) = container[slot]

	override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
		if (!container.testSlotFilter(slot, stack) || !filter.canInsert(slot, stack))
			return stack

		filter.preInsert(slot, stack, simulate)

		val localStack = container[slot]
		var amount = filter.modifyInsertCount(slot, stack, localStack, simulate)

		if (amount <= 0)
			return stack

		if (localStack.isEmpty) {
			amount = stack.count.coerceAtMost(container.getMaxStackSize(slot, stack)).coerceAtMost(amount)

			if (!simulate) {
				container.setItem(slot, stack.copyWithCount(amount))
			}

			if (stack.count <= amount) {
				return ItemStack.EMPTY
			} else {
				return stack.copyWithCount(stack.count - amount)
			}
		} else if (localStack.isStackable && container.getMaxStackSize(slot, localStack) > localStack.count && ItemStack.isSameItemSameTags(localStack, stack)) {
			val newCount = container.getMaxStackSize(slot, localStack).coerceAtMost(localStack.count + stack.count.coerceAtMost(amount))
			val diff = newCount - localStack.count

			if (diff != 0) {
				if (!simulate) {
					localStack.grow(diff)
					container.setChanged(slot)
				}

				val copy = stack.copy()
				copy.shrink(diff)
				return copy
			}
		}

		return stack
	}

	override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
		if (amount <= 0 || container.isSlotForbiddenForAutomation(slot))
			return ItemStack.EMPTY

		val localStack = container.getItem(slot)
		if (localStack.isEmpty) return ItemStack.EMPTY

		@Suppress("name_shadowing")
		val amount = filter.modifyExtractCount(slot, amount, simulate)
		if (amount <= 0) return ItemStack.EMPTY
		if (!filter.canExtract(slot, amount, localStack)) return ItemStack.EMPTY

		filter.preExtract(slot, amount, simulate)

		val minimal = amount.coerceAtMost(localStack.count)
		val copy = localStack.copy()
		copy.count = minimal

		if (!simulate) {
			localStack.shrink(minimal)
			container.setChanged(slot)
		}

		return copy
	}

	override fun getSlotLimit(slot: Int): Int {
		return container.maxStackSize
	}

	override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
		return container.testSlotFilter(slot, stack) && filter.canInsert(slot, stack)
	}
}
