import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:slrpg_app/src/shared/domain/entities/exercise.dart'; import 'package:slrpg_app/src/shared/domain/entities/workout_set.dart'; part 'battle_controller.g.dart'; class BattleState { final List exercises; final int currentExerciseIndex; final int currentSetIndex; final int repsCompleted; final bool isLoading; final bool isResting; final int restSeconds; final String? error; final bool isExerciseTimerActive; final int exerciseTimerSeconds; const BattleState({ this.exercises = const [], this.currentExerciseIndex = 0, this.currentSetIndex = 0, this.repsCompleted = 0, this.isLoading = true, this.isResting = false, this.restSeconds = 0, this.isExerciseTimerActive = false, this.exerciseTimerSeconds = 0, this.error, }); BattleState copyWith({ List? exercises, int? currentExerciseIndex, int? currentSetIndex, int? repsCompleted, bool? isLoading, bool? isResting, int? restSeconds, bool? isExerciseTimerActive, int? exerciseTimerSeconds, String? error, }) { return BattleState( exercises: exercises ?? this.exercises, currentExerciseIndex: currentExerciseIndex ?? this.currentExerciseIndex, currentSetIndex: currentSetIndex ?? this.currentSetIndex, repsCompleted: repsCompleted ?? this.repsCompleted, isLoading: isLoading ?? this.isLoading, isResting: isResting ?? this.isResting, restSeconds: restSeconds ?? this.restSeconds, isExerciseTimerActive: isExerciseTimerActive ?? this.isExerciseTimerActive, exerciseTimerSeconds: exerciseTimerSeconds ?? this.exerciseTimerSeconds, error: error ?? this.error, ); } } @riverpod class BattleController extends _$BattleController { @override BattleState build() { return const BattleState(); } void setExercises(List exercises) { state = state.copyWith( exercises: exercises, isLoading: false, repsCompleted: exercises.isNotEmpty && exercises.first.sets.isNotEmpty ? exercises.first.sets.first.repsTarget : 0, ); } void setLoading(bool loading) { state = state.copyWith(isLoading: loading); } void setError(String error) { state = state.copyWith(error: error, isLoading: false); } void updateRepsCompleted(int reps) { state = state.copyWith(repsCompleted: reps); } void startRest(int seconds) { state = state.copyWith(isResting: true, restSeconds: seconds); } void tickRest() { if (state.restSeconds > 0) { state = state.copyWith(restSeconds: state.restSeconds - 1); } else { state = state.copyWith(isResting: false); } } void skipRest() { state = state.copyWith(isResting: false, restSeconds: 0); } void startExerciseTimer(int seconds) { state = state.copyWith( isExerciseTimerActive: true, exerciseTimerSeconds: seconds); } void tickExerciseTimer() { if (state.exerciseTimerSeconds > 0) { state = state.copyWith(exerciseTimerSeconds: state.exerciseTimerSeconds - 1); } else { state = state.copyWith(isExerciseTimerActive: false); } } void stopExerciseTimer() { state = state.copyWith(isExerciseTimerActive: false, exerciseTimerSeconds: 0); } void completeSet({required int repsActual}) { if (state.isExerciseTimerActive) { state = state.copyWith(isExerciseTimerActive: false); } final currentExercise = state.exercises[state.currentExerciseIndex]; final currentSet = currentExercise.sets[state.currentSetIndex]; final updatedSet = currentSet.copyWith( repsActual: repsActual, completed: true, ); final updatedSets = List.from(currentExercise.sets); updatedSets[state.currentSetIndex] = updatedSet; final updatedExercise = currentExercise.copyWith(sets: updatedSets); final updatedExercises = List.from(state.exercises); updatedExercises[state.currentExerciseIndex] = updatedExercise; state = state.copyWith(exercises: updatedExercises); } void moveToNextSet() { final currentExercise = state.exercises[state.currentExerciseIndex]; if (state.currentSetIndex < currentExercise.sets.length - 1) { final nextSet = currentExercise.sets[state.currentSetIndex + 1]; state = state.copyWith( currentSetIndex: state.currentSetIndex + 1, repsCompleted: nextSet.repsTarget, ); } } void moveToNextExercise() { if (state.currentExerciseIndex < state.exercises.length - 1) { final nextExercise = state.exercises[state.currentExerciseIndex + 1]; final nextReps = nextExercise.sets.isNotEmpty ? nextExercise.sets.first.repsTarget : 0; state = state.copyWith( currentExerciseIndex: state.currentExerciseIndex + 1, currentSetIndex: 0, repsCompleted: nextReps, ); } } void adjustEmomSets(int newTotalSets) { final currentEx = state.exercises[state.currentExerciseIndex]; if (newTotalSets == currentEx.sets.length) return; List currentSets = List.from(currentEx.sets); if (newTotalSets > currentSets.length) { final templateSet = currentSets.last; for (int i = currentSets.length; i < newTotalSets; i++) { currentSets.add(templateSet.copyWith( setNumber: i + 1, completed: true, repsActual: templateSet.repsTarget, )); } } else { currentSets = currentSets.sublist(0, newTotalSets); } final updatedEx = currentEx.copyWith(sets: currentSets); final updatedExercises = List.from(state.exercises); updatedExercises[state.currentExerciseIndex] = updatedEx; state = state.copyWith( exercises: updatedExercises, currentSetIndex: newTotalSets - 1, repsCompleted: updatedEx.sets.last.repsTarget, ); } Exercise get currentExercise => state.exercises[state.currentExerciseIndex]; WorkoutSet get currentSet => currentExercise.sets[state.currentSetIndex]; bool get isLastSet => state.currentSetIndex >= currentExercise.sets.length - 1; bool get isLastExercise => state.currentExerciseIndex >= state.exercises.length - 1; int get totalHP => state.exercises.fold( 0, (sum, ex) => sum + ex.sets.fold(0, (s, set) => s + set.repsTarget), ); int get completedHP { int hp = 0; for (int i = 0; i < state.currentExerciseIndex; i++) { hp += state.exercises[i].sets.fold( 0, (sum, set) => sum + set.repsActual, ); } final currentExercise = state.exercises[state.currentExerciseIndex]; for (int i = 0; i < state.currentSetIndex; i++) { hp += currentExercise.sets[i].repsActual; } return hp; } }