package theorycrafter.fitting

import eve.data.ChargeType
import eve.data.DamagePattern
import eve.data.DamageType


/**
 * A damage effect on a fit.
 */
sealed interface DamageEffect {


    /**
     * The target fit of the effect.
     */
    val fit: theorycrafter.fitting.Fit


    /**
     * The damage pattern of this effect.
     */
    val damagePattern: DamagePattern


    /**
     * Whether this damage effect is enabled.
     */
    val enabled: Boolean


    /**
     * Sets the enabled state of this damage effect.
     */
    fun setEnabled(scope: FittingEngine.ModificationScope, isEnabled: Boolean)


    /**
     * A base [DamageEffect] implementation state that also implements [FitItemWithEnabledState].
     */
    sealed class DamageEffectWithEnabledState: DamageEffect, FitItemWithEnabledState {

        override val enabledState = EnabledState()

        override val enabled: Boolean
            get() = enabledState.value

        override fun setEnabled(scope: FittingEngine.ModificationScope,isEnabled: Boolean) {
            with(scope) {
                setEnabled(isEnabled)
            }
        }

    }


    /**
     * The given amount of damage per second, of the given type.
     */
    class ExactType(
        override val fit: theorycrafter.fitting.Fit,
        val damageType: DamageType,
        val damage: Double
    ) : DamageEffectWithEnabledState() {

        override val damagePattern = DamagePattern {
            if (it == damageType) damage else 0.0
        }

        override fun toString() = "DamageEffect[$damage x $damageType]"

    }


    /**
     * The damage implied by a fit.
     */
    class Fit(
        target: theorycrafter.fitting.Fit,
        val amount: Int,
        source: theorycrafter.fitting.Fit,
    ) : DamageEffect, RemoteEffect(target = target, source = source) {

        override val damagePattern: DamagePattern
            get() = source.firepower.dpsPattern * amount

        override val enabled: Boolean
            get() = enabledState.value

        override fun setEnabled(scope: FittingEngine.ModificationScope,isEnabled: Boolean) {
            with(scope) {
                setEnabled(isEnabled)
            }
        }

        override fun toString() = "DamageEffect[by $amount x $source]"

    }


    /**
     * The damage implied by the given ammo.
     */
    class Ammo(
        override val fit: theorycrafter.fitting.Fit,
        val amount: Int,
        val chargeType: ChargeType,
    ) : DamageEffectWithEnabledState() {

        override val damagePattern: DamagePattern
            get() = (chargeType.damagePattern ?: DamagePattern.None) * amount

        override fun toString() = "DamageEffect[by $amount x $chargeType]"

    }


    /**
     * The damage implied by the given drone group.
     */
    class Drone(
        override val fit: theorycrafter.fitting.Fit,
        val droneGroup: DroneGroup
    ) : DamageEffect {

        override val damagePattern: DamagePattern
            get() = droneGroup.totalDpsPattern ?: DamagePattern.None

        override val enabled: Boolean
            get() = droneGroup.active

        override fun setEnabled(scope: FittingEngine.ModificationScope,isEnabled: Boolean) {
            with(scope) {
                droneGroup.setActive(isEnabled)
            }
        }

        override fun toString() = "DamageEffect[by $droneGroup]"
    }


}


/**
 * Sets the enabled state of this damage effect with the given modification scope.
 *
 * This allows calling [DamageEffect.setEnabled] in a more convenient way.
 */
context(FittingEngine.ModificationScope)
fun DamageEffect.setEnabled(isEnabled: Boolean) = setEnabled(this@ModificationScope, isEnabled)