public ExerciseProgramsViewModel(
            IAudioService audioService,
            IDelayService delayService,
            IExerciseDocumentService exerciseDocumentService,
            ILoggerService loggerService,
            ISchedulerService schedulerService,
            ISpeechService speechService,
            IStateService stateService,
            IScreen hostScreen,
            ExerciseProgramViewModelFactory exerciseProgramViewModelFactory)
        {
            audioService.AssertNotNull(nameof(audioService));
            delayService.AssertNotNull(nameof(delayService));
            exerciseDocumentService.AssertNotNull(nameof(exerciseDocumentService));
            loggerService.AssertNotNull(nameof(loggerService));
            schedulerService.AssertNotNull(nameof(schedulerService));
            speechService.AssertNotNull(nameof(speechService));
            stateService.AssertNotNull(nameof(stateService));
            hostScreen.AssertNotNull(nameof(hostScreen));
            exerciseProgramViewModelFactory.AssertNotNull(nameof(exerciseProgramViewModelFactory));

            this.exerciseDocumentService = exerciseDocumentService;
            this.stateService            = stateService;
            this.logger      = loggerService.GetLogger(this.GetType());
            this.hostScreen  = hostScreen;
            this.disposables = new CompositeDisposable();

            var documentsFromCache = this
                                     .stateService
                                     .GetAsync <string>(exerciseProgramsCacheKey)
                                     .Where(x => x != null)
                                     .Select(x => new DocumentSourceWith <string>(DocumentSource.Cache, x));

            var documentsFromService = this
                                       .exerciseDocumentService
                                       .ExerciseDocument
                                       .Where(x => x != null)
                                       .Select(x => new DocumentSourceWith <string>(DocumentSource.Service, x));

            var documents = documentsFromCache
                            .Catch((Exception ex) => Observable.Empty <DocumentSourceWith <string> >())
                            .Concat(documentsFromService)
                            .Do(x => this.logger.Debug("Received document from {0}.", x.Source))
                            .Publish();

            var safeDocuments = documents
                                .Catch((Exception ex) => Observable.Empty <DocumentSourceWith <string> >());

            var results = documents
                          .ObserveOn(schedulerService.TaskPoolScheduler)
                          .Select(
                x =>
            {
                IResult <ExercisePrograms> parsedExercisePrograms;

                using (this.logger.Perf("Parsing exercise programs from {0}.", x.Source))
                {
                    parsedExercisePrograms = ExercisePrograms.TryParse(x.Item, audioService, delayService, loggerService, speechService);
                }

                return(new DocumentSourceWith <IResult <ExercisePrograms> >(x.Source, parsedExercisePrograms));
            })
                          .Publish();

            var safeResults = results
                              .Catch((Exception ex) => Observable.Empty <DocumentSourceWith <IResult <ExercisePrograms> > >());

            safeResults
            .Select(x => x.Item.WasSuccessful ? null : x.Item.ToString())
            .ObserveOn(schedulerService.MainScheduler)
            .Subscribe(x => this.ParseErrorMessage = x)
            .AddTo(this.disposables);

            results
            .Select(x => !x.Item.WasSuccessful ? ExerciseProgramsViewModelStatus.ParseFailed : x.Source == DocumentSource.Cache ? ExerciseProgramsViewModelStatus.LoadedFromCache : ExerciseProgramsViewModelStatus.LoadedFromService)
            .Catch((Exception ex) => Observable.Return(ExerciseProgramsViewModelStatus.LoadFailed))
            .ObserveOn(schedulerService.MainScheduler)
            .Subscribe(x => this.Status = x)
            .AddTo(this.disposables);

            safeResults
            .Select(x => x.Item.WasSuccessful ? x.Item.Value : null)
            .ObserveOn(schedulerService.MainScheduler)
            .Subscribe(x => this.Model = x)
            .AddTo(this.disposables);

            this.WhenAnyValue(x => x.Model)
            .Select(x => x == null ? null : x.Programs.CreateDerivedCollection(y => exerciseProgramViewModelFactory(y)))
            .ObserveOn(schedulerService.MainScheduler)
            .Subscribe(x => this.Programs = x)
            .AddTo(this.disposables);

            safeDocuments
            .Where(x => x.Source == DocumentSource.Service)
            .SelectMany(x => this.stateService.SetAsync(exerciseProgramsCacheKey, x.Item))
            .Subscribe()
            .AddTo(this.disposables);

            results
            .Connect()
            .AddTo(this.disposables);

            documents
            .Connect()
            .AddTo(this.disposables);

            this
            .WhenAnyValue(x => x.SelectedProgram)
            .Where(x => x != null)
            .Subscribe(x => this.hostScreen.Router.Navigate.Execute(x))
            .AddTo(this.disposables);

            this
            .hostScreen
            .Router
            .CurrentViewModel
            .OfType <ExerciseProgramsViewModel>()
            .Subscribe(x => x.SelectedProgram = null)
            .AddTo(this.disposables);
        }