package theorycrafter.fitting

import eve.data.*
import eve.data.utils.ValueByEnum
import eve.data.utils.valueByEnum
import theorycrafter.fitting.utils.sumOfNullable


/**
 * Resonance (1-resist) values of an item.
 */
class ItemResonances(
    private val item: EveItem<*>,
    private val attributeByDamageType: ValueByEnum<DamageType, Attribute<Double>>
) {


    /**
     * Returns the resonance for the given damage type.
     */
    operator fun get(damageType: DamageType): AttributeProperty<Double> =
        item.property(attributeByDamageType[damageType])


    /**
     * All the resonances.
     */
    val all: ValueByEnum<DamageType, Double>
        get() = valueByEnum { damageType -> get(damageType).doubleValue }


    override fun toString() = DamageType.entries.map{ get(it) }.joinToString(separator = ", ") {
        (1-it.doubleValue).fractionAsPercentage(1)
    }


}


/**
 * The properties of an item's defensive layer (e.g. structure, armor, shield).
 */
sealed class ItemDefense(
    val name: String,
    protected val item: EveItem<*>,
    private val hpAttribute: Attribute<Double>,
    resonancesAttributes: ValueByEnum<DamageType, Attribute<Double>>,
    private val damagePattern: () -> DamagePattern,
    private val localModules: () -> Iterable<Module>,
    private val friendlyEffects: () -> Iterable<RemoteEffect>,
) {


    /**
     * Total hitpoints of this layer.
     */
    val hp: AttributeProperty<Double>
        get() = item.property(hpAttribute)


    /**
     * The resonances of this layer.
     */
    val resonances = ItemResonances(item, resonancesAttributes)


    /**
     * Returns amount of HP in this layer repaired by the module or drone.
     */
    abstract val ModuleOrDrone<*>.hpRepairedPerSecondInLayer: Double?


    /**
     * The total EHP of this layer.
     */
    val ehp: Double
        get() = hp.doubleValue.toEhp()


    /**
     * Returns the resist factor of this layer; the value by which HP is multiplied to obtain EHP.
     */
    val resistFactor: Double
        get() = resistFactor(resonances = resonances.all, damagePattern = damagePattern())


    /**
     * Converts the given HP value to EHP.
     */
    fun Double.toEhp() = this * resistFactor


    /**
     * The total amount of hitpoints of this layer repaired per second by local modules.
     */
    val hpRepairedLocally: Double
        get() = localModules().sumOfNullable {
            if (it.type.isProjected)
                0.0
            else
                it.hpRepairedPerSecondInLayer
        }


    /**
     * The total amount of hitpoints of this layer repaired per second by (friendly) remote effects.
     */
    val hpRepairedRemotely: Double
        get() = friendlyEffects().sumOfNullable { remoteEffect ->
            val moduleReps = remoteEffect.source.modules.active.sumOfNullable {
                if (it.type.isProjected)
                    it.hpRepairedPerSecondInLayer
                else
                    0.0
            }
            val droneReps = remoteEffect.source.drones.active.sumOfNullable { droneGroup ->
                droneGroup.hpRepairedPerSecondInLayer?.let {
                    it * droneGroup.size
                }
            }

            return@sumOfNullable moduleReps + droneReps
        }


    /**
     * The total amount of effective hitpoints of this layer repaired per second by local modules.
     */
    val ehpRepairedLocally: Double
        get() = hpRepairedLocally.toEhp()


    /**
     * The total amount of effective hitpoints of this layer repaired per second by (friendly) remote effects.
     */
    val ehpRepairedRemotely: Double
        get() = hpRepairedRemotely.toEhp()


}


/**
 * Shield defense.
 */
class ItemShield(
    item: EveItem<*>,
    private val attributes: Attributes,
    damagePattern: () -> DamagePattern,
    localModules: () -> Iterable<Module>,
    friendlyEffects: () -> Iterable<RemoteEffect>,
): ItemDefense(
    name = "Shield",
    item = item,
    hpAttribute = attributes.shieldHp,
    resonancesAttributes = attributes.shieldResonance,
    damagePattern = damagePattern,
    localModules = localModules,
    friendlyEffects = friendlyEffects,
) {


    /**
     * The amount of shield HP repaired by the module/drone.
     */
    override val ModuleOrDrone<*>.hpRepairedPerSecondInLayer: Double?
        get() = shieldHpBoostedPerSecond


    /**
     * Shield recharge time, in milliseconds.
     */
    val rechargeTime: AttributeProperty<Double>
        get() = item.property(attributes.shieldRechargeTime)


    /**
     * Peak shield regeneration rate, in HP/sec.
     */
    val peakRegenHpPerSecond: Double
        get() = maxRechargeRate(hp.doubleValue, rechargeTime.doubleValue)


    /**
     * Peak shield regeneration rate, in EHP/sec.
     */
    val peakRegenEhpPerSecond: Double
        get() = peakRegenHpPerSecond.toEhp()


}



/**
 * Armor defense.
 */
class ItemArmor(
    item: EveItem<*>,
    attributes: Attributes,
    damagePattern: () -> DamagePattern,
    localModules: () -> Iterable<Module>,
    friendlyEffects: () -> Iterable<RemoteEffect>,
): ItemDefense(
    name = "Armor",
    item = item,
    hpAttribute = attributes.armorHp,
    resonancesAttributes = attributes.armorResonance,
    damagePattern = damagePattern,
    localModules = localModules,
    friendlyEffects = friendlyEffects,
) {

    /**
     * The amount of armor HP repaired by the module/drone.
     */
    override val ModuleOrDrone<*>.hpRepairedPerSecondInLayer: Double?
        get() = armorHpRepairedPerSecond


}


/**
 * Structure defense.
 */
class ItemStructure(
    item: EveItem<*>,
    attributes: Attributes,
    damagePattern: () -> DamagePattern,
    localModules: () -> Iterable<Module>,
    friendlyEffects: () -> Iterable<RemoteEffect>,
): ItemDefense(
    name = "Structure",
    item = item,
    hpAttribute = attributes.structureHp,
    resonancesAttributes = attributes.structureResonance,
    damagePattern = damagePattern,
    localModules = localModules,
    friendlyEffects = friendlyEffects,
) {


    /**
     * The property of the amount of structure HP repaired by the module/drone.
     */
    override val ModuleOrDrone<*>.hpRepairedPerSecondInLayer: Double?
        get() = structureHpRepairedPerSecond


}


/**
 * The properties of an item's defense layers.
 */
class ItemDefenses(
    item: EveItem<*>,
    attributes: Attributes,
    damagePattern: () -> DamagePattern,
    localModules: () -> Iterable<Module>,
    friendlyEffects: () -> Iterable<RemoteEffect>,
) {


    /**
     * Shield properties.
     */
    val shield = ItemShield(item, attributes, damagePattern, localModules, friendlyEffects)


    /**
     * Armor properties.
     */
    val armor = ItemArmor(item, attributes, damagePattern, localModules, friendlyEffects)


    /**
     * Structure (hull) properties.
     */
    val structure = ItemStructure(item, attributes, damagePattern, localModules, friendlyEffects)


    /**
     * The total hitponts of the fit, across all defenses.
     */
    val hp: Double
        get() = shield.hp.doubleValue + armor.hp.doubleValue + structure.hp.doubleValue


    /**
     * The total effective hitpoints of the fit, across all defenses.
     */
    val ehp: Double
        get() = shield.ehp + armor.ehp + structure.ehp


    /**
     * The total EHP repaired locally, across all defenses.
     */
    val ehpRepairedLocally: Double
        get() = shield.ehpRepairedLocally + armor.ehpRepairedLocally + structure.ehpRepairedLocally


    /**
     * The total EHP repaired by remote effects, across all defenses.
     */
    val ehpRepairedByRemoteEffects: Double
        get() = shield.ehpRepairedRemotely +
                armor.ehpRepairedRemotely +
                structure.ehpRepairedRemotely


}
