import 'dart:async'; import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/customer_training_plan.dart'; import 'package:aitrainer_app/model/customer_training_plan_details.dart'; import 'package:aitrainer_app/model/exercise.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; import 'package:aitrainer_app/repository/training_plan_repository.dart'; import 'package:aitrainer_app/service/exercise_service.dart'; import 'package:aitrainer_app/util/app_language.dart'; import 'package:aitrainer_app/util/enums.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; part 'training_plan_event.dart'; part 'training_plan_state.dart'; class TrainingPlanBloc extends Bloc { final TrainingPlanRepository trainingPlanRepository; final MenuBloc menuBloc; TrainingPlanBloc({required this.trainingPlanRepository, required this.menuBloc}) : super(TrainingPlanInitial()); CustomerTrainingPlan? _myPlan; CustomerTrainingPlanDetails? _myDetail; bool started = false; final List dayNames = []; bool restarting = false; bool celebrating = false; int activeDayIndex = 0; CustomerTrainingPlan? getMyPlan() => this._myPlan; setMyPlan(CustomerTrainingPlan? myPlan) => this._myPlan = myPlan; CustomerTrainingPlanDetails? getMyDetail() => this._myDetail; setMyDetail(CustomerTrainingPlanDetails? value) => this._myDetail = value; @override Stream mapEventToState(TrainingPlanEvent event) async* { try { if (event is TrainingPlanActivate) { yield TrainingPlanLoading(); _myPlan = await trainingPlanRepository.activateTrainingPlan(event.trainingPlanId); _myPlan!.type = CustomerTrainingPlanType.template; menuBloc.menuTreeRepository.sortedTree.forEach((name, list) { final List menuList = list as List; menuList.forEach((element) { element.exerciseType!.trainingPlanState = ExerciseTypeTrainingPlanState.none; }); }); this.activateDays(); Cache().myTrainingPlan = _myPlan; await Cache().saveMyTrainingPlan(); yield TrainingPlanFinished(); } else if (event is TrainingPlanWeightChange) { yield TrainingPlanLoading(); event.detail.weight = event.weight; yield TrainingPlanReady(); } else if (event is TrainingPlanRepeatsChange) { yield TrainingPlanLoading(); event.detail.repeats = event.repeats; yield TrainingPlanReady(); } else if (event is TrainingPlanSetChange) { yield TrainingPlanLoading(); event.detail.set = event.set; yield TrainingPlanReady(); } else if (event is TrainingPlanSaveExercise) { yield TrainingPlanLoading(); event.detail.state = ExercisePlanDetailState.inProgress; final Exercise exercise = Exercise(); exercise.customerId = Cache().userLoggedIn!.customerId!; exercise.exerciseTypeId = event.detail.exerciseTypeId; exercise.quantity = event.detail.repeats!.toDouble(); exercise.unit = event.detail.exerciseType!.unit; exercise.unitQuantity = event.detail.weight; exercise.dateAdd = DateTime.now(); event.detail.exercises.add(exercise); if (event.detail.exercises.length >= event.detail.set!) { event.detail.state = ExercisePlanDetailState.finished; } else if (event.detail.exercises.length >= 0) { event.detail.state = ExercisePlanDetailState.inProgress; } exercise.trainingPlanDetailsId = _myPlan!.trainingPlanId; // save Exercise await ExerciseApi().addExercise(exercise); Cache().addExercise(exercise); Cache().myTrainingPlan = _myPlan; await Cache().saveMyTrainingPlan(); if (isDayDone()) { this.add(TrainingPlanFinishDay()); } else { yield TrainingPlanReady(); } } else if (event is TrainingPlanSkipExercise) { yield TrainingPlanLoading(); print("Skipping ${event.detail.exerciseTypeId}"); event.detail.state = ExercisePlanDetailState.skipped; Cache().myTrainingPlan = _myPlan; await Cache().saveMyTrainingPlan(); if (isDayDone()) { this.add(TrainingPlanFinishDay()); } else { yield TrainingPlanReady(); } } else if (event is TrainingPlanFinishDay) { yield TrainingPlanLoading(); celebrating = true; yield TrainingPlanDayFinished(); } else if (event is TrainingPlanGoToRestart) { yield TrainingPlanLoading(); restarting = true; yield TrainingPlanDayReadyToRestart(); } else if (event is TrainingPlanAddExerciseType) { if (_myDetail == null) { throw Exception("Create new Detail"); } yield TrainingPlanLoading(); _myDetail!.exerciseType!.trainingPlanState = ExerciseTypeTrainingPlanState.added; _myPlan!.details.add(this._myDetail!); yield TrainingPlanReady(); } else if (event is TrainingPlanDeleteExerciseType) { if (_myPlan == null || _myPlan!.details.isEmpty) { throw Exception("No MyPlan"); } yield TrainingPlanLoading(); CustomerTrainingPlanDetails? remove; for (var detail in _myPlan!.details) { if (event.exerciseType.exerciseTypeId == detail.exerciseTypeId) { remove = detail; break; } } if (remove != null) { _myPlan!.details.remove(remove); } event.exerciseType.trainingPlanState = ExerciseTypeTrainingPlanState.none; yield TrainingPlanReady(); } else if (event is TrainingPlanCustomAddLoad) { yield TrainingPlanLoading(); addNewPlan(); _myDetail = CustomerTrainingPlanDetails(); _myDetail!.exerciseType = event.exerciseType; _myDetail!.exerciseTypeId = event.exerciseType.exerciseTypeId; _myDetail!.repeats = 12; _myDetail!.weight = 30; _myDetail!.set = 3; _myDetail!.parallel = false; _myDetail!.day = ""; _myDetail!.restingTime = 2; _myDetail!.state = ExercisePlanDetailState.start; if (_myDetail!.exerciseType!.unitQuantityUnit != null) { _myDetail = trainingPlanRepository.getCalculatedWeightRepeats(event.exerciseType.exerciseTypeId, _myDetail!); } else { _myDetail!.weight = 0; } yield TrainingPlanReady(); } } on Exception catch (e) { yield TrainingPlanError(message: e.toString()); } } void addNewPlan() { if (_myPlan == null) { _myPlan = CustomerTrainingPlan(); } else { if (!_myPlan!.type.equalsTo(CustomerTrainingPlanType.custom)) { _myPlan!.details.clear(); } } _myPlan!.trainingPlanId = 0; _myPlan!.name = "Custom"; _myPlan!.customerId = Cache().userLoggedIn == null ? Cache().userLoggedIn!.customerId : 0; _myPlan!.type = CustomerTrainingPlanType.custom; restart(); print("New custom plan: $_myPlan"); } void restart() { if (_myPlan == null) { return; } for (var day in dayNames) { _myPlan!.days[day]!.clear(); } _myPlan!.details.forEach((element) { element.state = ExercisePlanDetailState.start; element.exercises.clear(); }); dayNames.clear(); Cache().myTrainingPlan = _myPlan; Cache().saveMyTrainingPlan(); restarting = false; print("Restarting finished"); } void activateDays() { if (_myPlan == null) { return; } if (isDone100Percent()) { this.add(TrainingPlanGoToRestart()); } dayNames.clear(); _myPlan!.days.clear(); String dayName = "."; _myPlan!.details.forEach((element) { if (element.day != null && element.day != dayName) { dayNames.add(element.day!); dayName = element.day!; } if (_myPlan!.days[dayName] == null) { if (dayName == ".") { dayName = ""; } _myPlan!.days[dayName] = []; } _myPlan!.days[dayName]!.add(element); }); if (dayNames.length == 0) { dayNames.add(""); _myPlan!.days[""] = []; _myPlan!.days[""]!.addAll(_myPlan!.details); } getActiveDayIndex(); } CustomerTrainingPlanDetails? getTrainingPlanDetail(int trainingPlanDetailsId) { CustomerTrainingPlanDetails? detail; if (_myPlan == null || _myPlan!.details.isEmpty) { return null; } for (final det in this._myPlan!.details) { if (det.customerTrainingPlanDetailsId == trainingPlanDetailsId) { detail = det; break; } } return detail; } int? getActualWorkoutTreeId(int exerciseTypeId) { final WorkoutMenuTree? workoutTree = this.menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(exerciseTypeId); if (workoutTree == null) { return null; } return workoutTree.id; } String getActualImageName(int exerciseTypeId) { if (exerciseTypeId <= 0) { return ""; } final WorkoutMenuTree? workoutTree = this.menuBloc.menuTreeRepository.getMenuItemByExerciseTypeId(exerciseTypeId); if (workoutTree == null) { return ""; } return workoutTree.imageName; } CustomerTrainingPlanDetails? getNext() { if (_myPlan == null || _myPlan!.details.isEmpty) { return null; } CustomerTrainingPlanDetails? next; int minStep = 99; for (final detail in this._myPlan!.details) { if (!detail.state.equalsTo(ExercisePlanDetailState.finished)) { if (detail.exercises.isEmpty && !detail.state.equalsTo(ExercisePlanDetailState.skipped)) { next = detail; minStep = 1; break; } else { final int step = detail.exercises.length; if (step < minStep && !detail.state.equalsTo(ExercisePlanDetailState.skipped)) { next = detail; minStep = step; if (detail.parallel != true) { break; } } } } } print("Next detail $next"); return next; } bool isStarted() { if (_myPlan == null || _myPlan!.details.isEmpty) { return false; } if (_myPlan!.details[0].state == ExercisePlanDetailState.start) { return false; } else { return true; } } bool isDayDone() { bool isDone = true; final String day = dayNames[activeDayIndex]; for (var detail in _myPlan!.days[day]!) { if (!detail.state.equalsTo(ExercisePlanDetailState.finished) && !detail.state.equalsTo(ExercisePlanDetailState.skipped)) { isDone = false; } } print("Is Day '$day' done: $isDone"); return isDone; } double getOffset() { double offset = 5; if (_myPlan == null) { return offset; } int indexInProgress = 0; int indexInStart = 0; final String day = dayNames[this.activeDayIndex]; for (var detail in _myPlan!.days[day]!) { if (detail.state == ExercisePlanDetailState.inProgress) { break; } if (detail.state == ExercisePlanDetailState.start) { break; } indexInStart++; indexInProgress++; } int index = indexInStart > indexInProgress ? indexInStart : indexInProgress; offset = index * 80; return offset; } bool isDone100Percent() { bool done = true; if (_myPlan == null || _myPlan!.details.isEmpty) { return false; } _myPlan!.details.forEach((element) { if (!element.state.equalsTo(ExercisePlanDetailState.finished) && !element.state.equalsTo(ExercisePlanDetailState.skipped)) { done = false; } }); return done; } int getActiveDayIndex() { if (restarting) { return 0; } if (_myPlan == null || _myPlan!.details.isEmpty) { throw Exception("No defined Training Plan"); } if (dayNames.isEmpty || dayNames.length == 1) { return 0; } activeDayIndex = 0; for (var day in dayNames) { if (_myPlan!.days[day] == null) { throw Exception("Wrong activated day: $day does not exist"); } bool isDone = true; _myPlan!.days[day]!.forEach((element) { if (!element.state.equalsTo(ExercisePlanDetailState.finished) && !element.state.equalsTo(ExercisePlanDetailState.skipped)) { isDone = false; } }); if (!isDone) { break; } activeDayIndex++; } print("Active Day Index: $activeDayIndex"); if (activeDayIndex >= dayNames.length) { activeDayIndex = 0; this.add(TrainingPlanGoToRestart()); } return activeDayIndex; } String getCustomAddSummary() { String summary = ""; if (_myDetail == null || _myDetail!.set == null || _myDetail!.repeats == null) { return summary; } summary = getMyDetail()!.set!.toStringAsFixed(0) + " x " + getMyDetail()!.repeats!.toStringAsFixed(0); return summary; } String getExerciseName(Locale locale) { String exerciseName = ""; if (_myDetail == null || _myDetail!.exerciseType == null) { return exerciseName; } exerciseName = AppLanguage().appLocal == Locale("en") ? getMyDetail()!.exerciseType!.name : getMyDetail()!.exerciseType!.nameTranslation; return exerciseName; } String getWeightByExerciseType(ExerciseType exerciseType) { double weight = 0; if (_myPlan == null || _myPlan!.details.isEmpty) { return weight.toStringAsFixed(0); } for (var detail in _myPlan!.details) { if (exerciseType.exerciseTypeId == detail.exerciseTypeId) { weight = detail.weight!; break; } } int decimal = weight % weight.round() == 0 ? 0 : 1; return weight.toStringAsFixed(decimal); } String getSetByExerciseType(ExerciseType exerciseType) { int value = 0; if (_myPlan == null || _myPlan!.details.isEmpty) { return value.toStringAsFixed(0); } for (var detail in _myPlan!.details) { if (exerciseType.exerciseTypeId == detail.exerciseTypeId) { value = detail.set!; break; } } return value.toStringAsFixed(0); } String getRepeatsByExerciseType(ExerciseType exerciseType) { int value = 0; if (_myPlan == null || _myPlan!.details.isEmpty) { return value.toStringAsFixed(0); } for (var detail in _myPlan!.details) { if (exerciseType.exerciseTypeId == detail.exerciseTypeId) { value = detail.repeats!; break; } } return value.toStringAsFixed(0); } }