private Template GetTemplate(TrainerLevelType level, DaysType days, string templateType)
        {
            Template        template        = null;
            TemplateFactory templateFactory = new TemplateFactory(level);

            if (templateType != "DecideForMe")
            {
                template = templateFactory.CreateBasicTemplate(Enum.Parse <TemplateType>(templateType));
            }
            else
            {
                Random random;
                if (days <= DaysType.TwoDays)
                {
                    //FBW
                    template = templateFactory.CreateBasicTemplate(TemplateType.FBW);
                }

                if (days == DaysType.ThreeDays)
                {
                    if (level == TrainerLevelType.Novice)
                    {
                        //FBW
                        template = templateFactory.CreateBasicTemplate(TemplateType.FBW);
                    }

                    if (level == TrainerLevelType.Intermediate)
                    {
                        //FBW/AB (random)
                        var list = new TemplateType[] { TemplateType.AB };
                        random   = new Random();
                        template = templateFactory.CreateBasicTemplate(list[random.Next(list.Length)]);
                    }

                    if (level >= TrainerLevelType.Advanced)
                    {
                        //AB
                        template = templateFactory.CreateBasicTemplate(TemplateType.AB);
                    }
                }

                if (days == DaysType.FourDays)
                {
                    if (level <= TrainerLevelType.Intermediate)
                    {
                        //AB
                        template = templateFactory.CreateBasicTemplate(TemplateType.AB);
                    }

                    if (level >= TrainerLevelType.Advanced)
                    {
                        // AB, ABC, ABCD (random)
                        var list = new TemplateType[] { TemplateType.ABCD, TemplateType.AB, TemplateType.ABC };
                        random   = new Random();
                        template = templateFactory.CreateBasicTemplate(list[random.Next(list.Length)]);
                    }
                }

                if (days == DaysType.FiveDays)
                {
                    if (level <= TrainerLevelType.Advanced)
                    {
                        //ABC
                        template = templateFactory.CreateBasicTemplate(TemplateType.ABC);
                    }
                    else
                    {
                        //ABC, ABCD, ABCDE
                        var list = new TemplateType[] { TemplateType.ABCD, TemplateType.ABC };
                        random   = new Random(list.Length);
                        template = templateFactory.CreateBasicTemplate(list[random.Next(list.Length)]);
                    }
                }

                if (days == DaysType.SixDays || days == DaysType.SevenDays)
                {
                    if (level <= TrainerLevelType.Advanced)
                    {
                        //ABC
                        template = templateFactory.CreateBasicTemplate(TemplateType.ABC);
                    }
                    else
                    {
                        //ABC, ABCD, ABCDE, ABCDEF
                        var list = new[] { TemplateType.AB, TemplateType.ABC, TemplateType.ABCD };
                        random   = new Random(list.Length);
                        template = templateFactory.CreateBasicTemplate(list[random.Next(list.Length)]);
                    }
                }

                if (template == null)
                {
                    throw new Exception("could not instantiate template");
                }
            }


            template.DaysType         = days;
            template.TrainerLevelType = level;
            foreach (Workout templateWorkout in template.Workouts)
            {
                foreach (MuscleExercises templateWorkoutMuscleExercise in templateWorkout.WorkoutHistories.First()
                         .MuscleExercises)
                {
                    var exercisesOfMuscle = _db.Exercises.Where(x =>
                                                                x.MuscleType == templateWorkoutMuscleExercise.MuscleType).ToList();

                    for (int i = 0; i < templateWorkoutMuscleExercise.Exercises.Count; i++)
                    {
                        Random r = new Random();
                        var    exerciseSetting =
                            _programService.PickExerciseSetting(
                                _programService.GetRelevantExerciseData(i, level,
                                                                        templateWorkoutMuscleExercise.MuscleType));

                        var workoutExercise = templateWorkoutMuscleExercise.Exercises[i];

                        var exerciseChose = _programService.PickExercise(i, exerciseSetting, exercisesOfMuscle);
                        workoutExercise.Name = exerciseChose.Name;

                        var numOfSets = exerciseSetting.Sets[r.Next(exerciseSetting.Sets.Length)];
                        var reps      = _programService.PickReps(exerciseSetting);
                        var rest      = _programService.PickRest(exerciseSetting);
                        for (int j = 0; j < numOfSets; j++)
                        {
                            var set = new Set()
                            {
                                Reps = reps, Rest = rest
                            };
                            workoutExercise.Sets.Add(set);
                        }

                        exercisesOfMuscle.Remove(exerciseChose);
                    }
                }
            }

            return(template);
        }
        private WorkoutHistory ProgressWorkout(WorkoutHistory wh, Workout w)
        {
            var workoutHistory = CloneWorkoutHistory(wh);
            var r = new Random();

            foreach (var workoutHistoryMuscleExercise in workoutHistory.MuscleExercises)
            {
                var exercises = workoutHistoryMuscleExercise.Exercises;

                var mechanicalRepsCount = exercises.SelectMany(x => x.Sets).Count(x => x.Reps < 10);
                var stressRepsCount     = exercises.SelectMany(x => x.Sets).Count(x => x.Reps >= 10);

                int numOfExercisesToProgress = (int)Math.Ceiling((decimal)exercises.Count / 2);

                Dictionary <int, WorkoutExercise> selectedExercises;
                VolumeType vt;
                RepsType[] neededReps;

                if (mechanicalRepsCount == stressRepsCount)
                {
                    var volumeTypes = new[] { VolumeType.MetabolicStress, VolumeType.MechanicalTension };
                    vt = volumeTypes[r.Next(volumeTypes.Length)];
                }

                else if (stressRepsCount < mechanicalRepsCount)
                {
                    vt = VolumeType.MetabolicStress;
                }
                else
                {
                    vt = VolumeType.MechanicalTension;
                }

                switch (vt)
                {
                case VolumeType.MetabolicStress:
                    selectedExercises = exercises.Where(x => x.Sets.Any(s => s.Reps < 10)).Select((e, i) => new { i, e }).ToDictionary(x => x.i, x => x.e);
                    neededReps        = new[] { RepsType.HighAdvanced, RepsType.HighInter, RepsType.HighNovice };
                    break;

                case VolumeType.MechanicalTension:

                    selectedExercises = exercises.Where(x => x.Sets.Any(s => s.Reps >= 10)).Select((e, i) => new { i, e }).ToDictionary(x => x.i, x => x.e);
                    neededReps        = new[] { RepsType.Low, RepsType.MedAdvanced, RepsType.MedInter, RepsType.MedNovice };
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
                var modifyFuncs = new ProgramService.ModifyExerciseDelegate[]
                {
                    _programService.ModifyExerciseChangeReps,
                    _programService.ModifyExerciseChangeRest,
                    _programService.ModifyExerciseChangeSet
                };

                selectedExercises = selectedExercises.OrderBy(x => r.Next()).Take(numOfExercisesToProgress).ToDictionary(x => x.Key, x => x.Value);

                foreach (var selectedExercise in selectedExercises)
                {
                    var exerciseSettings = _programService.GetRelevantExerciseData(selectedExercise.Key,
                                                                                   w.Template.TrainerLevelType, workoutHistoryMuscleExercise.MuscleType);

                    var exerciseSetting = exerciseSettings.Where(x => neededReps.Any(y => y == x.Key))
                                          .OrderBy(x => r.Next())
                                          .Take(1).Select(x => x.Value).Single();

                    modifyFuncs = modifyFuncs.OrderBy(x => r.Next()).ToArray();
                    foreach (var modifyExerciseDelegate in modifyFuncs)
                    {
                        if (modifyExerciseDelegate.Invoke(selectedExercise.Value, exerciseSetting))
                        {
                            break;
                        }
                    }
                }
            }

            return(workoutHistory);
        }