/**
 * Defines the default values of various Eve items or states.
 */

package theorycrafter.fits

import eve.data.EveData
import eve.data.ModuleType
import eve.data.ShipType
import eve.data.SubsystemType
import eve.data.TacticalModeType
import eve.data.typeid.isAssaultDamageControl
import eve.data.typeid.isAutoTargetingSystem
import eve.data.typeid.isCapitalEmergencyHullEnergizer
import eve.data.typeid.isCargoScanner
import eve.data.typeid.isCloakingDevice
import eve.data.typeid.isCovertJumpPortalGenerator
import eve.data.typeid.isCynosuralFieldGenerator
import eve.data.typeid.isIntegratedSensorArray
import eve.data.typeid.isInterdictionNullifier
import eve.data.typeid.isJumpPortalGenerator
import eve.data.typeid.isLegion
import eve.data.typeid.isLoki
import eve.data.typeid.isMicroJumpDrive
import eve.data.typeid.isMicroJumpFieldGenerator
import eve.data.typeid.isMiningSurveyChipset
import eve.data.typeid.isNetworkedSensorArray
import eve.data.typeid.isPanic
import eve.data.typeid.isPassiveTargetingSystem
import eve.data.typeid.isProteus
import eve.data.typeid.isSignatureRadiusSuppressor
import eve.data.typeid.isTengu
import eve.data.typeid.isWarpCoreStabilizer
import eve.data.typeid.isZeroPointMassEntangler
import eve.data.utils.ValueByEnum
import eve.data.utils.mapValues
import theorycrafter.fitting.Fit
import theorycrafter.fitting.Module
import theorycrafter.fitting.Subsystem


/**
 * Returns the initial state to put the given module in when fitting it.
 */
context(EveData)
fun ModuleType.defaultState(): Module.State {
    if (!isActivable)
        return Module.State.ONLINE

    return when {
        isAssaultDamageControl() ||
                isCapitalEmergencyHullEnergizer() ||
                isMicroJumpDrive() ||
                isMicroJumpFieldGenerator() ||
                isSignatureRadiusSuppressor() ||
                isCynosuralFieldGenerator() ||
                isJumpPortalGenerator() ||
                isCovertJumpPortalGenerator() ||
                isInterdictionNullifier() ||
                isWarpCoreStabilizer() ||
                isCargoScanner() ||
                isCloakingDevice() ||
                isPassiveTargetingSystem() ||
                isAutoTargetingSystem() ||
                isMiningSurveyChipset() ||
                isPanic() ||
                isZeroPointMassEntangler() ||
                isNetworkedSensorArray() ||
                isIntegratedSensorArray()
            -> Module.State.ONLINE
        else
            -> Module.State.ACTIVE
    }
}


/**
 * Returns the initial tactical mode that should be set on the given ship; `null` if none.
 */
context(EveData)
fun ShipType.defaultTacticalMode(): TacticalModeType? {
    val tacticalModes = tacticalModeTypesOrNull(this) ?: return null
    return tacticalModes[TacticalModeType.Kind.DEFENSE]
}


/**
 * Returns the initial subsystems that should be set on the given ship that uses subsystems.
 */
context(EveData)
fun ShipType.defaultSubsystems(): ValueByEnum<SubsystemType.Kind, SubsystemType> {
    val subsystemsByKind = subsystemTypes(this)
    return when {
        isTengu() -> subsystemsByKind.selectNamed(TENGU_INITIAL_SUBSYSTEM_NAMES)
        isLoki() -> subsystemsByKind.selectNamed(LOKI_INITIAL_SUBSYSTEM_NAMES)
        isLegion() -> subsystemsByKind.selectNamed(LEGION_INITIAL_SUBSYSTEM_NAMES)
        isProteus() -> subsystemsByKind.selectNamed(PROTEUS_INITIAL_SUBSYSTEM_NAMES)
        else -> subsystemsByKind.mapValues { it.minByOrNull(SubsystemType::itemId)!! }  // Must pick something
    }
}


/**
 * Returns the initial subsystems that should be set on the given ship; `null` if the ship does not use subsystems.
 */
context(EveData)
fun ShipType.defaultSubsystemsOrNull(): ValueByEnum<SubsystemType.Kind, SubsystemType>? {
    return if (usesSubsystems) defaultSubsystems() else null
}


/**
 * The names of Tengu subsystems to fit initially.
 */
private val TENGU_INITIAL_SUBSYSTEM_NAMES = setOf(
    "Tengu Core - Augmented Graviton Reactor",
    "Tengu Defensive - Amplification Node",
    "Tengu Offensive - Accelerated Ejection Bay",
    "Tengu Propulsion - Fuel Catalyst"
)


/**
 * The names of Loki subsystems to fit initially.
 */
private val LOKI_INITIAL_SUBSYSTEM_NAMES = setOf(
    "Loki Core - Immobility Drivers",
    "Loki Defensive - Adaptive Defense Node",
    "Loki Offensive - Launcher Efficiency Configuration",
    "Loki Propulsion - Wake Limiter"
)


/**
 * The names of Legion subsystems to fit initially.
 */
private val LEGION_INITIAL_SUBSYSTEM_NAMES = setOf(
    "Legion Core - Augmented Antimatter Reactor",
    "Legion Defensive - Nanobot Injector",
    "Legion Offensive - Liquid Crystal Magnifiers",
    "Legion Propulsion - Wake Limiter"
)

/**
 * The names of Proteus subsystems to fit initially.
 */
private val PROTEUS_INITIAL_SUBSYSTEM_NAMES = setOf(
    "Proteus Core - Friction Extension Processor",
    "Proteus Defensive - Nanobot Injector",
    "Proteus Offensive - Hybrid Encoding Platform",
    "Proteus Propulsion - Hyperspatial Optimization"
)


/**
 * Selects the subsystems with the given names.
 */
private fun ValueByEnum<SubsystemType.Kind, List<SubsystemType>>.selectNamed(names: Set<String>) =
    mapValues { subsystems ->
        subsystems.find { it.name in names }!!
    }


/**
 * Because we always fit all subsystems, we can provide this extension, where the subsystems are never `null`.
 */
@Suppress("UNCHECKED_CAST")
val Fit.requiredSubsystemByKind: ValueByEnum<SubsystemType.Kind, Subsystem>?
    get() = subsystemByKind as ValueByEnum<SubsystemType.Kind, Subsystem>?