package net.aiterp.git.ykonsole2.infrastructure.drivers import net.aiterp.git.ykonsole2.application.logging.log import net.aiterp.git.ykonsole2.domain.models.* import net.aiterp.git.ykonsole2.domain.runtime.* import net.aiterp.git.ykonsole2.infrastructure.drivers.abstracts.ReactiveDriver class ProgramEnforcer( private val programRepo: ProgramRepository, private val workoutRepo: WorkoutRepository, ) : ReactiveDriver() { private var lastTransition: WorkoutState = WorkoutState() private var lastValues: ValuesReceived? = null private var lastStep: ProgramStep? = null private var stepIndex: Int = 0 private var program: Program? = null private suspend fun runTransition(input: CommandBus, newEvent: ValuesReceived? = null) { val prog = program ?: return val event = newEvent ?: lastValues ?: return if (prog.steps.size > stepIndex + 1) { stepIndex += 1 log.info("$stepIndex: Jumped!") // Go to next step and send the change lastTransition = lastTransition.copy( time = event.values.find() ?: lastTransition.time, calories = event.values.find(), distance = event.values.find(), level = event.values.find(), ) lastStep = prog.steps[stepIndex] lastStep?.values?.forEach { input.emit(SetValueCommand(it)) } } else { log.info("$stepIndex: Stopped!") // The program is done, let's stop it lastStep = null program = null input.emit(StopCommand) } } override suspend fun onEvent(event: Event, input: FlowBus) { if (event is Connected && event.initial) { // Start program on connecting val programId = workoutRepo.findActive()?.apply { lastTransition = WorkoutState(workoutId = id) }?.programId ?: return program = programRepo.findById(programId)?.takeIf { it.steps.isNotEmpty() } lastStep = program?.steps?.firstOrNull() stepIndex = 0 } if (event is Started) { // Set up the first step lastStep?.apply { values.map { input.emit(SetValueCommand(it)) } } } if (event is Skipped) { runTransition(input) } if (event is ValuesReceived && program != null && lastStep != null) { // If there's a program, look for next transition val step = lastStep!! val transition = when (step.duration) { is Time -> (event.values.findInt