package ru.dbotthepony.mc.otm.capability

import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.immutableSet
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.formatMatter
import ru.dbotthepony.mc.otm.core.util.formatPower

/**
 * Upgrades merge by sum of their effects
 *
 * Values which are otherwise not stated to allow negatives or not, Do allow negative values.
 */
interface IMatteryUpgrade {
	/**
	 * Type(s) this upgrade represent
	 */
	val upgradeTypes: Set<UpgradeType> get() = setOf()

	/**
	 * [speedBonus] of `1.0` means that machine doubles the speed (performs twice the amount of
	 * work ticks per work cycle).
	 */
	val speedBonus: Double get() = 0.0

	/**
	 * **CAN NOT be negative.**
	 *
	 * [processingItems] of `4` means machine will accept at most 5 (1 + 4) of items per job.
	 */
	val processingItems: Int get() = 0

	/**
	 * **CAM NOT be negative.**
	 *
	 * Added directly over regular energy storage capacity.
	 */
	val energyStorageFlat: Decimal get() = Decimal.ZERO

	/**
	 * **CAM NOT be negative.**
	 */
	val energyStorage: Decimal get() = Decimal.ZERO

	/**
	 * **CAM NOT be negative.**
	 *
	 * Added directly over regular matter storage capacity.
	 */
	val matterStorageFlat: Decimal get() = Decimal.ZERO

	/**
	 * **CAM NOT be negative.**
	 */
	val matterStorage: Decimal get() = Decimal.ZERO

	/**
	 * Value of `1` means power consumption is doubled, `2` is tripled, `-0.5` is halved.
	 */
	val energyConsumed: Decimal get() = Decimal.ZERO

	/**
	 * **CAN NOT be negative.**
	 *
	 * Added directly over regular throughput
	 */
	val energyThroughputFlat: Decimal get() = Decimal.ZERO

	/**
	 * **CAN NOT be negative.**
	 *
	 * Value of `1` means power throughput is doubled, `2` is tripled, and so on.
	 */
	val energyThroughput: Decimal get() = Decimal.ZERO

	/**
	 * **CAN NOT be negative.**
	 *
	 * **Merged by multiplication**
	 */
	val failureMultiplier: Double get() = 1.0

	companion object : IMatteryUpgrade
}

private val positiveBound = Decimal("0.01")
private val negativeBound = Decimal("-0.01")

fun IMatteryUpgrade.addUpgradeTooltipLines(tooltips: MutableCollection<Component>) {
	if (upgradeTypes.isNotEmpty() && ShiftPressedCond.asBoolean) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade_type.list").withStyle(ChatFormatting.GRAY))

		for (upgrade in upgradeTypes) {
			tooltips.add(upgrade.component.copy().withStyle(ChatFormatting.GRAY))
		}

		tooltips.add(TextComponent(""))
	}

	if (speedBonus >= 0.01) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.speed", TextComponent("+" + (speedBonus.coerceIn(MachinesConfig.Upgrades.MIN_SPEED, MachinesConfig.Upgrades.MAX_SPEED) * 100.0).toInt().toString()).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	} else if (speedBonus <= -0.01) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.speed", TextComponent((speedBonus.coerceIn(MachinesConfig.Upgrades.MIN_SPEED, MachinesConfig.Upgrades.MAX_SPEED) * 100.0).toInt().toString()).withStyle(ChatFormatting.DARK_RED)).withStyle(ChatFormatting.GRAY))
	}

	if (processingItems != 0) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.processing_items", TextComponent(processingItems.toString()).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	}

	if (energyStorageFlat != Decimal.ZERO) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_storage_flat", energyStorageFlat.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	}

	if (energyStorage != Decimal.ZERO) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_storage", TextComponent((energyStorage * 100).toString(0)).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	}

	if (matterStorageFlat != Decimal.ZERO) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.matter_storage_flat", matterStorageFlat.formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	}

	if (matterStorage != Decimal.ZERO) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.matter_storage", TextComponent((matterStorage * 100).toString(0)).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	}

	if (energyConsumed >= positiveBound) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_consumed", TextComponent("+" + (energyConsumed.coerceIn(MachinesConfig.Upgrades.MIN_ENERGY, MachinesConfig.Upgrades.MAX_ENERGY) * 100).toString(0)).withStyle(ChatFormatting.DARK_RED)).withStyle(ChatFormatting.GRAY))
	} else if (energyConsumed <= negativeBound) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_consumed", TextComponent((energyConsumed.coerceIn(MachinesConfig.Upgrades.MIN_ENERGY, MachinesConfig.Upgrades.MAX_ENERGY) * 100).toString(0)).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	}

	if (energyThroughputFlat != Decimal.ZERO) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_throughput_flat", energyThroughputFlat.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	}

	if (energyThroughput != Decimal.ZERO) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.energy_throughput", TextComponent((energyThroughput * 100).toString(0)).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	}

	if (failureMultiplier >= 1.01) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.failsafe", TextComponent("+" + ((failureMultiplier - 1) * 100).toInt().toString()).withStyle(ChatFormatting.DARK_RED)).withStyle(ChatFormatting.GRAY))
	} else if (failureMultiplier <= 0.99) {
		tooltips.add(TranslatableComponent("otm.gui.upgrade.failsafe", TextComponent(((failureMultiplier.coerceAtLeast(0.0) - 1) * 100).toInt().toString()).withStyle(ChatFormatting.DARK_GREEN)).withStyle(ChatFormatting.GRAY))
	}
}

fun IMatteryUpgrade.getUpgradeTooltipLines(): MutableList<Component> {
	return ArrayList<Component>().also { addUpgradeTooltipLines(it) }
}

enum class UpgradeType {
	SPEED,
	PROCESSING,
	ENERGY_STORAGE,
	ENERGY_CONSUMPTION,
	ENERGY_THROUGHPUT,
	MATTER_STORAGE,
	FAILSAFE;

	val localeId = "otm.gui.upgrade_type.${name.lowercase()}"
	val component: Component get() = TranslatableComponent(localeId)

	val flag = 1 shl (ordinal + 1)
	fun set() = sets[flag]

	companion object {
		private val cached = values()

		private val sets = Array(2 shl cached.size) {
			immutableSet {
				for (u in values()) if (it.and(u.flag) != 0) accept(u)
			}
		}

		@JvmField
		val ALL = set(*values())

		@JvmField
		val BASIC = set(SPEED, ENERGY_STORAGE, ENERGY_CONSUMPTION, ENERGY_THROUGHPUT)

		@JvmField
		val BASIC_MATTER = set(SPEED, ENERGY_STORAGE, ENERGY_CONSUMPTION, ENERGY_THROUGHPUT, MATTER_STORAGE)

		@JvmField
		val REPLICATOR = set(SPEED, ENERGY_STORAGE, ENERGY_CONSUMPTION, ENERGY_THROUGHPUT, MATTER_STORAGE, FAILSAFE)

		@JvmField
		val BASIC_PROCESSING = set(SPEED, ENERGY_STORAGE, ENERGY_CONSUMPTION, ENERGY_THROUGHPUT, PROCESSING)

		fun set(vararg types: UpgradeType): Set<UpgradeType> {
			var flags = 0
			for (v in types) flags = flags or v.flag
			return sets[flags]
		}

		fun set(): Set<UpgradeType> {
			return sets[0]
		}

		fun set(
			type0: UpgradeType,
		): Set<UpgradeType> = sets[type0.flag]

		fun set(
			type0: UpgradeType,
			type1: UpgradeType,
		): Set<UpgradeType> = sets[type0.flag or type1.flag]

		fun set(
			type0: UpgradeType,
			type1: UpgradeType,
			type2: UpgradeType,
		): Set<UpgradeType> = sets[type0.flag or type1.flag or type2.flag]

		fun set(
			type0: UpgradeType,
			type1: UpgradeType,
			type2: UpgradeType,
			type3: UpgradeType,
		): Set<UpgradeType> = sets[type0.flag or type1.flag or type2.flag or type3.flag]

		fun set(
			type0: UpgradeType,
			type1: UpgradeType,
			type2: UpgradeType,
			type3: UpgradeType,
			type4: UpgradeType,
		): Set<UpgradeType> = sets[type0.flag or type1.flag or type2.flag or type3.flag or type4.flag]
	}
}
