public ExerciseProgramViewModelBuilder()
 {
     this.loggerService = new LoggerServiceMock(MockBehavior.Loose);
     this.schedulerService = new SchedulerServiceMock(MockBehavior.Loose);
     this.hostScreen = new ScreenMock(MockBehavior.Loose);
     this.model = new ExerciseProgramBuilder();
 }
 public ExerciseProgramViewModelBuilder WithModel(ExerciseProgram model)
 {
     this.model = model;
     return this;
 }
        public ExerciseProgramViewModel(
            ILoggerService loggerService,
            IScheduler scheduler,
            IScreen hostScreen,
            ExerciseProgram model)
        {
            Ensure.ArgumentNotNull(loggerService, nameof(loggerService));
            Ensure.ArgumentNotNull(scheduler, nameof(scheduler));
            Ensure.ArgumentNotNull(hostScreen, nameof(hostScreen));
            Ensure.ArgumentNotNull(model, nameof(model));

            this.logger = loggerService.GetLogger(this.GetType());
            this.scheduler = scheduler;
            this.model = model;
            this.hostScreen = hostScreen;
            this.disposables = new CompositeDisposable();
            this.exercises = this.model.Exercises.CreateDerivedCollection(x => new ExerciseViewModel(scheduler, x, this.WhenAnyValue(y => y.ExecutionContext)));

            this
                .WhenAnyValue(
                    x => x.ExecutionContext,
                    x => x.ExecutionContext.IsCancelled,
                    (ec, isCancelled) => ec != null && !isCancelled)
                .ObserveOn(scheduler)
                .Subscribe(x => this.IsStarted = x)
                .AddTo(this.disposables);

            this
                .WhenAnyValue(x => x.ExecutionContext)
                .Select(x => x == null ? Observable.Return(false) : x.WhenAnyValue(y => y.IsPaused))
                .Switch()
                .ObserveOn(scheduler)
                .Subscribe(x => this.IsPaused = x)
                .AddTo(this.disposables);

            this
                .WhenAnyValue(x => x.ExecutionContext)
                .Select(x => x == null ? Observable.Return(TimeSpan.Zero) : x.WhenAnyValue(y => y.Progress))
                .Switch()
                .ObserveOn(scheduler)
                .Subscribe(x => this.ProgressTimeSpan = x)
                .AddTo(this.disposables);

            this
                .WhenAnyValue(x => x.ProgressTimeSpan)
                .Select(x => x.TotalMilliseconds / this.model.Duration.TotalMilliseconds)
                .Subscribe(x => this.Progress = x)
                .AddTo(this.disposables);

            this
                .WhenAnyValue(
                    x => x.ExecutionContext,
                    x => x.ExecutionContext.CurrentExercise,
                    (ec, currentExercise) => ec == null ? null : currentExercise)
                .Select(x => this.Exercises.SingleOrDefault(y => y.Model == x))
                .ObserveOn(scheduler)
                .Subscribe(x => this.CurrentExercise = x)
                .AddTo(this.disposables);

            var canStart = this
                .WhenAnyValue(x => x.IsStarted)
                .Select(x => !x);

            this.startCommand = ReactiveCommand
                .CreateFromObservable<TimeSpan?, Unit>(this.OnStart, canStart, scheduler)
                .AddTo(this.disposables);

            var canPause = this
                .WhenAnyValue(x => x.IsStarted)
                .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && !isPaused)
                .ObserveOn(scheduler);

            this.pauseCommand = ReactiveCommand
                .CreateFromObservable(this.OnPause, canPause, scheduler)
                .AddTo(this.disposables);

            var canResume = this
                .WhenAnyValue(x => x.IsStarted)
                .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && isPaused)
                .ObserveOn(scheduler);

            this.resumeCommand = ReactiveCommand
                .CreateFromObservable(this.OnResume, canResume, scheduler)
                .AddTo(this.disposables);

            var canSkipBackwards = this
                .WhenAnyValue(
                    x => x.ExecutionContext,
                    x => x.ProgressTimeSpan,
                    (ec, progress) => new { ExecutionContext = ec, Progress = progress })
                .Select(x => x.ExecutionContext != null && x.Progress >= skipBackwardsThreshold)
                .ObserveOn(scheduler);

            this.skipBackwardsCommand = ReactiveCommand
                .CreateFromObservable(this.OnSkipBackwards, canSkipBackwards, scheduler)
                .AddTo(this.disposables);

            var canSkipForwards = this
                .WhenAnyValue(
                    x => x.ExecutionContext,
                    x => x.CurrentExercise,
                    (ec, currentExercise) => new { ExecutionContext = ec, CurrentExercise = currentExercise })
                .Select(x => x.ExecutionContext != null && x.CurrentExercise != null && x.CurrentExercise != this.exercises.LastOrDefault())
                .ObserveOn(scheduler);

            this.skipForwardsCommand = ReactiveCommand
                .CreateFromObservable(this.OnSkipForwards, canSkipForwards, scheduler)
                .AddTo(this.disposables);

            this.startCommand
                .CanExecute
                .Subscribe(x => this.IsStartVisible = x)
                .AddTo(this.disposables);

            this.pauseCommand
                .CanExecute
                .Subscribe(x => this.IsPauseVisible = x)
                .AddTo(this.disposables);

            this.resumeCommand
                .CanExecute
                .Subscribe(x => this.IsResumeVisible = x)
                .AddTo(this.disposables);

            this.startCommand
                .ThrownExceptions
                .Subscribe(ex => this.OnThrownException("start", ex))
                .AddTo(this.disposables);

            this.pauseCommand
                .ThrownExceptions
                .Subscribe(ex => this.OnThrownException("pause", ex))
                .AddTo(this.disposables);

            this.resumeCommand
                .ThrownExceptions
                .Subscribe(ex => this.OnThrownException("resume", ex))
                .AddTo(this.disposables);

            this.skipBackwardsCommand
                .ThrownExceptions
                .Subscribe(ex => this.OnThrownException("skip backwards", ex))
                .AddTo(this.disposables);

            this.skipForwardsCommand
                .ThrownExceptions
                .Subscribe(ex => this.OnThrownException("skip forwards", ex))
                .AddTo(this.disposables);

            // we don't use a reactive command here because switching in different commands causes it to get confused and
            // command binding leaves the target button disabled. We could also have not used command binding to get around
            // this problem
            this.playbackCommand = new PlaybackCommandImpl(this);

            // cancel the exercise program if the user navigates away
            this
                .hostScreen
                .Router
                .NavigationStack
                .ItemsRemoved
                .OfType<ExerciseProgramViewModel>()
                .SelectMany(x => x.Stop())
                .Subscribe()
                .AddTo(this.disposables);
        }
 private ExerciseProgramViewModel ExerciseProgramViewModelFactory(ExerciseProgram model) =>
     new ExerciseProgramViewModel(
         this.loggerService.Value,
         this.schedulerService.Value,
         this.mainViewModel.Value,
         model);
 public ExerciseProgramsBuilder AddProgram(ExerciseProgram program)
 {
     this.programs.Add(program);
     return this;
 }
 public ExerciseProgramViewModelBuilder WithModel(ExerciseProgram model) =>
     this.With(ref this.model, model);