package theorycrafter.fitting

import androidx.compose.runtime.Stable
import eve.data.Attributes
import eve.data.ChargeType
import eve.data.DamageType
import eve.data.SensorType
import eve.data.utils.ValueByEnum
import eve.data.utils.valueByEnum


/**
 * An instance of a charge/script that can be loaded into a module.
 */
@Stable
class Charge internal constructor(


    /**
     * The module this charge is fitted into.
     */
    val module: Module,


    /**
     * The context [Attributes].
     */
    attributes: Attributes,


    /**
     * The charge type.
     */
    type: ChargeType


) : EveItem<ChargeType>(attributes, type), FitItem {


    /**
     * The fit this charge is part of.
     */
    override val fit: Fit
        get() = module.fit


    /**
     * The volley damage of each type done by this charge; `null` if this charge does no damage of that type.
     */
    val volleyDamageByType: ValueByEnum<DamageType, AttributeProperty<Double>?> by lazy{
        valueByEnum { damageType ->
            propertyOrNull(attributes.damage[damageType])
        }
    }


    /**
     * The mass of the charge.
     */
    val mass: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.mass)


    /**
     * The inertia modifier of the charge (e.g. missile).
     */
    val inertiaModifier: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.inertiaModifier)


    /**
     * The missile velocity, in m/s, if this charge is a missile (or bomb); `null` otherwise.
     */
    val missileVelocity: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.maxVelocity)


    /**
     * The missile flight time, in milliseconds, if this charge is a missile, bomb or probe.
     */
    val missileFlightTime: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.missileFlightTime)


    /**
     * The missile range, in meters, if this charge is a missile (or bomb); `null` otherwise.
     */
    val missileRange: MissileRange?
        get() {
            val velocity = missileVelocity?.doubleValue ?: return null
            val flightTime = missileFlightTime?.doubleValue ?: return null
            val mass = mass?.doubleValue ?: return null
            val inertiaModifier = inertiaModifier?.doubleValue ?: return null
            val shipRadius = module.fit.ship.radius.doubleValue

            return missileRange(
                velocity = velocity,
                flightTimeMs = flightTime,
                mass = mass,
                inertiaModifier = inertiaModifier,
                shipRadius = shipRadius
            )
        }


    /**
     * The missile (and bomb) explosion radius.
     */
    val missileExplosionRadius: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.aoeCloudSize)


    /**
     * The missile explosion velocity.
     */
    val missileExplosionVelocity: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.aoeVelocity)


    /**
     * The missile damage reduction factor.
     */
    val missileDamageReductionFactor: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.aoeDamageReductionFactor)


    /**
     * Explosion range (area-of-effect) of bombs.
     */
    val explosionRange: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.explosionRange)


    /**
     * Effect radius of warp disruption probes.
     */
    val warpDisruptionRange: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.warpScrambleRange)


    /**
     * Effect radius of webification probes.
     */
    val stasisWebificationRange: AttributeProperty<Double>?
        get() = aoeRange


    /**
     * Effect strength of webification probes.
     */
    val stasisWebificationStrength: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.speedFactor)


    /**
     * Base maximum deviation of scan probes, in AU.
     */
    val baseMaxScanDeviation: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.baseMaxScanDeviation)


    /**
     * Base scan range of scan probes, in AU.
     */
    val baseScanRange: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.baseScanRange)


    /**
     * Base sensor strength of scan probes.
     */
    val baseScanSensorStrength: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.baseScanSensorStrength)


    /**
     * The ECM strength of this bomb against each sensor type; `null` if this is not an ECM (lockbreaker) bomb.
     */
    val ecmStrength: ValueByEnum<SensorType, AttributeProperty<Double>?> by lazy {
        valueByEnum { sensorType ->
            propertyOrNull(attributes.ecmStrength[sensorType])
        }
    }


    /**
     * The amount of energy this neutralization (void) bomb neutralizes from the target.
     */
    val energyNeutralized: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.energyNeutralizerAmount)


    /**
     * The max absolute damage-over-time done by this charge per tick; `null` if none.
     */
    val dotMaxDamagePerTick: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.dotMaxDamagePerTick)


    /**
     * The max percentage-of-hp damage-over-time done by this charge per tick; `null` if none.
     */
    val dotMaxHpPctDamagePerTick: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.dotMaxHpPctDamagePerTick)


    /**
     * The lifetime duration of this damage-over-time charge.
     */
    val dotDuration: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.dotDuration)


    /**
     * The range of an AOE effect.
     */
    val aoeRange: AttributeProperty<Double>?
        get() = propertyOrNull(attributes.aoeRange)


}