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.ConciseBalanceValues
import ru.dbotthepony.mc.otm.config.VerboseBalanceValues
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.mapToDouble
import ru.dbotthepony.mc.otm.core.collect.mapToInt
import ru.dbotthepony.mc.otm.core.collect.reduce
import ru.dbotthepony.mc.otm.core.collect.sum
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal
import kotlin.math.pow

open class UpgradeContainer(slotCount: Int, open val allowedUpgrades: Set<UpgradeType> = UpgradeType.ALL, watcher: Runnable = Runnable {}) : MatteryContainer(watcher, slotCount), IMatteryUpgrade {
	constructor(watcher: Runnable, slotCount: Int, allowedUpgrades: Set<UpgradeType>) : this(slotCount, allowedUpgrades, watcher)

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

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

	override val speedBonus: Double
		get() = iterator().filter { it.isNotEmpty }.mapToDouble { it.getCapability(MatteryCapability.UPGRADE).map { it.speedBonus }.orElse(0.0) * it.count }.sum()
	override val processingItems: Int
		get() = iterator().filter { it.isNotEmpty }.mapToInt { 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().filter { it.isNotEmpty }.mapToDouble { 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: ConciseBalanceValues): ConciseBalanceValues {
		return object : ConciseBalanceValues {
			override val capacity: Decimal
				get() = values.capacity * (energyStorage + Decimal.ONE) + energyStorageFlat
			override val throughput: Decimal
				get() = values.throughput * (energyThroughput + Decimal.ONE) + energyThroughputFlat
		}
	}

	fun transform(values: VerboseBalanceValues): VerboseBalanceValues {
		return object : VerboseBalanceValues {
			override val capacity: Decimal
				get() = values.capacity * (energyStorage + Decimal.ONE) + energyStorageFlat
			override val receive: Decimal
				get() = values.receive * (energyThroughput + Decimal.ONE) + energyThroughputFlat
			override val extract: Decimal
				get() = values.extract * (energyThroughput + Decimal.ONE) + energyThroughputFlat
		}
	}

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