package ru.dbotthepony.mc.otm.menu

import com.google.common.collect.ImmutableList
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.runOnClient

/**
 * Make slots for single container
 */
inline fun <C : Container, S : Slot> makeSlots(container: C, initializer: (C, Int) -> S): ImmutableList<S> {
	return immutableList(container.containerSize) { initializer.invoke(container, it) }
}

/**
 * Make slots for list of containers with single slot in them
 */
inline fun <S : Slot> makeSlots(containers: List<Container>?, size: Int, initializer: (Container, Int) -> S): ImmutableList<S> {
	return immutableList(size) { initializer(containers?.get(it) ?: SimpleContainer(1), 0) }
}

open class MatterySlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : Slot(container, index, x, y) {
	var ignoreSpectators = true

	override fun mayPickup(player: Player): Boolean {
		return super.mayPickup(player) && (!ignoreSpectators || !player.isSpectator)
	}

	override fun mayPlace(itemStack: ItemStack): Boolean {
		return super.mayPlace(itemStack) && (!ignoreSpectators || runOnClient(true) { minecraft.player?.isSpectator != true })
	}

	open fun canTakeItemForPickAll(): Boolean {
		return true
	}
}

open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
	var hasSetFilter = false
		private set

	var filter: GetterSetter<Item?>? = null
		set(value) {
			hasSetFilter = true
			field = value
		}

	override fun canTakeItemForPickAll(): Boolean {
		return filter?.get() == null
	}

	fun isSameFilter(other: Slot): Boolean {
		if (other !is UserFilteredSlot)
			return filter?.get() == null

		return (
			(other.filter == null && filter == null) ||
			(other.filter != null && filter != null && other.filter!!.get() == filter!!.get())
		)
	}

	override fun isSameInventory(other: Slot): Boolean {
		return isSameFilter(other) && super.isSameInventory(other)
	}
}

open class MachineOutputSlot(container: Container, index: Int, x: Int = 0, y: Int = 0, val onTake: (ItemStack) -> Unit = {}) : MatterySlot(container, index, x, y) {
	override fun mayPlace(itemStack: ItemStack): Boolean {
		return false
	}

	override fun onTake(pPlayer: Player, pStack: ItemStack) {
		super.onTake(pPlayer, pStack)
		this.onTake.invoke(pStack)
	}

	override fun onQuickCraft(pStack: ItemStack, pAmount: Int) {
		super.onQuickCraft(pStack, pAmount)
		this.onTake.invoke(pStack)
	}
}

open class BatterySlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
	override fun mayPlace(itemStack: ItemStack): Boolean {
		return super.mayPlace(itemStack) && (itemStack.energy?.canExtract() ?: false)
	}
}

open class ChargeSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
	override fun mayPlace(itemStack: ItemStack): Boolean {
		return super.mayPlace(itemStack) && (itemStack.energy?.canReceive() ?: false)
	}
}

open class MatterContainerInputSlot(
	container: Container,
	index: Int,
	x: Int = 0,
	y: Int = 0,
	val direction: FlowDirection = FlowDirection.BI_DIRECTIONAL
) : MatterySlot(container, index, x, y) {
	override fun mayPlace(itemStack: ItemStack): Boolean {
		val handler = itemStack.getCapability(MatteryCapability.MATTER).resolve()
		if (handler.isEmpty) return false
		return super.mayPlace(itemStack) && this.direction.test(handler.get().matterFlow)
	}
}

open class PatternSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
	override fun mayPlace(itemStack: ItemStack): Boolean {
		return super.mayPlace(itemStack) && itemStack.getCapability(MatteryCapability.PATTERN).isPresent
	}
}

open class DriveSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
	override fun mayPlace(itemStack: ItemStack): Boolean {
		return super.mayPlace(itemStack) && itemStack.getCapability(MatteryCapability.DRIVE).isPresent
	}
}
