package theorycrafter.fitting

import eve.data.EveData
import eve.data.ModuleSlotType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlin.test.Test
import kotlin.time.measureTime


/**
 * Tests the performance of the fitting engine.
 */
class PerformanceTest {


    /**
     * Measures and prints the amount of time it takes to load the standard [EveData].
     */
    @Test
    fun loadStandardEveData() {
        val time = measureTime {
            EveData.loadStandard()
        }
        println("Loading standard eve data took $time")
    }


    /**
     * Measures and prints the amount of time it takes to fit a ship.
     */
    @Test
    fun basicFitTest() {
        val modulesBySlot = mapOf(
            ModuleSlotType.HIGH to listOf(
                Pair("Heavy Assault Missile Launcher II", "Scourge Rage Heavy Assault Missile"),
                Pair("Heavy Assault Missile Launcher II", "Scourge Rage Heavy Assault Missile"),
                Pair("Heavy Assault Missile Launcher II", "Scourge Rage Heavy Assault Missile"),
                Pair("Heavy Assault Missile Launcher II", "Scourge Rage Heavy Assault Missile"),
                Pair("Heavy Assault Missile Launcher II", "Scourge Rage Heavy Assault Missile"),
                Pair("Heavy Assault Missile Launcher II", "Scourge Rage Heavy Assault Missile"),
            ),
            ModuleSlotType.MEDIUM to listOf(
                Pair("50MN Cold-Gas Enduring Microwarpdrive", null),
                Pair("Large Ancillary Shield Booster", "Navy Cap Booster 150"),
                Pair("EM Shield Hardener II", null),
                Pair("Large Shield Extender II", null),
                Pair("Multispectrum Shield Hardener II", null),

            ),
            ModuleSlotType.LOW to listOf(
                Pair("Assault Damage Control II", null),
                Pair("Ballistic Control System II", null),
                Pair("Ballistic Control System II", null),
                Pair("Ballistic Control System II", null),
            ),
            ModuleSlotType.RIG to listOf(
                Pair("Medium EM Shield Reinforcer II", null),
                Pair("Medium Explosive Shield Reinforcer II", null)
            )
        )

        val drones = listOf(Pair("Acolyte II", 3))

        val implants = listOf(
            "High-grade Crystal Alpha",
            "High-grade Crystal Beta",
            "High-grade Crystal Gamma",
            "Low-grade Crystal Delta",
            "Low-grade Crystal Epsilon",
            "Inherent Implants 'Squire' Power Grid Management EG-603",
            "Zainou 'Deadeye' Missile Bombardment MB-703",
            "Inherent Implants 'Squire' Capacitor Management EM-803",
            "Eifyr and Co. 'Rogue' High Speed Maneuvering HS-903",
            "Zainou 'Deadeye' Rapid Launch RL-1003",
        )

        val cargoItems = listOf(
            Pair("Nanite Repair Paste", 400),
            Pair("Scourge Rage Heavy Assault Missile", 1000),
            Pair("Navy Cap Booster 150", 27),
            Pair("Caldari Navy Scourge Heavy Assault Missile", 1000),
        )

        val fittingEngine = FittingEngine(
            eveData = eveData,
            fittingRestrictionsEnforcementMode = FittingEngine.FittingRestrictionsEnforcementMode.SET_ITEM_LEGALITY,
            coroutineContext = Dispatchers.Unconfined
        )

        fun doFit() {
            with(eveData) {
                runBlocking {
                    val fit = fittingEngine.modify(silent = true) {
                        val fit = newFit(shipType("Cerberus"))
                        for (modulesAndCharges in modulesBySlot.values) {
                            for ((index, moduleAndChargeNames) in modulesAndCharges.withIndex()) {
                                val moduleType = moduleType(moduleAndChargeNames.first)
                                val module = fit.fitModule(moduleType, index)
                                val charge = moduleAndChargeNames.second?.let { chargeType(it) }
                                if (charge != null)
                                    module.setCharge(charge)
                                if (moduleType.isActivable)
                                    module.setState(Module.State.ACTIVE)
                            }
                        }
                        for ((droneName, amount) in drones) {
                            val droneType = droneType(droneName)
                            fit.addDroneGroup(droneType, amount)
                        }
                        for (implantName in implants) {
                            val implantType = implantTypes.getOrNull(implantName)!!
                            fit.fitImplant(implantType)
                        }
                        for ((cargoItemName, amount) in cargoItems) {
                            val itemType = cargoItemType(cargoItemName)
                            fit.addCargoItem(itemType, amount)
                        }
                        fit
                    }

                    fittingEngine.modify(silent = true) {
                        fit.remove()
                    }
                }
            }
        }

        val count = 1000
        val time = measureTime {
            repeat(count) {
                doFit()
            }
        }
        println("Fitting a Cerberus $count times took $time, " +
                "at a rate of ~${1000 * count / time.inWholeMilliseconds } fits/sec")
    }


}
