import 'dart:async';
import 'dart:collection';

import 'package:aitrainer_app/model/cache.dart';
import 'package:aitrainer_app/model/exercise_ability.dart';
import 'package:aitrainer_app/model/workout_menu_tree.dart';
import 'package:aitrainer_app/repository/exercise_device_repository.dart';
import 'package:aitrainer_app/repository/exercise_repository.dart';
import 'package:aitrainer_app/repository/workout_tree_repository.dart';
import 'package:aitrainer_app/service/logging.dart';
import 'package:aitrainer_app/util/trans.dart';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:meta/meta.dart';
import 'package:aitrainer_app/library/image_cache.dart' as wt;

part 'menu_event.dart';
part 'menu_state.dart';

class MenuBloc extends Bloc<MenuEvent, MenuState> with Trans, Logging {
  final WorkoutTreeRepository menuTreeRepository;
  final ExerciseRepository exerciseRepository = ExerciseRepository();
  ExerciseDeviceRepository exerciseDeviceRepository = ExerciseDeviceRepository();
  int parent = 0;
  WorkoutMenuTree? workoutItem;
  final List<int> listFilterDevice = [];

  String infoTitle = "";
  String infoText = "";
  String infoText2 = "";
  String infoText3 = "";
  String infoLink = "";
  int missingParent = 0;
  String? missingTreeName = "";

  late BuildContext context;

  ExerciseAbility? ability;

  MenuBloc({required this.menuTreeRepository}) : super(MenuInitial()) {
    parent = 0;
  }

  void setMenuInfo() {
    double percent = Cache().getPercentExercises();
    if (percent == -1) {
      exerciseRepository.getBaseExerciseFinishedPercent();
      percent = Cache().getPercentExercises();
    }

    percent = percent * 100;
    //log("Percent " + percent.toString());
    if (percent == -1 || percent == 0) {
      infoTitle = t("Greetings!");
      infoText = t("The purpose is to measure you physical condition") +
          " " +
          t("The suggested order of the exercises: chest - biceps - triceps - back - shoulders - core - tigh - calf.");
      infoText2 = t("I suggest begin your tests with a");
      infoText3 = t("Go to the menu Strength - One Rep Max - Chest, and select your favourite exercise.");
      infoLink = t("Bring me there");
    } else if (percent > 0 && percent < 20) {
      infoTitle = t("Nice! This is a good start");
    } else if (percent > 20 && percent < 40) {
      infoTitle = t("Go on!") + " " + t("You are on track");
    } else if (percent > 60 && percent < 80) {
      infoTitle = t("Persistence!") + " " + t("Not so much left");
    } else if (percent > 80 && percent < 100) {
      infoTitle = t("Almost!") + " " + t("You have only 1-2 exercise left to finish!");
    } else {
      infoTitle = t("Congratulation!");
      infoText2 = t("You have achieved to first 100% test-round!");
      infoText3 = t("Now you unlocked: Development By Muscles and the Suggested Trainings Plan");
    }

    menuTreeRepository.sortByMuscleType();
    missingTreeName = exerciseRepository.nextMissingBaseExercise(menuTreeRepository.sortedTree);
    //log("Missing " + missingTreeName);
    if (missingTreeName != null) {
      if (percent > 0) {
        infoText = t("Please continue your tests with a") + " '" + missingTreeName! + "' " + t("exercise!");
        infoLink = t("Bring me there");
      }
    }
  }

  void setContext(BuildContext context) {
    this.context = context;
  }

  @override
  Stream<MenuState> mapEventToState(
    MenuEvent event,
  ) async* {
    try {
      if (event is MenuCreate) {
        yield MenuLoading();
        //await menuTreeRepository.createTree();
        //menuTreeRepository.getBranch(this.parent);
        //setMenuInfo();
        if (Cache().getDevices() != null) {
          exerciseDeviceRepository.setDevices(Cache().getDevices()!);
        }
        yield MenuReady();
      } else if (event is MenuRecreateTree) {
        yield MenuLoading();
        // ie. at language changes
        menuTreeRepository.createTree();
        yield MenuReady();
      } else if (event is MenuTreeDown) {
        yield MenuLoading();
        parent = event.parent;
        workoutItem = event.item;

        if (workoutItem != null) {
          setAbility(workoutItem!.nameEnglish);
        }
        final LinkedHashMap<String, WorkoutMenuTree> branch = menuTreeRepository.getBranch(event.parent);

        await getImages(branch);
        //await Future.delayed(Duration(seconds: 2));
        yield MenuReady();
      } else if (event is MenuTreeUp) {
        yield MenuLoading();
        // get parent menus or exercises
        parent = event.parent;
        workoutItem = menuTreeRepository.getParentItem(parent);

        LinkedHashMap<String, WorkoutMenuTree> branch;
        if (workoutItem != null) {
          setAbility(workoutItem!.nameEnglish);
          branch = menuTreeRepository.getBranch(workoutItem!.parent);
          await getImages(branch);
        }

        yield MenuReady();
      } else if (event is MenuTreeJump) {
        yield MenuLoading();
        parent = event.parent;
        workoutItem = menuTreeRepository.getParentItem(parent);

        if (workoutItem != null) {
          setAbility(workoutItem!.nameEnglish);
        }
        final LinkedHashMap<String, WorkoutMenuTree> branch = menuTreeRepository.getBranch(workoutItem!.parent);
        await getImages(branch);

        yield MenuReady();
      } else if (event is MenuClickExercise) {
        yield MenuLoading();
        // get exercise page
        yield MenuReady();
      } else if (event is MenuFilterExerciseType) {
        yield MenuLoading();
        final int deviceId = event.deviceId;
        if (selectedDevice(deviceId)) {
          listFilterDevice.add(deviceId);
        } else {
          listFilterDevice.remove(deviceId);
        }
        yield MenuReady();
      }
    } on Exception catch (ex) {
      yield MenuError(message: ex.toString());
    }
  }

  void setAbility(String name) {
    switch (name) {
      case "Muscle Build / Shape Toning":
        ability = ExerciseAbility.oneRepMax;
        break;
      case "Endurance":
        ability = ExerciseAbility.endurance;
        break;
      case "Cardio":
        ability = ExerciseAbility.running;
        break;
      case "Test Center":
        ability = ExerciseAbility.mini_test_set;
        break;
      case "My Body":
        ability = ExerciseAbility.none;
        break;
    }
    log("Ability: " + ability.toString() + " name: " + name);
  }

  Future<void> getImages(LinkedHashMap<String, WorkoutMenuTree> branch) async {
    wt.ImageCache().clearImageList();
    await putBranchImageToHash(branch);
    log("downloaded image size in KB: " + wt.ImageCache().downloadSize.toString());
  }

  Future<void> putBranchImageToHash(LinkedHashMap<String, WorkoutMenuTree> branch) async {
    await Future.forEach(branch.keys, (key) async {
      final WorkoutMenuTree? value = branch[key];
      if (value != null && !value.imageName.contains("asset")) {
        await wt.ImageCache().putImageToList(value.id, value.imageName);
      }
    });
  }

  String? getImage(int id, String name) {
    String? imageString;
    if (name.contains("http")) {
      imageString = wt.ImageCache().getImageString(id, name);
    }
    return imageString;
  }

  bool selectedDevice(int deviceId) {
    return !listFilterDevice.contains(deviceId);
  }

  LinkedHashMap<String, WorkoutMenuTree> getFilteredBranch(int? parent) {
    if (parent == null) return LinkedHashMap();

    LinkedHashMap<String, WorkoutMenuTree> branch = menuTreeRepository.getBranch(parent);
    if (!menuTreeRepository.isChild(parent) || ability.toString() == ExerciseAbility.none.toString() || listFilterDevice.isEmpty) {
      return branch;
    }
    LinkedHashMap<String, WorkoutMenuTree> filtered = LinkedHashMap();

    branch.forEach((key, value) {
      final WorkoutMenuTree elem = value;
      if (elem.exerciseType != null) {
        for (int i = 0; i < elem.exerciseType!.devices.length; i++) {
          if (listFilterDevice.contains(elem.exerciseType!.devices[i])) {
            filtered[elem.name] = elem;
          }
        }
      }
    });
    return filtered;
  }
}