package ru.dbotthepony.mc.otm.capability.energy

import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.registry.game.MDataComponentTypes

abstract class ItemEnergyStorageImpl(val itemStack: ItemStack) : IMatteryEnergyStorage, IEnergyStorageImpl {
	abstract val initialBatteryLevel: Decimal

	override var batteryLevel: Decimal
		get() = itemStack[MDataComponentTypes.BATTERY_LEVEL] ?: initialBatteryLevel
		set(value) { itemStack[MDataComponentTypes.BATTERY_LEVEL] = value }

	override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
		if (!howMuch.isPositive || itemStack.count != 1)
			return Decimal.ZERO

		val batteryLevel = batteryLevel

		if (!batteryLevel.isPositive)
			return Decimal.ZERO

		val newLevel = (batteryLevel - howMuch.coerceAtMost(maxOutput ?: Decimal.POSITIVE_INFINITY)).moreThanZero()
		val diff = (batteryLevel - newLevel).coerceIn(Decimal.ZERO, howMuch)

		if (!simulate && batteryLevel != newLevel) {
			this.batteryLevel = newLevel
		}

		return diff
	}

	override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
		if (!howMuch.isPositive || itemStack.count != 1)
			return Decimal.ZERO

		val batteryLevel = batteryLevel
		if (batteryLevel >= maxBatteryLevel && !maxBatteryLevel.isInfinite)
			return Decimal.ZERO

		val newLevel = (batteryLevel + howMuch.coerceAtMost(maxInput ?: Decimal.POSITIVE_INFINITY)).coerceAtMost(maxBatteryLevel)
		val diff = (newLevel - batteryLevel).coerceIn(Decimal.ZERO, howMuch)

		if (!simulate && batteryLevel != newLevel) {
			this.batteryLevel = newLevel
		}

		return diff
	}

	companion object {
		const val ENERGY_KEY = "energy"
	}
}

abstract class SimpleEnergyItem(
	override val energyFlow: FlowDirection,
	stack: ItemStack,
	maxBatteryLevel: Decimal,
	maxInput: Decimal?,
	maxOutput: Decimal?,
	override val initialBatteryLevel: Decimal
) : ItemEnergyStorageImpl(stack) {
	override var maxInput: Decimal? = maxInput
		protected set

	override var maxOutput: Decimal? = maxOutput
		protected set

	override var maxBatteryLevel: Decimal = maxBatteryLevel
		protected set
}

open class EnergyConsumerItem(
	stack: ItemStack,
	maxBatteryLevel: Decimal,
	maxInput: Decimal? = null,
	maxOutput: Decimal? = maxInput,
	initialBatteryLevel: Decimal = Decimal.ZERO
) : SimpleEnergyItem(FlowDirection.INPUT, stack, maxBatteryLevel, maxInput, maxOutput, initialBatteryLevel)

open class EnergyProducerItem(
	stack: ItemStack,
	maxBatteryLevel: Decimal,
	maxInput: Decimal? = null,
	maxOutput: Decimal? = maxInput,
	initialBatteryLevel: Decimal = Decimal.ZERO
) : SimpleEnergyItem(FlowDirection.OUTPUT, stack, maxBatteryLevel, maxInput, maxOutput, initialBatteryLevel)

open class EnergyCapacitorItem(
	stack: ItemStack,
	maxBatteryLevel: Decimal,
	maxInput: Decimal? = null,
	maxOutput: Decimal? = maxInput,
	override val initialBatteryLevel: Decimal = Decimal.ZERO
) : ItemEnergyStorageImpl(stack) {
	override val energyFlow: FlowDirection
		get() = itemStack[MDataComponentTypes.FLOW_DIRECTION] ?: FlowDirection.BI_DIRECTIONAL

	override var maxInput: Decimal? = maxInput
		protected set

	override var maxOutput: Decimal? = maxOutput
		protected set

	override var maxBatteryLevel: Decimal = maxBatteryLevel
		protected set
}

