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.minecraftforge.common.capabilities.ForgeCapabilities
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.menu.tech.BatteryBankMenu
import ru.dbotthepony.mc.otm.registry.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 synchronizer.float().property
		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].boolean = new.getCapability(ForgeCapabilities.ENERGY).isPresent
			gaugeLevel = batteryLevel.percentage(maxBatteryLevel)
		}

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

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

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

	private var currentChangeSlot = 0
	private var currentDischangeSlot = 0

	private var lastTickCharged = false
	private var lastTickDischarged = false

	init {
		savetables.stateful(::container, INVENTORY_KEY)
		savetables.int(::currentChangeSlot)
		savetables.int(::currentDischangeSlot)
		savetables.bool(::lastTickCharged)
		savetables.bool(::lastTickDischarged)
	}

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

		var summ = Decimal.ZERO
		var remaining = howMuch
		var currentSlot = if (isReceiving) currentChangeSlot else currentDischangeSlot

		for (i in 0 until container.containerSize) {
			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
					}
				}
			}

			currentSlot = (currentSlot + 1) % container.containerSize
		}

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

		if (!simulate) {
			if (isReceiving) {
				this.lastTickCharged = true
				if (currentSlot == this.currentChangeSlot) currentSlot = (currentSlot + 1) % container.containerSize
				this.currentChangeSlot = currentSlot
			} else {
				this.lastTickDischarged = true
				if (currentSlot == this.currentDischangeSlot) currentSlot = (currentSlot + 1) % container.containerSize
				this.currentDischangeSlot = currentSlot
			}
		}

		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 fun tick() {
		super.tick()

		if (!lastTickCharged)
			currentChangeSlot = (currentChangeSlot + 1) % container.containerSize

		if (!lastTickDischarged)
			currentDischangeSlot = (currentDischangeSlot + 1) % container.containerSize

		lastTickCharged = false
		lastTickDischarged = false
	}

	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
	)

	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
	}
}
