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

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.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.kommons.util.value
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energyStoredMattery
import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery
import ru.dbotthepony.mc.otm.capability.transcieveEnergy
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.otmRandom
import ru.dbotthepony.mc.otm.core.shuffle
import ru.dbotthepony.mc.otm.menu.tech.BatteryBankMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import java.util.function.Supplier

class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.BATTERY_BANK, p_155229_, p_155230_), IMatteryEnergyStorage {
	var gaugeLevel by syncher.float()
		private set

	// 6 на 2
	val container: MatteryContainer = object : MatteryContainer(::setChanged, CAPACITY) {
		override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
			super.setChanged(slot, new, old)
			batteryStatus[slot].value = new.getCapability(Capabilities.EnergyStorage.ITEM) != null
			gaugeLevel = batteryLevel.percentage(maxBatteryLevel)
		}

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

	val batteryStatus = immutableList(CAPACITY) {
		syncher.boolean(false)
	}

	val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(HandlerFilter.Dischargeable))

	init {
		savetables.stateful(::container, INVENTORY_KEY)
	}

	private val containerSlotIndices = IntArray(CAPACITY) { it }

	private fun distributeEnergy(isReceiving: Boolean, howMuch: Decimal, simulate: Boolean): Decimal {
		if (!howMuch.isPositive)
			return Decimal.ZERO

		containerSlotIndices.shuffle(level!!.otmRandom)
		var summ = Decimal.ZERO
		var remaining = howMuch

		for (currentSlot in containerSlotIndices) {
			val item = container[currentSlot]

			if (!item.isEmpty) {
				val energy = item.matteryEnergy ?: item.energy

				if (energy != null) {
					val diff = energy.transcieveEnergy(remaining, isReceiving, simulate)
					summ += diff
					remaining -= diff

					if (!remaining.isPositive) {
						break
					}
				}
			}
		}

		if (!simulate && !summ.isZero) {
			markDirtyFast()
			gaugeLevel = batteryLevel.percentage(maxBatteryLevel)
		}

		return summ
	}

	override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
		return distributeEnergy(isReceiving = false, howMuch, simulate)
	}

	override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
		return distributeEnergy(isReceiving = true, howMuch, simulate)
	}

	override val canSetBatteryLevel: Boolean
		get() = false

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

	override var batteryLevel: Decimal
		get() {
			var result = Decimal.ZERO

			for (i in 0 until container.containerSize) {
				val stack = container.getItem(i)

				if (!stack.isEmpty) {
					stack.energy?.let {
						result += it.energyStoredMattery
					}
				}
			}

			return result
		}
		set(value) {
			throw UnsupportedOperationException("Can't set battery value for battery bank")
		}

	override val maxBatteryLevel: Decimal
		get() {
			var result = Decimal.ZERO

			for (i in 0 until container.containerSize) {
				val stack = container.getItem(i)

				if (!stack.isEmpty) {
					stack.energy?.let {
						result += it.maxEnergyStoredMattery
					}
				}
			}

			return result
		}

	val energyConfig = ConfigurableEnergy(
		ProfiledEnergyStorage(this),
		frontDefault = FlowDirection.OUTPUT,
		backDefault = FlowDirection.INPUT,
		leftDefault = FlowDirection.INPUT,
		rightDefault = FlowDirection.INPUT,
		topDefault = FlowDirection.INPUT,
		bottomDefault = FlowDirection.INPUT,
		volatileEnergyValues = true
	)

	init {
		energyConfig.front.automatePush = true
		savetables.stateful(Supplier { energyConfig.energy.savedata }, "energyUsageHistory")
	}

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

	companion object {
		const val CAPACITY = 6 * 2
	}
}
