package ru.dbotthepony.mc.otm.container

import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.ForgeHooks
import net.minecraftforge.common.capabilities.ForgeCapabilities
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.isNotEmpty

interface HandlerFilter {
	fun canInsert(slot: Int, stack: ItemStack): Boolean {
		return true
	}

	fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
		return true
	}

	fun preInsert(slot: Int, stack: ItemStack, simulate: Boolean) {}
	fun preExtract(slot: Int, amount: Int, simulate: Boolean) {}

	fun and(other: HandlerFilter): HandlerFilter {
		return object : HandlerFilter {
			override fun canInsert(slot: Int, stack: ItemStack): Boolean {
				return this@HandlerFilter.canInsert(slot, stack) && other.canInsert(slot, stack)
			}

			override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
				return this@HandlerFilter.canExtract(slot, amount, stack) && other.canExtract(slot, amount, stack)
			}

			override fun preInsert(slot: Int, stack: ItemStack, simulate: Boolean) {
				this@HandlerFilter.preInsert(slot, stack, simulate)
				other.preInsert(slot, stack, simulate)
			}

			override fun preExtract(slot: Int, amount: Int, simulate: Boolean) {
				this@HandlerFilter.preExtract(slot, amount, simulate)
				other.preExtract(slot, amount, simulate)
			}
		}
	}

	object FluidContainers : HandlerFilter {
		override fun canInsert(slot: Int, stack: ItemStack): Boolean {
			return stack.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).map { it.tanks > 0 }.orElse(false)
		}
	}

	object DrainableFluidContainers : HandlerFilter {
		override fun canInsert(slot: Int, stack: ItemStack): Boolean {
			return stack.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM).map { it.stream().anyMatch { it.isNotEmpty } }.orElse(false)
		}

		override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
			return !canInsert(slot, stack)
		}
	}

	object OnlyIn : HandlerFilter {
		override fun canInsert(slot: Int, stack: ItemStack): Boolean {
			return true
		}

		override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
			return false
		}
	}

	object OnlyOut : HandlerFilter {
		override fun canInsert(slot: Int, stack: ItemStack): Boolean {
			return false
		}

		override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
			return true
		}
	}

	object Both : HandlerFilter

	object Dischargeable : HandlerFilter {
		override fun canInsert(slot: Int, stack: ItemStack): Boolean {
			return stack.getCapability(ForgeCapabilities.ENERGY).map { it.canExtract() && it.extractEnergy(Int.MAX_VALUE, true) > 0 }.orElse(false)
		}

		override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
			return stack.getCapability(ForgeCapabilities.ENERGY).map { !it.canExtract() || it.extractEnergy(Int.MAX_VALUE, true) <= 0 }.orElse(true)
		}
	}

	object Chargeable : HandlerFilter {
		override fun canInsert(slot: Int, stack: ItemStack): Boolean {
			return stack.getCapability(ForgeCapabilities.ENERGY).map { it.canReceive() && it.receiveEnergy(Int.MAX_VALUE, true) > 0 }.orElse(false)
		}

		override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
			return stack.getCapability(ForgeCapabilities.ENERGY).map { !it.canReceive() || it.receiveEnergy(Int.MAX_VALUE, true) <= 0 }.orElse(true)
		}
	}

	object ChemicalFuel : HandlerFilter {
		override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
			return ForgeHooks.getBurnTime(stack, null) <= 0
		}

		override fun canInsert(slot: Int, stack: ItemStack): Boolean {
			return ForgeHooks.getBurnTime(stack, null) > 0
		}
	}
}
