package theorycrafter.tournaments.impl.ag

import eve.data.*
import eve.data.typeid.*
import theorycrafter.fitting.Fit
import theorycrafter.tournaments.*
import theorycrafter.tournaments.TournamentRules.FittingRules
import theorycrafter.ui.fiteditor.*
import theorycrafter.utils.with


/**
 * The base class for Anger Games composition rules.
 */
open class AngerGamesCompositionRules(
    eveData: EveData,
    maxCompositionSize: Int,
    maxCompositionCost: Int,
    shipCostByName: Map<String, Int>,
): BasicPointsCompositionRules(
    eveData = eveData,
    maxCompositionSize = maxCompositionSize,
    maxCompositionCost = maxCompositionCost,
    shipCostByType = shipCostByName.mapKeys { (name, _) -> eveData.shipType(name) },
) {

    override fun compositionShipsIllegalityReason(composition: Composition): List<String?> {
        with(eveData) {
            return shipIllegalityBySizeClass(
                composition = composition,
                maxInSizeClass = {
                    when (it) {
                        ShipSizeClass.LOGI_CRUISER -> 1
                        ShipSizeClass.LOGI_FRIGATE -> 2
                        else -> 3
                    }
                },
                ::standardLogiLegalityCheck
            )
        }
    }

}


/**
 * The base class for fitting rules for Anger Games.
 */
open class AngerGamesFittingRules(
    protected val eveData: EveData,
    private val svipulAndConfessorCanBeLogistics: Boolean = false,
): FittingRules {


    /**
     * Whether the given ship type is a Svipul or Confessor.
     */
    private val ShipType.isSvipulOrConfessor: Boolean
        get() = name == "Svipul" || name == "Confessor"


    override fun isModuleLegal(moduleType: ModuleType, shipType: ShipType, isFlagship: Boolean): Boolean {
        with(eveData) {
            if (!moduleType.isTech1Item && !moduleType.isTech2Item)
                return false

            return when {
                moduleType.isRemoteRepairer -> shipType.isLogistics || (svipulAndConfessorCanBeLogistics && shipType.isSvipulOrConfessor)
                moduleType.isRemoteCapacitorTransmitter() -> isRemoteCapacitorTransmitterLegal(shipType)
                moduleType.isEcm() || moduleType.isBurstJammer() -> isEcmLegal(moduleType, shipType)
                moduleType.isWeaponDisruptor() -> isWeaponDisruptorLegal(moduleType, shipType)
                moduleType.isRemoteSensorDampener() -> isRemoteSensorDampenerLegal(moduleType, shipType)
                moduleType.isMicroJumpFieldGenerator() -> false
                moduleType.isBastionModule() -> false
                moduleType.isCloakingDevice() -> false
                moduleType.isRig -> moduleType.isTech1Item
                else -> true
            }
        }
    }


    override fun fitModulesLegality(moduleTypes: List<ModuleType>, shipType: ShipType, isFlagship: Boolean): List<String?> {
        with(eveData) {
            val regularIllegality = moduleTypes.map {
                if (isModuleLegal(it, shipType, isFlagship)) null else "Module is illegal here"
            }
            val illegalityByGroup = moduleIllegalityByGroup(
                moduleTypes = moduleTypes,
                maxInGroup = {
                    when (it) {
                        groups.armorPlate -> if (shipType.isBattleship) 1 else Unlimited
                        groups.shieldExtender -> if (shipType.isBattleship) 2 else Unlimited
                        groups.remoteCapacitorTransmitter -> 1
                        groups.ancillaryShieldBooster -> 1
                        groups.warpDisruptFieldGenerator -> 1
                        else -> Unlimited
                    }
                },
                additionalChecks = arrayOf(
                    { group, countByGroup ->
                        if (shipType.isBattleship)
                            standardBattleshipBufferTankCheck(group, countByGroup)
                        else
                            null
                    }
                )
            )

            return combineIllegalityReasons(regularIllegality, illegalityByGroup)
        }
    }


    override fun isChargeLegal(chargeType: ChargeType?, moduleType: ModuleType): Boolean {
        with(eveData) {
            if (moduleType.isInterdictionSphereLauncher())
                return (chargeType != null) && chargeType.isStasisWebificationProbe
            return isChargeLegal(chargeType)
        }
    }


    override fun isDroneLegal(droneType: DroneType): Boolean {
        return with(eveData) {
            when {
                droneType.name == "Gecko" -> false
                droneType.isSentry -> droneType.isTech1Item
                droneType.isCombatDrone() -> droneType.isTech1Item || droneType.isEmpireNavyFactionItem
                droneType.isShieldMaintenanceBot() ||
                        droneType.isArmorMaintenanceBot() ||
                        droneType.isHullMaintenanceBot() -> droneType.isTech1Item
                droneType.isStasisWebifyingDrone() ||
                        droneType.isTargetPaintingDrone() ||
                        droneType.isEnergyNeutralizerDrone() ||
                        droneType.isEcmDrone() ||
                        droneType.isSensorDampeningDrone() ||
                        droneType.isTrackingDisruptingDrone() -> true
                else -> false
            }
        }
    }


    override fun isImplantLegal(implantType: ImplantType): Boolean {
        return implantType.is010203Hardwiring
    }


    override fun isCargoItemLegal(itemType: EveItemType): Boolean {
        with(eveData) {
            if (itemType.isCargoContainer)
                return false

            return when (itemType) {
                is ChargeType -> isChargeLegal(itemType)
                is ImplantType -> isImplantLegal(itemType)
                is BoosterType -> isBoosterLegal(itemType)
                else -> true
            }
        }
    }


    override fun preloadedCharge(
        fit: Fit,
        moduleType: ModuleType,
        charges: Collection<ChargeType>
    ): Pair<Boolean, ChargeType?> {
        with(eveData) {
            val chargeType = when {
                moduleType.isEnergyWeapon() -> forEnergyTurret(fit.ship.type, charges)
                moduleType.isHybridWeapon() -> forHybridTurret(fit.ship.type, charges)
                moduleType.isProjectileWeapon() -> forProjectileTurret(charges)
                moduleType.isMissileLauncher() -> forMissileLauncher(fit, moduleType, charges)
                moduleType.isWarpDisruptionFieldGenerator() -> charges.find { it.isFocusedWarpScramblingScript }
                else -> return Pair(false, null)
            }

            return Pair(true, chargeType)
        }
    }


    override fun hasPreloadedCharge(
        fit: Fit,
        moduleType: ModuleType,
        charges: Collection<ChargeType>,
    ): Pair<Boolean, Boolean> {
        val hasTournamentSpecificCharge = with(eveData, moduleType) {
            isEnergyWeapon() ||
                    isHybridWeapon() ||
                    isProjectileWeapon() ||
                    isMissileLauncher() ||
                    isWarpDisruptionFieldGenerator()
        }

        return Pair(hasTournamentSpecificCharge, true)
    }


    override val packForBattleConfiguration: PackForBattleConfiguration
        get() = PackForBattleConfig


}


/**
 * Returns whether the given remote capacitor transmitter module is legal on the given ship type.
 */
context(EveData)
private fun isRemoteCapacitorTransmitterLegal(shipType: ShipType): Boolean {
    return (shipType.group == groups.logistics) || (shipType.name in Tech1LogiCruisers)
}


/**
 * Returns whether the given ECM module is legal on the given ship type.
 */
context(EveData)
private fun isEcmLegal(moduleType: ModuleType, shipType: ShipType): Boolean {
    return moduleType.isTech1Item && (shipType.name in EcmShips)
}


/**
 * Returns whether the given weapon disruption module is legal on the given ship type.
 */
context(EveData)
private fun isWeaponDisruptorLegal(moduleType: ModuleType, shipType: ShipType): Boolean {
    return moduleType.isTech1Item && (shipType.name in WeaponDisruptionShips)
}


/**
 * Returns whether the given sensor dampener module is legal on the given ship type.
 */
context(EveData)
private fun isRemoteSensorDampenerLegal(moduleType: ModuleType, shipType: ShipType): Boolean {
    return moduleType.isTech1Item && (shipType.name in SensorDampeningShips)
}


/**
 * Returns whether the charge is legal, whether in a module or in the cargohold.
 */
context(EveData)
private fun isChargeLegal(chargeType: ChargeType?): Boolean {
    return when {
        chargeType == null -> true
        chargeType.isWarpDisruptionScript() -> true
        chargeType.isInterdictionSphereLauncherProbe() -> chargeType.isStasisWebificationProbe
        chargeType.isAmmo() -> when {
            chargeType.isTech2Item -> true
            chargeType.isBomb() -> true
            chargeType.isDefenderMissile() -> true
            chargeType.isAutoTargetingMissile() -> chargeType.isEmpireNavyFactionItem
            chargeType.isMissile()
                    || chargeType.isProjectileAmmo()
                    || chargeType.isFrequencyCrystalAmmo()
                    || chargeType.isHybridChargeAmmo() -> chargeType.isElitePirateFactionItem
            else -> true
        }
        else -> true
    }
}


/**
 * Returns the crystal to preload into the given laser turret.
 */
private fun EveData.forEnergyTurret(shipType: ShipType, crystals: Collection<ChargeType>): ChargeType? {
    // Advanced pulse crystal with the longest range should be Scorch
    val scorch = crystals
        .filter { it.group == groups.advancedPulseLaserCrystal }
        .maxByOrNull { it.attributeValueOrNull(attributes.weaponRangeMultiplier) ?: 0.0 }
    if (scorch != null)
        return scorch

    // Highest damage faction should be Multifrequency
    return crystals
        .filter { isElitePirateCrystalProperForShipType(shipType, it) }
        .consistentMaxByOrNull { it.totalDamage ?: 0.0 }
}


/**
 * Returns the hybrid charge to preload into the given hybrid turret.
 */
private fun EveData.forHybridTurret(shipType: ShipType, charges: Collection<ChargeType>): ChargeType? {
    return charges
        .filter { isElitePirateHybridChargeProperForShipType(shipType, it) }
        .consistentMaxByOrNull {
            it.totalDamage ?: 0.0
        }
}


/**
 * Returns the ammo to preload into the given projectile turret.
 */
private fun EveData.forProjectileTurret(ammo: Collection<ChargeType>): ChargeType?{
    // Advanced autocannon ammo with the highest damage should be Hail
    val hail = ammo
        .filter { it.group == groups.advancedAutocannonAmmo }
        .maxByOrNull { it.totalDamage ?: 0.0 }
    if (hail != null)
        return hail

    // EMP, Phased Plasma and Fusion all have the same damage, but Phased Plasma is the most versatile one.
    return ammo
        .filter { it.isElitePirateFactionItem }
        .consistentMaxByOrNull(preferNameContains = "Phased Plasma") { it.totalDamage ?: 0.0 }
}


/**
 * Returns the missile to preload into the given missile launcher fitted to the given ship.
 */
private fun EveData.forMissileLauncher(
    fit: Fit,
    missileLauncher: ModuleType,
    missiles: Collection<ChargeType>
): ChargeType? {
    val dreadGuristas = missiles.filter { it.isElitePirateFactionItem }
    return preferredMissile(fit, missileLauncher, dreadGuristas)
}


/**
 * The configuration for the "Pack for Battle" dialog.
 */
private val PackForBattleConfig = object: PackForBattleConfiguration {


    context(EveData)
    override fun script(chargeType: ChargeType): Boolean {
        return isChargeLegal(chargeType)
    }


    context(EveData)
    override fun interdictionSphereLauncherProbe(chargeType: ChargeType): Boolean {
        return isChargeLegal(chargeType)
    }


    context(EveData)
    override fun ammo(shipType: ShipType, moduleType: ModuleType, chargeType: ChargeType): Boolean {
        return when {
            chargeType.isBomb() -> true
            chargeType.isDefenderMissile() -> true
            chargeType.isAutoTargetingMissile() -> chargeType.isEmpireNavyFactionItem
            chargeType.isTech2Item -> true
            moduleType.isEnergyWeapon() -> isElitePirateCrystalProperForShipType(shipType, chargeType)
            moduleType.isHybridWeapon() -> isElitePirateHybridChargeProperForShipType(shipType, chargeType)
            else -> chargeType.isElitePirateFactionItem
        }
    }


}

