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 ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.canSetBatteryMattery
import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energyStoredMattery
import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery
import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.menu.tech.EnergyServoMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities

class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_SERVO, blockPos, blockState) {
	val discharge = MatteryContainer(::setChangedLight, 1).also(::addDroppableContainer)
	val charge = MatteryContainer(::setChangedLight, 1).also(::addDroppableContainer)

	val energy: ProfiledEnergyStorage<IMatteryEnergyStorage> = ProfiledEnergyStorage(object : IMatteryEnergyStorage {
		override val energyFlow: FlowDirection get() {
			val discharge = discharge[0].energy
			val charge = charge[0].energy
			return FlowDirection.of(input = charge?.canReceive() ?: false, output = discharge?.canExtract() ?: false)
		}

		override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
			if (redstoneControl.isBlockedByRedstone)
				return Decimal.ZERO

			return discharge[0].energy?.extractEnergy(howMuch, simulate) ?: Decimal.ZERO
		}

		override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
			if (redstoneControl.isBlockedByRedstone)
				return Decimal.ZERO

			return charge[0].energy?.receiveEnergy(howMuch, simulate) ?: Decimal.ZERO
		}

		override val canSetBatteryLevel: Boolean
			get() = charge[0].energy?.canSetBatteryMattery ?: discharge[0].energy?.canSetBatteryMattery ?: false

		override var batteryLevel: Decimal
			get() = charge[0].energy?.energyStoredMattery ?: discharge[0].energy?.energyStoredMattery ?: Decimal.ZERO
			set(value) {
				val energy = charge[0].energy ?: discharge[0].energy ?: throw UnsupportedOperationException("No item in slots")
				energy.energyStoredMattery = value
			}

		override val maxBatteryLevel: Decimal
			get() = charge[0].energy?.maxEnergyStoredMattery ?: discharge[0].energy?.maxEnergyStoredMattery ?: Decimal.ZERO
	})

	val energyConfig = ConfigurableEnergy(energy, possibleModes = FlowDirection.BI_DIRECTIONAL)

	val itemConfig = ConfigurableItemHandler(
		input = charge.handler(HandlerFilter.OnlyIn.and(HandlerFilter.Chargeable)),
		output = discharge.handler(HandlerFilter.OnlyOut.and(HandlerFilter.Dischargeable))
	)

	init {
		savetables.stateful(::charge)
		savetables.stateful(::discharge)
	}

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

	override fun tick() {
		super.tick()

		if (redstoneControl.isBlockedByRedstone)
			return

		val charge = charge[0]
		val discharge = discharge[0]

		if (!charge.isEmpty && !discharge.isEmpty) {
			val chargeEnergy = charge.energy ?: return
			val dischargeEnergy = discharge.energy ?: return

			val extracted = dischargeEnergy.extractEnergy(Decimal.LONG_MAX_VALUE, true)

			if (extracted.isPositive) {
				val received = chargeEnergy.receiveEnergy(extracted, true)

				if (received.isPositive) {
					val extracted2 = dischargeEnergy.extractEnergy(received, false)

					if (extracted2 == received) {
						chargeEnergy.receiveEnergy(dischargeEnergy.extractEnergy(received, false), false)
					}
				}
			}
		}
	}
}
