/**
 * Specifies the value to be displayed in the "Range" column for various modules.
 */

package theorycrafter.ui.fiteditor

import androidx.compose.runtime.Composable
import eve.data.*
import theorycrafter.fitting.*
import theorycrafter.ui.widgets.TextAndTooltip
import kotlin.math.ceil


/**
 * Returns the displayed range for the given module.
 */
@Composable
fun displayedModuleRange(module: Module): TextAndTooltip? {
    if (module.optimalRange != null)
        return module.standardDisplayedRange()

    val charge = module.loadedCharge
    if ((charge != null) && (charge.missileRange != null))
        return charge.missileDisplayedRange()

    val warpDisruptionRange = charge?.warpDisruptionRange?.value
    if (warpDisruptionRange != null)
        return displayedRange(warpDisruptionRange)

    val stasisWebificationRange = charge?.stasisWebificationRange?.value
    if (stasisWebificationRange != null)
        return displayedRange(stasisWebificationRange)

    return null
}


/**
 * Returns the displayed range for the given drone group.
 */
@Composable
fun displayedDroneRange(droneGroup: DroneGroup) = droneGroup.standardDisplayedRange()


/**
 * Returns the standard displayed range for the given module or drone.
 */
private fun ModuleOrDrone<*>.standardDisplayedRange(): TextAndTooltip {
    // For mobile drones display speed in the cell
    if ((this is DroneGroup) && (mwdSpeed != null)) {
        val speed = mwdSpeed!!.value
        val speedText = speed.asSpeed()
        return TextAndTooltip(
            text = speedText,
            tooltipContent = { ValueWithDescriptionTable(standardTooltipItems()) }
        )
    }

    val optimal = optimalRange!!.value
    val falloff = falloffRange?.value
    return TextAndTooltip(
        text = asRange(optimal = optimal, falloff = falloff),
        tooltipContent = { ValueWithDescriptionTable(standardTooltipItems()) }
    )
}


/**
 * Returns the standard items to display in the tooltip for the given module or drone.
 */
private fun ModuleOrDrone<*>.standardTooltipItems(): List<ValueWithDescription> {
    val optimal = optimalRange!!.value
    val falloff = falloffRange?.value
    val trackingSpeed = trackingSpeed?.value
    val signatureRadius = (this as? Module)?.signatureRadius?.value  // Super Weapons have this
    val aoeRange = (this as? Module)?.aoeRange?.value  // Burst Projectors have this

    return buildList(2) {
        add(optimalTooltipItem(optimal))
        if ((falloff != null) && (falloff != 0.0))
            add(falloffTooltipItem(falloff))
        if (trackingSpeed != null)
            add(trackingSpeedTooltipItem(trackingSpeed))
        if (signatureRadius != null)
            add(signatureRadiusTooltipItem(signatureRadius))
        if (aoeRange != null)
            add(aoeRangeTooltipItem(aoeRange))
    }
}


/**
 * Returns the [ValueWithDescription] for the given optimal range.
 */
private fun optimalTooltipItem(optimal: Double) =
    ValueWithDescription(
        value = optimal.asTooltipDistance(),
        description = "optimal range"
    )


/**
 * Returns the [ValueWithDescription] for the given falloff.
 */
private fun falloffTooltipItem(falloff: Double) =
    ValueWithDescription(
        value = falloff.asTooltipDistance(),
        description = "falloff range"
    )


/**
 * Returns the [ValueWithDescription] for the given tracking speed.
 */
private fun trackingSpeedTooltipItem(trackingSpeed: Double) =
    ValueWithDescription(
        value = trackingSpeed.asTrackingSpeed(),
        description = "tracking speed"
    )


/**
 * Returns the [ValueWithDescription] for the given signature radius.
 */
private fun signatureRadiusTooltipItem(signatureRadius: Double) =
    ValueWithDescription(
        value = signatureRadius.asDistance(),
        description = "signature radius"
    )


/**
 * Returns the [ValueWithDescription] for the given AOE radius.
 */
private fun aoeRangeTooltipItem(aoeRadius: Double) =
    ValueWithDescription(
        value = aoeRadius.asDistance(),
        description = "effect radius"
    )


/**
 * Returns the displayed range for missiles.
 */
private fun Charge.missileDisplayedRange(
): TextAndTooltip {
    val range = missileRange!!
    val velocity = missileVelocity?.value
    val flightTime = missileFlightTime?.value
    val explosionRadius = missileExplosionRadius?.value
    val explosionVelocity = missileExplosionVelocity?.value

    val tooltipItems = buildList(5) {
        add(
            ValueWithDescription(
                value = range.shortRange.asTooltipDistance(),
                description = "with ${range.shortProbability.asTooltipChance()} chance"
            )
        )

        add(
            ValueWithDescription(
                value = range.longRange.asTooltipDistance(),
                description = "with ${range.longProbability.asTooltipChance()} chance"
            )
        )

        val averageRange = range.averageRange
        add(
            ValueWithDescription(
                value = averageRange.asTooltipDistance(),
                description = "avg. flight distance"
            )
        )

        if (velocity != null) {
            add(
                ValueWithDescription(
                    value = velocity.asSpeed(),
                    description = "flight velocity"
                )
            )
        }

        if (flightTime != null) {
            add(
                ValueWithDescription(
                    value = flightTime.millisAsTimeSec(),
                    description = "flight time"
                )
            )
        }

        if (explosionRadius != null) {
            add(
                ValueWithDescription(
                    value = explosionRadius.asDistance(),
                    description = "explosion radius"
                )
            )
        }

        if (explosionVelocity != null) {
            add(
                ValueWithDescription(
                    value = explosionVelocity.asSpeed(),
                    description = "explosion velocity"
                )
            )
        }
    }
    return TextAndTooltip(
        text = range.asString(),
        tooltipContent = {
            ValueWithDescriptionTable(
                items = tooltipItems
            )
        }
    )
}


/**
 * Returns the displayed range for the given distance.
 */
private fun displayedRange(range: Double): TextAndTooltip {
    return TextAndTooltip(
        text = range.asDistance(),
        tooltipContent = {
            ValueWithDescriptionTable(
                ValueWithDescription(
                    value = range.asTooltipDistance(),
                    description = "range"
                )
            )
        }
    )
}


/**
 * Converts a distance to text to be displayed in the tooltip.
 */
private fun Double.asTooltipDistance() = this.asDistanceWithPrecision(precision = 1)


/**
 * Converts a probability value to text to be displayed in the tooltip.
 */
private fun Double.asTooltipChance() = this.fractionAsPercentage(precision = 1)


/**
 * Returns the text to display for the given missile range.
 */
private fun MissileRange.asString(): String {
    val shortKm = (shortRange/1000).toInt()  // Round down on purpose
    val longKm = ceil(longRange/1000).toInt()  // Round up on purpose
    return buildString {
        append(shortKm)
        append("–")  // en-dash
        append(longKm)
        append(NNBSP)
        append("km")
    }
}
