/**
 * Determines the value to be displayed in the "Effect" column for subsystems.
 */
package theorycrafter.ui.fiteditor.effectcolumn

import androidx.compose.runtime.Composable
import eve.data.*
import eve.data.typeid.*
import theorycrafter.fitting.AppliedEffect
import theorycrafter.fitting.AttributeProperty
import theorycrafter.fitting.Fit
import theorycrafter.fitting.Subsystem
import theorycrafter.ui.widgets.TextAndTooltip
import theorycrafter.utils.with


/**
 * Returns a very short string describing the effect of the given subsystem; `null` if unknown.
 */
fun SubsystemType.shortEffectDescription(): String? {
    return when {
        isAugmentedAntimatterReactor()
                || isAugmentedGravitonReactor()
                || isAugmentedFusionReactor()
                || isAugmentedNuclearReactor() -> "Powergrid+Cap"
        isDissolutionSequencer() -> "Sensors"
        isElectronicEfficiencyGate() -> "CPU+Sensors"
        isAugmentedPlating()
                || isSupplementalScreening()
                || isAugmentedDurability() -> "Buffer Tank"
        isEnergyParasiticComplex() -> "Energy Warfare"
        isCovertReconfiguration() -> "Covert Ops"
        isNanobotInjector()
                || isAdaptiveDefenseNode()
                || isAmplificationNode() -> "Active Tank"
        isAssaultOptimization()
                || isAcceleratedEjectionBay()
                || isLauncherEfficiencyConfiguration() -> "Missiles"
        isLiquidCrystalMagnifiers() -> "Energy Turrets"
        isMagneticInfusionBasin()
                || isHybridEncodingPlatform() -> "Hybrid Turrets"
        isProjectileScopingArray() -> "Projectiles"
        isDroneSynthesisProjector() -> "Drones"
        isObfuscationManifold() -> "ECM"
        isImmobilityDrivers() -> "Web Range"
        isFrictionExtensionProcessor() -> "Point Range"
        isSupportProcessor() -> "Support"
        isInterdictionNullifier() -> "Bubble-Proof"
        isWakeLimiter() -> "Speed Tank"
        isFuelCatalyst()
                || isLocalizedInjectors() -> "Speed"
        isIntercalatedNanofibers()
                || isChassisOptimization() -> "Nanofiber"
        isHyperspatialOptimization() -> "Warping"
        else -> null
    }
}


/**
 * All the information we need to display a [Subsystem]'s effect.
 */
private class SubsystemInfo(
    override val fit: Fit,
    val subsystem: Subsystem
): AffectingItemInfo{

    // We're only displaying subsystem properties, not applied effects
    override val appliedEffects: Map<AttributeProperty<*>, Collection<AppliedEffect>>
        get() = error("Tactical modes don't display their applied effects")

}


/**
 * Returns the displayed effect for the fitting resources (slots, hardpoints) provided by the given subsystem type.
 */
fun SubsystemType.displayedSubsystemFitResources(): TextAndTooltip? {
    val highSlots = highSlotsAdded
    val medSlots = medSlotsAdded
    val lowSlots = lowSlotsAdded

    val text = listOf(highSlots to "H", medSlots to "M", lowSlots to "L")
        .filter { it.first != 0 }
        .joinToString(NNBSP) {
            it.first.toString(true) + it.second
        }

    return if (text.isEmpty()) null else TextAndTooltip(text)
}


/**
 * Returns the displayed effect for the given subsystem.
 */
@Composable
fun displayedSubsystemEffect(fit: Fit, subsystem: Subsystem): TextAndTooltip? {
    val subsystemType = subsystem.type
    val shipType = fit.ship.type

    val effectDescription = subsystem.type.shortEffectDescription() ?: return null
    val subsystemInfo = SubsystemInfo(fit, subsystem)
    return with(SubsystemEffectSources, subsystemInfo, subsystemType) {
        when {
            isAugmentedAntimatterReactor() || isAugmentedGravitonReactor() ->
                withCellText(
                    effectDescription,
                    POWER_OUTPUT_PCT_BONUS,
                    ENERGY_WARFARE_RESISTANCE_BONUS,
                    CAPACITOR_CAPACITY_PCT_BONUS,
                    CAPACITOR_CAPACITY_FLAT_BONUS,
                )
            isAugmentedFusionReactor() || isAugmentedNuclearReactor() ->
                withCellText(
                    effectDescription,
                    POWER_OUTPUT_PCT_BONUS,
                    ENERGY_WARFARE_RESISTANCE_BONUS,
                    CAPACITOR_RECHARGE_TIME_BONUS,
                    CAPACITOR_CAPACITY_FLAT_BONUS,
                )
            isDissolutionSequencer() ->
                withCellText(
                    effectDescription,
                    SENSOR_STRENGTH_BONUS,
                    TARGETING_RANGE_BONUS,
                    SCAN_RESOLUTION_BONUS,
                    MAX_LOCKED_TARGET_BONUS
                )
            isElectronicEfficiencyGate() ->
                withCellText(
                    effectDescription,
                    SENSOR_STRENGTH_BONUS,
                    TARGETING_RANGE_BONUS,
                    CPU_OUTPUT_PCT_BONUS,
                    MAX_LOCKED_TARGET_BONUS
                )
            isAugmentedPlating() ->
                withCellText(
                    effectDescription,
                    ARMOR_HITPOINTS_PCT_BONUS,
                    ARMOR_HITPOINTS_FLAT_BONUS,
                    ARMOR_HARDENER_OVERHEAT_BONUS,
                    SIGNATURE_RADIUS_PENALTY,
                    REDUCTION_IN_ARMOR_PLATE_MASS_PENALTY
                )
            isSupplementalScreening() ->
                withCellText(
                    effectDescription,
                    SHIELD_HITPOINTS_PCT_BONUS,
                    SHIELD_HITPOINTS_FLAT_BONUS,
                    SHIELD_HARDENER_OVERHEAT_BONUS,
                    SIGNATURE_RADIUS_PENALTY,
                )
            isAugmentedDurability() ->
                withCellText(
                    effectDescription,
                    BUFFER_PCT_BONUS,
                    RESIST_HARDENER_OVERHEAT_BONUS,
                    SHIELD_HITPOINTS_FLAT_BONUS,
                    ARMOR_HITPOINTS_FLAT_BONUS,
                    SIGNATURE_RADIUS_PENALTY,
                )
            isEnergyParasiticComplex() ->
                withCellText(
                    effectDescription,
                    NEUT_AMOUNT_BONUS,
                    OVERHEATING_NEUTS_BONUS,
                    REDUCTION_IN_OVERHEAT_DAMAGE,
                    REDUCTION_IN_FITTING_NEUTS,
                    CAPACITOR_CAPACITY_FLAT_BONUS
                )
            isCovertReconfiguration() ->
                withCellText(
                    effectDescription,
                    defensiveRepBonus(shipType),
                    defensiveRepOverheatBonus(shipType),
                    SCANNING_PROBE_STRENGTH_BONUS,
                    REDUCTION_IN_CLOAK_CPU_NEED,
                    CLOAK_REACTIVATION_DELAY,
                    CAPACITOR_CAPACITY_FLAT_BONUS,
                    SHIELD_HITPOINTS_FLAT_BONUS,
                    ARMOR_HITPOINTS_FLAT_BONUS,
                    STRUCTURE_HITPOINTS_FLAT_BONUS,
                    SIGNATURE_RADIUS_PENALTY,
                    CARGO_CAPACITY_BONUS,
                )
            isNanobotInjector() || isAdaptiveDefenseNode() || isAmplificationNode()->
                withCellText(
                    effectDescription,
                    defensiveRepBonus(shipType),
                    defensiveRepOverheatBonus(shipType),
                    CAPACITOR_CAPACITY_FLAT_BONUS,
                    SHIELD_HITPOINTS_FLAT_BONUS,
                    ARMOR_HITPOINTS_FLAT_BONUS,
                    STRUCTURE_HITPOINTS_FLAT_BONUS
                )
            isAssaultOptimization() ->
                withCellText(
                    effectDescription,
                    ASSAULT_OPTIMIZATION_MISSILE_DAMAGE_BONUS,
                    mediumMissileLauncherRowBonus(0),
                    DRONE_DAMAGE_BONUS,
                    DRONE_BANDWIDTH_BONUS,
                    DRONE_CAPACITY_BONUS,
                    REDUCTION_IN_MEDIUM_LAUNCHER_FITTING,
                    POWER_OUTPUT_FLAT_BONUS,
                    CPU_OUTPUT_FLAT_BONUS
                )
            isAcceleratedEjectionBay() ->
                withCellText(
                    effectDescription,
                    mediumMissileLauncherRowBonus(0),
                    ACCELERATED_EJECTION_BAY_MISSILE_DAMAGE_BONUS,
                    mediumMissileVelocityBonusProperty(2),
                    REDUCTION_IN_MEDIUM_LAUNCHER_FITTING,
                    POWER_OUTPUT_FLAT_BONUS,
                    CPU_OUTPUT_FLAT_BONUS
                )
            isLauncherEfficiencyConfiguration() ->
                withCellText(
                    effectDescription,
                    mediumMissileLauncherRowBonus(1),
                    mediumMissileVelocityBonusProperty(0),
                    MISSILE_EXPLOSION_VELOCITY_BONUS,
                    DRONE_BANDWIDTH_BONUS,
                    DRONE_CAPACITY_BONUS,
                    REDUCTION_IN_MEDIUM_LAUNCHER_FITTING,
                    POWER_OUTPUT_FLAT_BONUS,
                    CPU_OUTPUT_FLAT_BONUS
                )
            isLiquidCrystalMagnifiers() ->
                withCellText(
                    effectDescription,
                    MEDIUM_ENERGY_TURRET_DAMAGE_BONUS,
                    MEDIUM_ENERGY_TURRET_OPTIMAL_RANGE_BONUS,
                    MEDIUM_ENERGY_TURRET_ACTIVATION_COST_BONUS,
                    REDUCTION_IN_MEDIUM_ENERGY_TURRET_FITTING,
                    CAPACITOR_CAPACITY_FLAT_BONUS,
                    POWER_OUTPUT_FLAT_BONUS,
                    CPU_OUTPUT_FLAT_BONUS
                )
            isMagneticInfusionBasin() ->
                withCellText(
                    effectDescription,
                    MEDIUM_HYBRID_TURRET_DAMAGE_BONUS,
                    MEDIUM_HYBRID_TURRET_OPTIMAL_RANGE_BONUS,
                    REDUCTION_IN_MEDIUM_HYBRID_TURRET_FITTING,
                    CAPACITOR_CAPACITY_FLAT_BONUS,
                    POWER_OUTPUT_FLAT_BONUS,
                    CPU_OUTPUT_FLAT_BONUS
                )
            isHybridEncodingPlatform() ->
                withCellText(
                    effectDescription,
                    MEDIUM_HYBRID_TURRET_DAMAGE_BONUS,
                    MEDIUM_HYBRID_TURRET_FALLOFF_BONUS,
                    MEDIUM_HYBRID_TURRET_TRACKING_SPEED_BONUS,
                    DRONE_BANDWIDTH_BONUS,
                    DRONE_CAPACITY_BONUS,
                    REDUCTION_IN_MEDIUM_HYBRID_TURRET_FITTING,
                    CAPACITOR_CAPACITY_FLAT_BONUS,
                    POWER_OUTPUT_FLAT_BONUS,
                    CPU_OUTPUT_FLAT_BONUS
                )
            isProjectileScopingArray() ->
                withCellText(
                    effectDescription,
                    MEDIUM_PROJECTILE_TURRET_DAMAGE_BONUS,
                    MEDIUM_PROJECTILE_TURRET_OPTIMAL_AND_FALLOFF_BONUS,
                    MEDIUM_PROJECTILE_TURRET_TRACKING_SPEED_BONUS,
                    REDUCTION_IN_MEDIUM_PROJECTILE_TURRET_FITTING,
                    DRONE_BANDWIDTH_BONUS,
                    DRONE_CAPACITY_BONUS,
                    POWER_OUTPUT_FLAT_BONUS,
                    CPU_OUTPUT_FLAT_BONUS
                )
            isDroneSynthesisProjector() ->
                withCellText(
                    effectDescription,
                    DRONE_DAMAGE_AND_HITPOINTS_BONUS,
                    DRONE_VELOCITY_AND_TRACKING_SPEED_BONUS,
                    DRONE_BANDWIDTH_BONUS,
                    DRONE_CAPACITY_BONUS,
                    MEDIUM_HYBRID_TURRET_TRACKING_SPEED_BONUS,
                    REDUCTION_IN_MEDIUM_HYBRID_TURRET_FITTING,
                    POWER_OUTPUT_FLAT_BONUS,
                    CPU_OUTPUT_FLAT_BONUS
                )
            isObfuscationManifold() ->
                withCellText(
                    effectDescription,
                    ECM_TARGETED_ECM_STRENGTH_AND_OPTIMAL_BONUS,
                    OVERHEATING_TARGETED_ECM_BONUS,
                    REDUCTION_IN_OVERHEAT_DAMAGE,
                )
            isImmobilityDrivers() ->
                withCellText(
                    effectDescription,
                    WEB_RANGE_BONUS,
                    WEB_OVERHEATING_BONUS,
                    REDUCTION_IN_OVERHEAT_DAMAGE
                )
            isFrictionExtensionProcessor() ->
                withCellText(
                    effectDescription,
                    WARP_DISRUPTION_RANGE_BONUS,
                    WARP_DISRUPTION_OVERHEATING_BONUS,
                    REDUCTION_IN_OVERHEAT_DAMAGE
                )
            isSupportProcessor() ->
                withCellText(
                    effectDescription,
                    offensiveCommandBurstBonus(shipType),
                    REDUCTION_IN_COMMAND_BURST_FITTING,
                    COMMAND_BURST_RANGE_BONUS,
                    REMOTE_ARMOR_REPAIRER_OPTIMAL_BONUS,
                    REMOTE_ARMOR_REPAIRER_FALLOFF_BONUS,
                    REMOTE_SHIELD_BOOSTER_FALLOFF_BONUS,
                    REDUCTION_IN_MED_REMOTE_SHIELD_BOOSTER_FITTING,
                    REDUCTION_IN_MED_REMOTE_ARMOR_REPAIRER_FITTING,
                    offensiveRemoteRepairActivationReduction(shipType),
                    offensiveRemoteRepairOverheatingBonus(shipType),
                    DRONE_BANDWIDTH_BONUS,
                    DRONE_CAPACITY_BONUS,
                    CAPACITOR_CAPACITY_FLAT_BONUS,
                    MAX_LOCKED_TARGET_BONUS
                )
            isInterdictionNullifier() ->
                withCellText(
                    effectDescription,
                    WARP_SPEED_BONUS,
                    WARP_CAPACITOR_NEED_BONUS,
                    INTERDICTION_NULLIFIER_DURATION_BONUS,
                    REDUCTION_IN_INTERDICTION_NULLIFIER_REACTIVATION_DELAY,
                    REDUCTION_IN_INTERDICTION_NULLIFIER_MAX_TARGETING_RANGE_PENALTY,
                    INERTIA_MODIFIER_PENALTY,
                    MAX_TARGETING_RANGE_BONUS,
                    SIGNATURE_RADIUS_PENALTY,
                )
            isWakeLimiter() ->
                withCellText(
                    effectDescription,
                    AFTERBURNER_SPEED_BONUS,
                    REDUCTION_IN_MWD_SIG_RADIUS_PENALTY
                )
            isFuelCatalyst() ->
                withCellText(
                    effectDescription,
                    AFTERBURNER_SPEED_BONUS,
                    PROPMOD_OVERHEATING_BONUS
                )
            isLocalizedInjectors() ->
                withCellText(
                    effectDescription,
                    REDUCTION_IN_PROPMOD_ACTIVATION_COST,
                    PROPMOD_OVERHEATING_BONUS
                )
            isIntercalatedNanofibers() || isChassisOptimization() ->
                withCellText(
                    effectDescription,
                    MAX_SPEED_BONUS,
                    AGILITY_BONUS
                )
            isHyperspatialOptimization() ->
                withCellText(
                    effectDescription,
                    AGILITY_BONUS,
                    WARP_SPEED_INVERTED_BONUS
                )
            else -> null
        }
    }
}


/**
 * A [DisplayedEffectSource] for displaying the value of a subsystem property.
 */
private class SubsystemPropertyEffectSource<T: Any>(


    /**
     * Returns the property.
     */
    val property: (Subsystem) -> AttributeProperty<T>?,


    /**
     * A very short description of the property.
     */
    description: String,


    /**
     * Formats the property's value.
     */
    private val formatValue: (T, isCellDisplay: Boolean) -> String?


): DisplayedEffectSource<SubsystemInfo>(description = description){


    /**
     * A constructor that takes a [formatValue] function which doesn't care about whether the value will be displayed
     * in the cell or the tooltip.
     */
    constructor(
        property: (Subsystem) -> AttributeProperty<T>?,
        description: String,
        formatValue: (T) -> String?
    ): this(
        property = property,
        description = description,
        formatValue = { value, _ -> formatValue(value) }
    )


    @Composable
    override fun valueOrMissingText(
        fit: Fit,
        affectingItemInfo: SubsystemInfo,
        isCellDisplay: Boolean
    ): ValueOrMissing?{
        val property = property(affectingItemInfo.subsystem) ?: return null
        val valueText = formatValue(property.value, isCellDisplay)
        return valueText.valueOr(null)
    }


}


/**
 * The sources of tactical mode effects.
 */
private object SubsystemEffectSources{


    /**
     * Returns a standard percentage property source.
     */
    private fun percentagePropertySource(
        property: (Subsystem) -> AttributeProperty<Double>?,
        inverted: Boolean = false,
        description: String,
    ) = SubsystemPropertyEffectSource(
        property = property,
        description = description,
        formatValue = { value, isCellDisplay ->
            (if (inverted) -value else value).asStandardPercentageNullIfZero(isCellDisplay, withSign = true)
        }
    )


    /**
     * Returns a percentage property source for the core bonus property with the given index.
     */
    private fun coreBonusPctProperty(
        index: Int,
        description: String,
        inverted: Boolean = false,
    ) = percentagePropertySource(
        property = { it.coreBonusProperty(index) },
        inverted = inverted,
        description = description
    )


    /**
     * Returns a percentage property source for the defensive bonus property with the given index.
     */
    private fun defensiveBonusPctProperty(
        index: Int,
        description: String,
        inverted: Boolean = false,
    ) = percentagePropertySource(
        property = { it.defensiveBonusProperty(index) },
        inverted = inverted,
        description = description
    )


    /**
     * Returns a percentage property source for the offensive bonus property with the given index.
     */
    private fun offensiveBonusPctProperty(
        index: Int,
        description: String,
        inverted: Boolean = false,
    ) = percentagePropertySource(
        property = { it.offensiveBonusProperty(index) },
        inverted = inverted,
        description = description
    )


    /**
     * Returns a percentage property source for the propulsion bonus property with the given index.
     */
    private fun propulsionBonusPctProperty(
        index: Int,
        description: String,
        inverted: Boolean = false,
    ) = percentagePropertySource(
        property = { it.propulsionBonusProperty(index) },
        inverted = inverted,
        description = description
    )


    /**
     * The property source for the percentage bonus to power output.
     */
    val POWER_OUTPUT_PCT_BONUS = percentagePropertySource(
        property = Subsystem::powerOutputPercentageBonus,
        description = "power output",
    )


    /**
     * The property source for the percentage bonus to ship CPU output.
     */
    val CPU_OUTPUT_PCT_BONUS = percentagePropertySource(
        property = Subsystem::cpuOutputPercentageBonus,
        description = "cpu output"
    )


    /**
     * The property source for the flat bonus to power output.
     */
    val POWER_OUTPUT_FLAT_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::powerOutputFlatBonus,
        description = "power output",
        formatValue = { value -> value.asPower(withSign = true) }
    )


    /**
     * The property source for the bonus to ship CPU output, as a percentage.
     */
    val CPU_OUTPUT_FLAT_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::cpuOutputFlatBonus,
        description = "cpu output",
        formatValue = { value -> value.asCpu(withSign = true) }
    )


    /**
     * The property source for the flat bonus to capacitor capacity.
     */
    val CAPACITOR_CAPACITY_FLAT_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::capacitorCapacityFlatBonus,
        description = "capacitor capacity",
        formatValue = { value -> value.asCapacitorEnergy(withSign = true) }
    )


    /**
     * The property source for the percentage bonus to capacitor capacity on Core Reactor subsystems.
     */
    val CAPACITOR_CAPACITY_PCT_BONUS = coreBonusPctProperty(0, "capacitor capacity")


    /**
     * The property source for the bonus to capacitor recharge time on Core Reactor subsystems.
     */
    val CAPACITOR_RECHARGE_TIME_BONUS = coreBonusPctProperty(0, "capacitor recharge time")


    /**
     * The property source for the bonus to energy warfare resistance.
     */
    val ENERGY_WARFARE_RESISTANCE_BONUS = coreBonusPctProperty(1, "energy warfare resistance", inverted = true)


    /**
     * The property source for the bonus to sensor strength.
     */
    val SENSOR_STRENGTH_BONUS = coreBonusPctProperty(0, "sensor strength")


    /**
     * The property source for the bonus to targeting range.
     */
    val TARGETING_RANGE_BONUS = coreBonusPctProperty(1, "targeting range")


    /**
     * The property source for the bonus to scan resolution.
     */
    val SCAN_RESOLUTION_BONUS = coreBonusPctProperty(2, "scan resolution")


    /**
     * The property source for the bonus to max. locked targets.
     */
    val MAX_LOCKED_TARGET_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::maxLockedTargetsBonus,
        description = "max. locked targets",
        formatValue = { value -> value.toString(withSign = true) }
    )


    /**
     * The property source for the percentage bonus to shield hitpoints.
     */
    val SHIELD_HITPOINTS_PCT_BONUS = defensiveBonusPctProperty(0, "shield HP")


    /**
     * The property source for the percentage bonus to armor hitpoints.
     */
    val ARMOR_HITPOINTS_PCT_BONUS = defensiveBonusPctProperty(0, "armor HP")


    /**
     * The property source for the percentage bonus to shield and armor hitpoints.
     */
    val BUFFER_PCT_BONUS = defensiveBonusPctProperty(0, "shield and armor HP")


    /**
     * The property source for the bonus to overheating armor hardeners.
     */
    val ARMOR_HARDENER_OVERHEAT_BONUS = defensiveBonusPctProperty(1, "benefit of overheating armor hardeners")


    /**
     * The property source for the bonus to overheating shield hardeners.
     */
    val SHIELD_HARDENER_OVERHEAT_BONUS = defensiveBonusPctProperty(1, "benefit of overheating shield hardeners")


    /**
     * The property source for the bonus to overheating shield and armor hardeners.
     */
    val RESIST_HARDENER_OVERHEAT_BONUS = defensiveBonusPctProperty(1, "benefit of overheating shield and armor hardeners")


    /**
     * The property source for the flat bonus to shield hitpoints.
     */
    val SHIELD_HITPOINTS_FLAT_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::shieldFlatHpBonus,
        description = "shield HP",
        formatValue = { value -> value.asHitPoints(withSign = true, withUnits = false) }
    )


    /**
     * The property source for the flat bonus to armor hitpoints.
     */
    val ARMOR_HITPOINTS_FLAT_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::armorHpFlatBonus,
        description = "armor HP",
        formatValue = { value -> value.asHitPoints(withSign = true, withUnits = false) }
    )


    /**
     * The property source for the flat bonus to structure hitpoints.
     */
    val STRUCTURE_HITPOINTS_FLAT_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::structureHpFlatBonus,
        description = "structure HP",
        formatValue = { value -> value.asHitPoints(withSign = true, withUnits = false) }
    )


    /**
     * The property source for the penalty to signature radius.
     */
    val SIGNATURE_RADIUS_PENALTY = SubsystemPropertyEffectSource(
        property = Subsystem::signatureRadius,
        description = "signature radius",
        formatValue = { value -> value.asSignatureRadius(withSign = true) }
    )


    /**
     * The property source for the reduction in penalty of armor plate mass.
     */
    val REDUCTION_IN_ARMOR_PLATE_MASS_PENALTY = percentagePropertySource(
        property = Subsystem::bonusMassAddition,
        description = "armor plate mass penalty",
    )


    /**
     * The property source for the bonus to energy neutralizer and nosferatu drain amount.
     */
    val NEUT_AMOUNT_BONUS = coreBonusPctProperty(1, "energy draining amount")


    /**
     * The property source for the bonus to overheating neutralizer and nosferatu modules.
     */
    val OVERHEATING_NEUTS_BONUS = coreBonusPctProperty(2, "benefit of overheating energy draining modules")


    /**
     * The property source for the reduction in overheat damage by modules.
     */
    val REDUCTION_IN_OVERHEAT_DAMAGE = coreBonusPctProperty(0, "module overheating damage")


    /**
     * The property source for the reduction in fitting needs of energy neutralizer and nosferatu modules.
     */
    val REDUCTION_IN_FITTING_NEUTS = percentagePropertySource(
        property = Subsystem::energyNeutFittingReduction,
        description = "PG and CPU need for energy draining modules"
    )


    /**
     * Returns a property source for the repair amount bonus of shield boosters and/or armor repairers.
     */
    fun defensiveRepBonus(shipType: ShipType) = defensiveBonusPctProperty(
        index = 0,
        description = when {
            shipType.isLegion() || shipType.isProteus() -> "armor HP repaired"
            shipType.isTengu() -> "shield HP boosted"
            shipType.isLoki() -> "shield boost and armor repair amount"
            else -> "HP repaired"  // Just in case
        }
    )


    /**
     * Returns a property source for the bonus to overheating repairers.
     */
    fun defensiveRepOverheatBonus(shipType: ShipType) = defensiveBonusPctProperty(
        index = 2,
        description = when {
            shipType.isLegion() || shipType.isProteus() -> "benefit of overheating armor repairers"
            shipType.isTengu() -> "benefit of overheating shield boosters"
            shipType.isLoki() -> "benefit of overheating shield boosters and armor repairers"
            else -> "benefit of overheating repairers"  // Just in case
        }
    )


    /**
     * The property source for the bonus to scan probe strength.
     */
    val SCANNING_PROBE_STRENGTH_BONUS = defensiveBonusPctProperty(1, "bonus to scan probe strength")


    /**
     * The property source for the reduction in CPU need for fitting cloaking devices.
     */
    val REDUCTION_IN_CLOAK_CPU_NEED = SubsystemPropertyEffectSource(
        property = Subsystem::cloakingCpuNeedBonus,
        description = "CPU need for cloaking devices",
        formatValue = { value, isCellDisplay ->
            (100*(value-1)).asStandardPercentageNullIfZero(isCellDisplay, withSign = true)
        }
    )


    /**
     * The property source for the cloaking reactivation delay value.
     */
    val CLOAK_REACTIVATION_DELAY = SubsystemPropertyEffectSource(
        property = Subsystem::covertOpsAndReconOpsCloakModuleDelay,
        description = "cloak reactivation delay",
        formatValue = { value -> value.millisAsTimeSec() }
    )


    /**
     * The property source for the bonus to cargo capacity.
     */
    val CARGO_CAPACITY_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::cargoCapacityAdd,
        description = "cargo capacity",
        formatValue = { value -> value.asVolume(withSign = true) }
    )


    /**
     * The property source for the bonus to missile damage given by the "Assault Optimization" (Legion) subsystem.
     */
    val ASSAULT_OPTIMIZATION_MISSILE_DAMAGE_BONUS = offensiveBonusPctProperty(
        index = 1,
        description = "light, heavy and heavy assault missile damage"
    )


    /**
     * The property source for the bonus to missile damage given by the "Accelerated Ejection Bay" (Tengu) subsystem.
     */
    val ACCELERATED_EJECTION_BAY_MISSILE_DAMAGE_BONUS = offensiveBonusPctProperty(
        index = 1,
        description = "kinetic light, heavy and heavy assault missile damage"
    )


    /**
     * Returns a property source for the bonus to cruiser-sized missile launcher rate-of-fire.
     */
    fun mediumMissileLauncherRowBonus(propertyIndex: Int) = offensiveBonusPctProperty(
        index = propertyIndex,
        description = "cruiser-sized missile launcher rate of fire",
        inverted = true,
    )


    /**
     * Returns a property source for the bonus to heavy and heavy assault missile velocity.
     */
    fun mediumMissileVelocityBonusProperty(propertyIndex: Int) = offensiveBonusPctProperty(
        index = propertyIndex,
        description = "heavy and heavy assault missile velocity"
    )


    /**
     * The property source for the bonus to missile explosion velocity.
     */
    val MISSILE_EXPLOSION_VELOCITY_BONUS = offensiveBonusPctProperty(2, "missile explosion velocity")


    /**
     * The property source for the reduction in fitting needs of cruiser-sized missile launchers.
     */
    val REDUCTION_IN_MEDIUM_LAUNCHER_FITTING = percentagePropertySource(
        property = Subsystem::medMissileFittingReduction,
        description = "PG and CPU need for cruiser-sized missile launchers"
    )


    /**
     * The property source for the bonus to drone damage.
     */
    val DRONE_DAMAGE_BONUS = offensiveBonusPctProperty(2, "drone damage and HP")


    /**
     * The property source for the bonus to drone bandwidth.
     */
    val DRONE_BANDWIDTH_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::droneBandwidthBonus,
        description = "drone bandwidth",
        formatValue = { value -> value.asDroneBandwidth(withSign = true) }
    )


    /**
     * The property source for the bonus to drone bay capacity.
     */
    val DRONE_CAPACITY_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::droneCapacityBonus,
        description = "drone bay capacity",
        formatValue = { value -> value.asDroneCapacity(withSign = true) }
    )


    /**
     * The property source for the bonus to medium energy turret damage.
     */
    val MEDIUM_ENERGY_TURRET_DAMAGE_BONUS = offensiveBonusPctProperty(0, "medium energy turret damage")


    /**
     * The property source for the bonus to medium energy turret optimal range.
     */
    val MEDIUM_ENERGY_TURRET_OPTIMAL_RANGE_BONUS = offensiveBonusPctProperty(2, "medium energy turret optimal range")


    /**
     * The property source for the bonus to medium energy activation cost.
     */
    val MEDIUM_ENERGY_TURRET_ACTIVATION_COST_BONUS = offensiveBonusPctProperty(1, "medium energy turret activation cost")


    /**
     * The property source for the reduction in fitting requirements of medium energy turrets.
     */
    val REDUCTION_IN_MEDIUM_ENERGY_TURRET_FITTING = percentagePropertySource(
        property = Subsystem::medEnergyTurretFittingReduction,
        description = "PG and CPU need for medium energy turrets"
    )


    /**
     * The property source for the bonus to medium energy turret damage.
     */
    val MEDIUM_HYBRID_TURRET_DAMAGE_BONUS = offensiveBonusPctProperty(1, "medium hybrid turret damage")


    /**
     * The property source for the bonus to medium hybrid turret optimal range.
     */
    val MEDIUM_HYBRID_TURRET_OPTIMAL_RANGE_BONUS = offensiveBonusPctProperty(0, "medium hybrid turret optimal range")


    /**
     * The property source for the bonus to medium hybrid turret falloff.
     */
    val MEDIUM_HYBRID_TURRET_FALLOFF_BONUS = offensiveBonusPctProperty(0, "medium hybrid turret falloff")


    /**
     * The property source for the bonus to medium hybrid turret tracking speed.
     */
    val MEDIUM_HYBRID_TURRET_TRACKING_SPEED_BONUS = offensiveBonusPctProperty(2, "medium hybrid turret tracking speed")


    /**
     * The property source for the reduction in fitting requirements of medium hybrid turrets.
     */
    val REDUCTION_IN_MEDIUM_HYBRID_TURRET_FITTING = percentagePropertySource(
        property = Subsystem::medHybridTurretFittingReduction,
        description = "PG and CPU need for medium hybrid turrets"
    )


    /**
     * The property source for the bonus to medium projectile turret damage.
     */
    val MEDIUM_PROJECTILE_TURRET_DAMAGE_BONUS = offensiveBonusPctProperty(1, "medium projectile turret damage")


    /**
     * The property source for the bonus to medium projectile turret optimal and falloff.
     */
    val MEDIUM_PROJECTILE_TURRET_OPTIMAL_AND_FALLOFF_BONUS = offensiveBonusPctProperty(
        index = 0,
        description = "medium projectile turret optimal and falloff"
    )


    /**
     * The property source for the bonus to medium projectile turret tracking speed.
     */
    val MEDIUM_PROJECTILE_TURRET_TRACKING_SPEED_BONUS = offensiveBonusPctProperty(
        index = 2,
        description = "medium projectile turret tracking speed"
    )


    /**
     * The property source for the reduction in fitting requirements of medium projectile turrets.
     */
    val REDUCTION_IN_MEDIUM_PROJECTILE_TURRET_FITTING = percentagePropertySource(
        property = Subsystem::medProjectileTurretFittingReduction,
        description = "PG and CPU need for medium projectile turrets"
    )


    /**
     * The property source for the bonus to drone damage and hitpoints.
     */
    val DRONE_DAMAGE_AND_HITPOINTS_BONUS = offensiveBonusPctProperty(0, "drone damage and HP")


    /**
     * The property source for the bonus to drone velocity and tracking speed.
     */
    val DRONE_VELOCITY_AND_TRACKING_SPEED_BONUS = offensiveBonusPctProperty(1, "drone velocity and tracking speed")


    /**
     * The property source for the bonus to targeted ECM optimal range and strength.
     */
    val ECM_TARGETED_ECM_STRENGTH_AND_OPTIMAL_BONUS = coreBonusPctProperty(1, "targeted ECM optimal range and strength")


    /**
     * The property source for the bonus to overheating targeted ECM.
     */
    val OVERHEATING_TARGETED_ECM_BONUS = coreBonusPctProperty(2, "benefit of overheating targeted ECM")


    /**
     * The property source for the bonus to webifier range.
     */
    val WEB_RANGE_BONUS = coreBonusPctProperty(1, "stasis webifier range")


    /**
     * The property source for the bonus to benefit from webifier overheating.
     */
    val WEB_OVERHEATING_BONUS = coreBonusPctProperty(2, "benefit of overheating stasis webifiers")


    /**
     * The property source for the bonus to warp scrambler and disruptor range.
     */
    val WARP_DISRUPTION_RANGE_BONUS = coreBonusPctProperty(1, "warp scrambler and disruptor range")


    /**
     * The property source for the bonus to benefit from warp scrambler and disruptor overheating.
     */
    val WARP_DISRUPTION_OVERHEATING_BONUS = coreBonusPctProperty(2, "benefit of overheating warp scramblers and disruptors")


    /**
     * Returns a property source for the command burst bonus.
     */
    fun offensiveCommandBurstBonus(shipType: ShipType) = offensiveBonusPctProperty(
        index = 0,
        description = when {
            shipType.isLoki() -> "armor, shield and skirmish "
            shipType.isTengu() -> "shield, skirmish and information "
            shipType.isLegion() || shipType.isProteus() -> "armor, skirmish and information "
            else -> ""
        } + "command burst strength and duration"
    )


    /**
     * Returns a property source for the reduction in remote repair activation cost.
     */
    fun offensiveRemoteRepairActivationReduction(shipType: ShipType) = offensiveBonusPctProperty(
        index = 1,
        description = "activation cost of " + when {
            shipType.isLoki() -> "remote shield boosters and armor repairers"
            shipType.isTengu() -> "remote shield boosters"
            shipType.isLegion() || shipType.isProteus() -> "remote armor repairers"
            else -> "remote repairers"
        }
    )


    /**
     * Returns a property source for the bonus to overheating remote repairs.
     */
    fun offensiveRemoteRepairOverheatingBonus(shipType: ShipType) = offensiveBonusPctProperty(
        index = 2,
        description = "benefit of overheating " + when {
            shipType.isLoki() -> "remote shield boosters and armor repairers"
            shipType.isTengu() -> "remote shield boosters"
            shipType.isLegion() || shipType.isProteus() -> "remote armor repairers"
            else -> "remote repairers"
        }
    )


    /**
     * The property source for the reduction in fitting requirements for medium remote shield boosters.
     */
    val REDUCTION_IN_MED_REMOTE_SHIELD_BOOSTER_FITTING = percentagePropertySource(
        property = Subsystem::medRsbFittingReduction,
        description = "PG and CPU need for medium remote shield boosters"
    )


    /**
     * The property source for the reduction in fitting requirements for medium remote armor repairers.
     */
    val REDUCTION_IN_MED_REMOTE_ARMOR_REPAIRER_FITTING = percentagePropertySource(
        property = Subsystem::medRarFittingReduction,
        description = "PG and CPU need for medium remote armor repairers"
    )


    /**
     * The property source for the reduction in fitting requirements for command bursts.
     */
    val REDUCTION_IN_COMMAND_BURST_FITTING = percentagePropertySource(
        property = Subsystem::commandBurstFittingReduction,
        description = "PG and CPU need for command bursts"
    )


    /**
     * The property source for the bonus to remote armor repairer optimal range.
     */
    val REMOTE_ARMOR_REPAIRER_OPTIMAL_BONUS = percentagePropertySource(
        property = Subsystem::remoteArmorRepairerOptimalBonus,
        description = "remote armor repairer optimal range"
    )


    /**
     * The property source for the reduction in remote armor repairer falloff.
     */
    val REMOTE_ARMOR_REPAIRER_FALLOFF_BONUS = percentagePropertySource(
        property = Subsystem::remoteArmorRepairerFalloffBonus,
        description = "remote armor repairer falloff"
    )


    /**
     * The property source for the reduction in remote shield booster falloff.
     */
    val REMOTE_SHIELD_BOOSTER_FALLOFF_BONUS = percentagePropertySource(
        property = Subsystem::remoteShieldBoosterFalloffBonus,
        description = "remote shield booster falloff"
    )


    /**
     * The property source for the bonus to the range of command bursts.
     */
    val COMMAND_BURST_RANGE_BONUS = percentagePropertySource(
        property = Subsystem::commandBurstRange,
        description = "range of command burst effect"
    )


    /**
     * The property source for the bonus to warp speed.
     */
    val WARP_SPEED_BONUS = propulsionBonusPctProperty(1, "warp speed")


    /**
     * The property source for the bonus to warp speed (it's inverted on the Proteus for some reason).
     */
    val WARP_SPEED_INVERTED_BONUS = propulsionBonusPctProperty(1, "warp speed", inverted = true)


    /**
     * The property source for the bonus to capacitor need for warp.
     */
    val WARP_CAPACITOR_NEED_BONUS = propulsionBonusPctProperty(0, "capacitor need for warp")


    /**
     * The property source for the bonus to interdiction nullifier duration.
     */
    val INTERDICTION_NULLIFIER_DURATION_BONUS = percentagePropertySource(
        property = Subsystem::shipBonusRole2,
        description = "interdiction nullifier duration",
    )


    /**
     * The property source for the bonus to interdiction nullifier reactivation delay.
     */
    val REDUCTION_IN_INTERDICTION_NULLIFIER_REACTIVATION_DELAY = percentagePropertySource(
        property = Subsystem::shipBonusRole1,
        description = "interdiction nullifier reactivation delay",
    )


    /**
     * The property source for the reduction in the interdiction nullifier max. targeting range penalty.
     */
    val REDUCTION_IN_INTERDICTION_NULLIFIER_MAX_TARGETING_RANGE_PENALTY = percentagePropertySource(
        property = Subsystem::shipBonusRole1,
        description = "interdiction nullifier max. targeting range penalty",
    )


    /**
     * The property source for the penalty to agility.
     */
    val INERTIA_MODIFIER_PENALTY = percentagePropertySource(
        property = Subsystem::agilityBonusAdd,
        description = "inertia modifier",
    )


    /**
     * The property source for the bonus to max. targeting range.
     */
    val MAX_TARGETING_RANGE_BONUS = SubsystemPropertyEffectSource(
        property = Subsystem::maxTargetRangeBonus,
        description = "targeting range",
        formatValue = { value -> value.asDistance(withSign = true) }
    )


    /**
     * The property source for the bonus to afterburner speed.
     */
    val AFTERBURNER_SPEED_BONUS = propulsionBonusPctProperty(0, "afterburner speed")


    /**
     * The property source for the reduction in the microwarpdrive signature radius penalty.
     */
    val REDUCTION_IN_MWD_SIG_RADIUS_PENALTY = propulsionBonusPctProperty(1, "microwarpdrive signature radius penalty")


    /**
     * The property source for the bonus to afterburner and microwarpdrive overheating.
     */
    val PROPMOD_OVERHEATING_BONUS = propulsionBonusPctProperty(1, "benefit of overheating ABs and MWDs")


    /**
     * The property source for the reduction in afterburner and microwarpdrive activation cost.
     */
    val REDUCTION_IN_PROPMOD_ACTIVATION_COST = propulsionBonusPctProperty(0, "reduction in activation cost of ABs and MWDs")


    /**
     * The property source for the bonus to maximum ship speed.
     */
    val MAX_SPEED_BONUS = propulsionBonusPctProperty(0, "max. velocity")


    /**
     * The property source for the bonus to agility.
     */
    val AGILITY_BONUS = propulsionBonusPctProperty(1, "agility", inverted = true)


}
