package ru.dbotthepony.mc.otm.container.util

import net.minecraft.world.Container
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import ru.dbotthepony.mc.otm.container.IMatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty

/**
 * While this somewhat similar to [net.minecraft.world.inventory.Slot], this slot is not meant
 * for Player interaction.
 */
interface IContainerSlot : GetterSetter<ItemStack> {
	val slot: Int
	val container: Container

	operator fun component1() = slot
	operator fun component2() = item

	fun getMaxStackSize(item: ItemStack = this.item): Int {
		return container.maxStackSize
	}

	val isForbiddenForAutomation: Boolean get() {
		return getFilter() === Items.AIR
	}

	fun getFilter(): Item?

	/**
	 * @return whenever the filter was set. Returns false only if container can't be filtered.
	 */
	fun setFilter(filter: Item? = null): Boolean

	val hasFilter: Boolean
		get() = getFilter() != null

	fun remove() {
		container[slot] = ItemStack.EMPTY
	}

	fun remove(count: Int): ItemStack {
		return container.removeItem(slot, count)
	}

	override fun get(): ItemStack {
		return container[slot]
	}

	override fun accept(t: ItemStack) {
		container[slot] = t
	}

	fun setChanged() {
		container.setChanged()
	}

	var item: ItemStack
		get() = container[slot]
		set(value) { container[slot] = value }

	val isEmpty: Boolean
		get() = container[slot].isEmpty

	val isNotEmpty: Boolean
		get() = container[slot].isNotEmpty
}

class ContainerSlot(override val slot: Int, override val container: Container) : IContainerSlot {
	init {
		require(slot in 0 until container.containerSize) { "Slot out of bounds: $slot (container: $container with size ${container.containerSize})" }
	}

	override fun getFilter(): Item? {
		return null
	}

	override fun setFilter(filter: Item?): Boolean {
		return false
	}
}

fun Container.containerSlot(slot: Int): IContainerSlot {
	if (this is IMatteryContainer) {
		return containerSlot(slot)
	} else {
		return ContainerSlot(slot, this)
	}
}

operator fun Container.iterator() = iterator(true)

fun Container.iterator(nonEmpty: Boolean): Iterator<ItemStack> {
	if (this is IMatteryContainer) {
		return iterator(nonEmpty)
	} else if (nonEmpty) {
		return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
	} else {
		return (0 until containerSize).iterator().map { this[it] }
	}
}

fun Container.slotIterator(nonEmpty: Boolean = true): Iterator<IContainerSlot> {
	if (this is IMatteryContainer) {
		return slotIterator(nonEmpty)
	} else if (nonEmpty) {
		return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { ContainerSlot(it, this) }
	} else {
		return (0 until containerSize).iterator().map { ContainerSlot(it, this) }
	}
}
