import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:kettlebell_tracker/models/training_session.dart'; import 'package:kettlebell_tracker/services/api_service.dart'; import 'package:kettlebell_tracker/services/database_helper.dart'; import 'dart:async'; // Training State class TrainingState { final bool isTrainingRunning; final int remainingSeconds; final int initialDurationSeconds; final int setsDone; final int goalSets; final int repsPerSet; final List setTimes; final double progress; final int secondsSinceLastSet; final DateTime? lastSetTimestamp; final String currentProgram; final int currentBlockDay; final int currentReps; final int totalTrainingDays; TrainingState({ this.isTrainingRunning = false, this.remainingSeconds = 0, this.initialDurationSeconds = 0, this.setsDone = 0, this.goalSets = 5, this.repsPerSet = 5, this.setTimes = const [], this.progress = 0.0, this.secondsSinceLastSet = 0, this.lastSetTimestamp, this.currentProgram = 'giant_1.0', this.currentBlockDay = 1, this.currentReps = 5, this.totalTrainingDays = 0, }); TrainingState copyWith({ bool? isTrainingRunning, int? remainingSeconds, int? initialDurationSeconds, int? setsDone, int? goalSets, int? repsPerSet, List? setTimes, double? progress, int? secondsSinceLastSet, DateTime? lastSetTimestamp, bool clearLastSetTimestamp = false, String? currentProgram, int? currentBlockDay, int? currentReps, int? totalTrainingDays, }) { return TrainingState( isTrainingRunning: isTrainingRunning ?? this.isTrainingRunning, remainingSeconds: remainingSeconds ?? this.remainingSeconds, initialDurationSeconds: initialDurationSeconds ?? this.initialDurationSeconds, setsDone: setsDone ?? this.setsDone, goalSets: goalSets ?? this.goalSets, repsPerSet: repsPerSet ?? this.repsPerSet, setTimes: setTimes ?? this.setTimes, progress: progress ?? this.progress, secondsSinceLastSet: secondsSinceLastSet ?? this.secondsSinceLastSet, lastSetTimestamp: clearLastSetTimestamp ? null : lastSetTimestamp ?? this.lastSetTimestamp, currentProgram: currentProgram ?? this.currentProgram, currentBlockDay: currentBlockDay ?? this.currentBlockDay, currentReps: currentReps ?? this.currentReps, totalTrainingDays: totalTrainingDays ?? this.totalTrainingDays, ); } } class TrainingNotifier extends StateNotifier { final Ref ref; TrainingNotifier(this.ref) : super(TrainingState()); void _updateProgram() { int newTotalDays = state.totalTrainingDays + 1; String newProgram = state.currentProgram; int newDay = (state.currentBlockDay % 3) + 1; int newReps = state.currentReps; if (newTotalDays > 0 && newTotalDays % 12 == 0) { switch (state.currentProgram) { case 'giant_1.0': newProgram = 'giant_1.1'; break; case 'giant_1.1': newProgram = 'giant_1.2'; break; case 'giant_1.2': newProgram = 'ksk_1.0'; break; case 'ksk_1.0': newProgram = 'ksk_1.1'; break; case 'ksk_1.1': newProgram = 'ksk_1.2'; break; case 'ksk_1.2': newProgram = 'giant_1.0'; break; default: newProgram = 'giant_1.0'; } newDay = 1; } if (newProgram == 'giant_1.0') { newReps = [5, 6, 4][newDay - 1]; } else if (newProgram == 'giant_1.1') { newReps = [6, 8, 7][newDay - 1]; } else if (newProgram == 'giant_1.2') { newReps = [7, 9, 8][newDay - 1]; } else if (newProgram == 'ksk_1.0') { newReps = [5, 6, 4][newDay - 1]; } else if (newProgram == 'ksk_1.1') { newReps = [6, 8, 7][newDay - 1]; } else if (newProgram == 'ksk_1.2') { newReps = [7, 9, 8][newDay - 1]; } else { newReps = 5; } state = state.copyWith( currentProgram: newProgram, currentBlockDay: newDay, currentReps: newReps, totalTrainingDays: newTotalDays, ); } void startTraining(int minutes, int goal) { _updateProgram(); final duration = minutes * 60; state = state.copyWith( isTrainingRunning: true, initialDurationSeconds: duration, remainingSeconds: duration, goalSets: goal, repsPerSet: state.currentReps, ); } void tick() { if (state.remainingSeconds > 0) { state = state.copyWith(remainingSeconds: state.remainingSeconds - 1); } } void tickLastSetTimer() { if (state.isTrainingRunning && state.lastSetTimestamp != null) { state = state.copyWith( secondsSinceLastSet: DateTime.now().difference(state.lastSetTimestamp!).inSeconds, ); } } void completeSet() { final newSetsDone = state.setsDone + 1; final newSetTimes = List.from(state.setTimes) ..add(DateTime.now()); final newProgress = state.goalSets > 0 ? newSetsDone / state.goalSets : 0.0; state = state.copyWith( setsDone: newSetsDone, setTimes: newSetTimes, progress: newProgress > 1.0 ? 1.0 : newProgress, lastSetTimestamp: DateTime.now(), secondsSinceLastSet: 0, ); } Future finishTraining(TrainingSession session) async { final updatedSession = session.copyWith( program: state.currentProgram, blockDay: state.currentBlockDay, ); await DatabaseHelper().saveTraining(updatedSession); try { await sendTrainingToBackend( reps: session.repsPerSet, rest: session.duration / session.sets, sets: session.sets); } catch (e) { print("Error sending information to backend"); } ref.refresh(historyProvider); resetTraining(); } void resetTraining() { state = TrainingState(); } } final historyProvider = FutureProvider>((ref) async { return DatabaseHelper().getHistory(); }); final trainingProvider = StateNotifierProvider( (ref) { return TrainingNotifier(ref); }, );