package eve.data

import androidx.compose.runtime.Immutable
import eve.data.utils.ValueByEnum
import eve.data.utils.valueByEnum


/**
 * Sizes of eve items, loosely corresponding to ship sizes e.g. rig size ([Attributes.rigSize]) or charge size
 * ([Attributes.chargeSize]).
 */
enum class ItemSize(


    /**
     * The displayed name of this size.
     */
    private val displayName: String,


    /**
     * The code (in the SDE) of this size.
     */
    val code: Int

) {


    /**
     * Typically corresponds to frigate- or destroyer-sized items.
     */
    SMALL("Small", 1),


    /**
     * Typically corresponds to cruiser- or battlecruiser-sized items.
     */
    MEDIUM("Medium", 2),


    /**
     * Typically corresponds to battleship-sized items.
     */
    LARGE("Large", 3),


    /**
     * Typically corresponds to capital-sized items.
     */
    CAPITAL("Capital", 4);


    override fun toString(): String {
        return displayName
    }


    companion object{


        /**
         * Returns the [ItemSize] with the given code.
         */
        fun fromCode(code: Int) = entries.find { it.code == code }


    }


}


/**
 * Ship sensor types.
 */
enum class SensorType(


    /**
     * The id of the sensor type, as used in attribute names.
     */
    val id: String,


    /**
     * The name of the sensor type, as it should be presented to the user.
     */
    val displayName: String,


    /**
     * A shortened name that can be presented to the user when there is little space.
     */
    val shortDisplayName: String


) {


    RADAR(id = "radar", displayName = "Radar", shortDisplayName = "Radar"),
    MAGNETOMETRIC(id = "magnetometric", displayName = "Magnetometric", shortDisplayName = "Magnet."),
    GRAVIMETRIC(id = "gravimetric", displayName = "Gravimetric", shortDisplayName = "Grav."),
    LADAR(id = "ladar", displayName = "Ladar", shortDisplayName = "Ladar");


    override fun toString() = displayName


}


/**
 * A requirement to have trained some skill to a certain level.
 */
@Immutable
data class SkillRequirement(val skillId: Int, val level: Int)


/**
 * Ship module slot types.
 */
enum class ModuleSlotType(
    val maxSlotCount: Int,
    val slotName: String,
    val lowercaseName: String,
) {

    HIGH(8, "High", "high"),
    MEDIUM(8, "Medium", "medium"),
    LOW(8, "Low", "low"),
    RIG(3, "Rig", "rig");

}


/**
 * An actual module slot.
 */
data class ModuleSlot(
    val type: ModuleSlotType,
    val index: Int
)


/**
 * Returns a [ModuleSlot] with the given slot type, at the given index.
 */
fun ModuleSlotType.slotAtIndex(index: Int) = ModuleSlot(this, index)


/**
 * Damage types.
 */
enum class DamageType(val id: String){


    EM("em"),
    THERMAL("thermal"),
    KINETIC("kinetic"),
    EXPLOSIVE("explosive");


    /**
     * The displayable name.
     */
    val displayName: String
        get() = if (this == EM) "EM" else id.replaceFirstChar(Char::uppercaseChar)


    /**
     * The displayable name, in lowercase.
     */
    val displayNameLowercase: String
        get() = id


    override fun toString() = displayName


}


/**
 * Returns damage pattern of the given item type; `null` if it has no damage attributes at all.
 */
internal fun damagePatternOf(
    itemType: EveItemType,
    attributes: Attributes,
): DamagePattern? {
    val damageByType = valueByEnum<DamageType, Double?> {
        itemType.attributeValueOrNull(attributes.damage[it])
    }
    if (damageByType.values.all { it == null }) return null
    return DamagePattern {
        damageByType[it] ?: 0.0
    }
}


/**
 * A resonance pattern: the resonance for each damage type.
 */
typealias ResonancePattern = ValueByEnum<DamageType, Double>


/**
 * Combines the mining properties into amount mined per hour and residue generated per hour.
 */
@Immutable
data class MiningInfo(


    /**
     * The amount mined per hour.
     */
    val minedPerHour: Double,


    /**
     * The residue per hour.
     */
    val residuePerHour: Double?


) {

    /**
     * Returns the mining info for [count] items.
     */
    operator fun times(count: Int) = MiningInfo(
        minedPerHour = minedPerHour * count,
        residuePerHour = residuePerHour?.let { it * count }
    )

}
