package ru.dbotthepony.mc.otm.container

import ru.dbotthepony.mc.otm.capability.IMatteryUpgrade
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.config.VerboseEnergyBalanceValues
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.reduce
import ru.dbotthepony.mc.otm.core.math.Decimal
import kotlin.math.pow

open class UpgradeContainer(slotCount: Int, open val allowedUpgrades: Set<UpgradeType> = UpgradeType.ALL, listener: ContainerListener = EmptyListener) : MatteryContainer(listener, slotCount), IMatteryUpgrade {
	constructor(listener: () -> Unit, slotCount: Int, allowedUpgrades: Set<UpgradeType>) : this(slotCount, allowedUpgrades, { _, _, _ -> listener.invoke() })

	final override val upgradeTypes: Set<UpgradeType>
		get() = setOf()

	protected fun decimals(fn: (IMatteryUpgrade) -> Decimal, reducer: (Decimal, Decimal) -> Decimal): Decimal {
		return iterator()
			.map { it.getCapability(MatteryCapability.UPGRADE).map(fn).orElse(Decimal.ZERO).moreThanZero() * it.count }
			.reduce(Decimal.ZERO, reducer)
	}

	override val speedBonus: Double
		get() = iterator().map { it.getCapability(MatteryCapability.UPGRADE).map { it.speedBonus }.orElse(0.0) * it.count }.reduce(0.0) { a, b -> a + b }
	override val processingItems: Int
		get() = iterator().map { it.getCapability(MatteryCapability.UPGRADE).map { it.processingItems }.orElse(0).coerceAtLeast(0) * it.count }.reduce(0) { a, b -> a + b }
	override val energyStorageFlat: Decimal
		get() = decimals(IMatteryUpgrade::energyStorageFlat, Decimal::plus)
	override val energyStorage: Decimal
		get() = decimals(IMatteryUpgrade::energyStorage, Decimal::plus)
	override val matterStorageFlat: Decimal
		get() = decimals(IMatteryUpgrade::matterStorageFlat, Decimal::plus)
	override val matterStorage: Decimal
		get() = decimals(IMatteryUpgrade::matterStorage, Decimal::plus)
	override val energyConsumed: Decimal
		get() = decimals(IMatteryUpgrade::energyConsumed, Decimal::plus)
	override val failureMultiplier: Double
		get() = iterator().map { it.getCapability(MatteryCapability.UPGRADE).map { it.failureMultiplier }.orElse(1.0).coerceAtLeast(0.0).pow(it.count.toDouble()) }.reduce(1.0) { a, b -> a * b }
	override val energyThroughputFlat: Decimal
		get() = decimals(IMatteryUpgrade::energyThroughputFlat, Decimal::plus)
	override val energyThroughput: Decimal
		get() = decimals(IMatteryUpgrade::energyThroughput, Decimal::plus)

	fun transform(values: EnergyBalanceValues): EnergyBalanceValues {
		return object : EnergyBalanceValues {
			override val energyCapacity: Decimal
				get() = values.energyCapacity * (energyStorage + Decimal.ONE) + energyStorageFlat
			override val energyThroughput: Decimal
				get() = values.energyThroughput * (this@UpgradeContainer.energyThroughput + Decimal.ONE) + energyThroughputFlat
		}
	}

	fun transform(values: VerboseEnergyBalanceValues): VerboseEnergyBalanceValues {
		return object : VerboseEnergyBalanceValues {
			override val energyCapacity: Decimal
				get() = values.energyCapacity * (energyStorage + Decimal.ONE) + energyStorageFlat
			override val maxEnergyReceive: Decimal
				get() = values.maxEnergyReceive * (energyThroughput + Decimal.ONE) + energyThroughputFlat
			override val maxEnergyExtract: Decimal
				get() = values.maxEnergyExtract * (energyThroughput + Decimal.ONE) + energyThroughputFlat
		}
	}

	fun matterCapacity(value: () -> Decimal): () -> Decimal {
		return { value.invoke() * (matterStorage + Decimal.ONE) + matterStorageFlat }
	}
}
