package ru.dbotthepony.mc.otm.block.entity.matter

import net.minecraft.core.BlockPos
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.BatteryBankBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import ru.dbotthepony.mc.otm.menu.matter.MatterCapacitorBankMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities

class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.MATTER_CAPACITOR_BANK, p_155229_, p_155230_), IMatterStorage {
	var gaugeLevel by synchronizer.float().property
		private set

	val matterNode = SimpleMatterNode(matter = this)

	override val canSetMatterLevel: Boolean
		get() = false

	override var storedMatter: Decimal
		get() {
			var summ = Decimal.ZERO

			for (stack in container)
				if (!stack.isEmpty)
					stack.getCapability(MatteryCapability.MATTER).ifPresentK {
						summ += it.storedMatter
					}

			return summ
		}
		set(value) {
			throw UnsupportedOperationException()
		}

	override val maxStoredMatter: Decimal
		get() {
		var summ = Decimal.ZERO

		for (stack in container)
			if (!stack.isEmpty)
				stack.getCapability(MatteryCapability.MATTER).ifPresentK {
					summ += it.maxStoredMatter
				}

		return summ
	}

	override fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal {
		if (!howMuch.isPositive)
			return Decimal.ZERO

		@Suppress("NAME_SHADOWING")
		var howMuch = howMuch
		var summ = Decimal.ZERO

		for (stack in container) {
			if (!stack.isEmpty) {
				stack.getCapability(MatteryCapability.MATTER).ifPresent {
					val diff = it.receiveMatterChecked(howMuch, simulate)
					summ += diff
					howMuch -= diff
				}

				if (howMuch.isZero) {
					break
				}
			}
		}

		if (summ.isPositive && !simulate) {
			gaugeLevel = (storedMatter / maxStoredMatter).toFloat()
		}

		return summ
	}

	override fun extractMatter(howMuch: Decimal, simulate: Boolean): Decimal {
		if (!howMuch.isPositive)
			return Decimal.ZERO

		@Suppress("NAME_SHADOWING")
		var howMuch = howMuch
		var summ = Decimal.ZERO

		for (stack in container) {
			if (!stack.isEmpty) {
				stack.getCapability(MatteryCapability.MATTER).ifPresent {
					val diff = it.extractMatterChecked(howMuch, simulate)
					summ += diff
					howMuch -= diff
				}

				if (howMuch.isZero) {
					break
				}
			}
		}

		if (summ.isPositive && !simulate) {
			gaugeLevel = (storedMatter / maxStoredMatter).toFloat()
		}

		return summ
	}

	override val matterFlow: FlowDirection
		get() = FlowDirection.BI_DIRECTIONAL

	val container = object : MatteryContainer(this::setChangedLight, BatteryBankBlockEntity.CAPACITY) {
		override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
			super.setChanged(slot, new, old)
			capacitorStatus[slot].boolean = new.getCapability(MatteryCapability.MATTER).isPresent
			gaugeLevel = (storedMatter / maxStoredMatter).toFloat()
		}

		override fun getMaxStackSize(): Int = 1
	}.also(::addDroppableContainer)

	val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(object : HandlerFilter {
		override fun canInsert(slot: Int, stack: ItemStack): Boolean {
			return stack.getCapability(MatteryCapability.MATTER).isPresent
		}

		override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
			stack.getCapability(MatteryCapability.MATTER).ifPresentK {
				if (it.storedMatter.isPositive) {
					return false
				}
			}

			return true
		}
	}))

	val capacitorStatus = immutableList(BatteryBankBlockEntity.CAPACITY) {
		synchronizer.bool(false)
	}

	init {
		savetables.stateful(::container, INVENTORY_KEY)
		exposeGlobally(MatteryCapability.MATTER, this)
		exposeGlobally(MatteryCapability.MATTER_NODE, matterNode)
	}

	override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
		return MatterCapacitorBankMenu(containerID, inventory, this)
	}

	override fun setRemoved() {
		super.setRemoved()
		matterNode.isValid = false
	}

	override fun setLevel(level: Level) {
		super.setLevel(level)
		matterNode.discover(this)
	}
}
