package com.nisemoe.nise.osu

enum class Mod(val value: Int) {
    NM(0), // NoMod
    NF(1 shl 0), // NoFail
    EZ(1 shl 1), // Easy
    TD(1 shl 2), // TouchDevice
    HD(1 shl 3), // Hidden
    HR(1 shl 4), // HardRock
    SD(1 shl 5), // SuddenDeath
    DT(1 shl 6), // DoubleTime
    RX(1 shl 7), // Relax
    HT(1 shl 8), // HalfTime
    _NC(1 shl 9), // _Nightcore, technical version
    NC(_NC.value or DT.value), // Nightcore, as defined in-game
    FL(1 shl 10), // Flashlight
    AT(1 shl 11), // Autoplay
    SO(1 shl 12), // SpunOut
    AP(1 shl 13), // Autopilot
    _PF(1 shl 14), // _Perfect, technical version
    PF(_PF.value or SD.value), // Perfect, as defined in-game
    K4(1 shl 15), // Key4
    K5(1 shl 16), // Key5
    K6(1 shl 17), // Key6
    K7(1 shl 18), // Key7
    K8(1 shl 19), // Key8
    FI(1 shl 20), // FadeIn
    RD(1 shl 21), // Random
    CN(1 shl 22), // Cinema
    TP(1 shl 23), // Target
    K9(1 shl 24), // Key9
    CO(1 shl 25), // KeyCoop
    K1(1 shl 26), // Key1
    K3(1 shl 27), // Key3
    K2(1 shl 28), // Key2
    V2(1 shl 29), // ScoreV2
    MR(1 shl 30); // Mirror

    companion object {

        private val ORDER = listOf(
            NM, EZ, HD, HT, DT, NC, HR, FL, NF, SD, PF, RX, AP, SO, AT,
            V2, TD, // we stop caring about order after this point
            FI, RD, CN, TP, K1, K2, K3, K4, K5, K6, K7, K8, K9, CO, MR
        )

        private fun orderMods(mods: Set<Mod>): List<Mod> {
            val modsMutable = mods.toMutableSet()

            // Replace _NC with NC if present
            if (_NC in modsMutable) {
                modsMutable.remove(_NC)
                modsMutable.add(NC)
            }

            // Replace _PF with PF if present
            if (_PF in modsMutable) {
                modsMutable.remove(_PF)
                modsMutable.add(PF)
            }

            val orderedMods = ORDER.filter { it in modsMutable }.toMutableList()

            // Additional logic for removing DT when _NC is replaced by NC
            // and removing SD when _PF is replaced by PF, if necessary
            if (NC in orderedMods && DT in orderedMods) {
                orderedMods -= DT
            }
            if (PF in orderedMods && SD in orderedMods) {
                orderedMods -= SD
            }
            if(NM in orderedMods) {
                orderedMods -= NM
            }

            return orderedMods
        }

        fun print(combinationValue: Int): String {
            return parseModCombination(combinationValue).joinToString("")
        }

        fun parseModCombination(combinationValue: Int): List<String> {
            // First, determine which mods are present in the combination
            val modsPresent = entries
                .filter { mod -> (combinationValue and mod.value) == mod.value }
                .toSet()

            // Then, order these mods according to the predefined order
            val orderedMods = orderMods(modsPresent)

            // Finally, return the names of the ordered mods
            return orderedMods.map { it.name }
        }

        fun combineModStrings(modStrings: List<String>): Int {
            return modStrings
                .mapNotNull { modString -> entries.firstOrNull { it.name == modString }?.value }
                .fold(0) { acc, modValue -> acc or modValue }
        }

        fun containsMod(number: Int, mod: Mod): Boolean {
            return (number and mod.value) == mod.value
        }

    }

}