using System; using System.Threading.Tasks; using Serilog; using ShareeBike.MultilingualResources; using ShareeBike.Services.Logging; using ShareeBike.View; using static ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.OpenCommand; using System.ComponentModel; namespace ShareeBike.ViewModel.Bikes.Bike.BluetoothLock { /// /// View model for action open bluetooth lock. /// /// internal class OpenLockActionViewModel : IOpenCommandListener { /// /// View model to be used for progress report and unlocking/ locking view. /// private IBikesViewModel BikesViewModel { get; set; } /// /// View service to show modal notifications. /// private IViewService ViewService { get; } /// Object to start or stop update of view model objects from Copri. private Func ViewUpdateManager { get; } /// Bike open. private Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable SelectedBike { get; } /// /// Constructs the object. /// /// Bike to open. /// Object to start or stop update of view model objects from Copri. /// View service to show modal notifications. /// View model to be used for progress report and unlocking/ locking view. /// public OpenLockActionViewModel( Model.Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable selectedBike, Func viewUpdateManager, IViewService viewService, IBikesViewModel bikesViewModel) { SelectedBike = selectedBike; ViewUpdateManager = viewUpdateManager; ViewService = viewService; BikesViewModel = bikesViewModel ?? throw new ArgumentException($"Can not construct {GetType().Name}-object. {nameof(bikesViewModel)} must not be null."); } /// /// Processes the open lock progress. /// /// Current step to process. public void ReportStep(Step step) { switch (step) { case Step.OpeningLock: // 1a.Step: Open lock BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock; BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessOpenLockStepOpenLock; BikesViewModel.RentalProcess.ImportantStepInfoText = AppResources.MarkingRentalProcessOpenLockObserve; break; case Step.GetLockInfos: // 1b.Step: Get lock infos BikesViewModel.RentalProcess.ImportantStepInfoText = AppResources.MarkingRentalProcessOpenLockWait; break; case Step.WaitStopPolling: // 1c.Step: Wait for polling to be stopped break; case Step.UpdateLockingState: // 1c.Step: Sent info to backend BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState; BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessOpenLockStepUpload; BikesViewModel.RentalProcess.ImportantStepInfoText = AppResources.MarkingRentalProcessOpenLockWait; break; } } /// /// Processes the open lock state. /// /// State to process. /// Textual details describing current state. public async Task ReportStateAsync(State state, string details) { switch (state) { case State.OutOfReachError: BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed; BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, AppResources.ErrorLockOutOfReach, AppResources.MessageAnswerOk); break; case State.CouldntOpenBoldStatusIsUnknownError: BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed; BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, AppResources.ErrorOpenLockStatusUnknown, AppResources.MessageAnswerOk); break; case State.CouldntOpenBoldIsBlockedError: BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed; BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, AppResources.ErrorOpenLockBoldBlocked, AppResources.MessageAnswerOk); break; case State.CouldntOpenInconsistentStateError: BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed; BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, AppResources.ErrorOpenLockStillClosed, AppResources.MessageAnswerOk); break; case State.GeneralOpenError: BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed; BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorOpenLockTitle, details, AppResources.MessageAnswerOk); break; case State.StopPollingFailed: break; case State.WebConnectFailed: BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate; break; case State.ResponseIsInvalid: BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate; break; case State.BackendUpdateFailed: BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate; break; } } /// Open lock in order to pause ride and update COPRI lock state. public async Task OpenLockAsync() { Log.ForContext().Information("User request to open lock of bike {bikeId}.", SelectedBike.Id); // lock GUI BikesViewModel.IsIdle = false; // Stop Updater BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; var stopPollingTask = ViewUpdateManager().StopAsync(); // Clear logging memory sink to avoid passing log data not related to returning of bike to backend. // Log data is passed to backend when calling CopriCallsHttps.DoReturn(). MemoryStackSink.ClearMessages(); // 1. Step // Parameter for RentalProcess View BikesViewModel.StartRentalProcess(new RentalProcessViewModel(SelectedBike.Id) { State = CurrentRentalProcess.OpenLock, StepIndex = 1, Result = CurrentStepStatus.None }); try { #if USELOCALINSTANCE var command = new OpenCommand(SelectedBike, GeolocationService, LockService, IsConnectedDelegate, ConnectorFactory, ViewUpdateManager); await command.Invoke(this); #else await SelectedBike.OpenLockAsync(this, stopPollingTask); #endif Log.ForContext().Information("Lock of bike {bikeId} opened successfully.", SelectedBike.Id); } catch (Exception exception) { Log.ForContext().Information("Lock of bike {bikeId} can not be opened. {@exception}", SelectedBike.Id, exception); BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; await ViewUpdateManager().StartAsync(); BikesViewModel.ActionText = string.Empty; BikesViewModel.RentalProcess.State = CurrentRentalProcess.None; BikesViewModel.IsIdle = true; return; } BikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; await ViewUpdateManager().StartAsync(); BikesViewModel.ActionText = string.Empty; //Confirmation message Log.ForContext().Information("User request to park bike {bikeId}.", SelectedBike.Id); await ViewService.DisplayAlert( AppResources.MessageRentalProcessOpenLockFinishedTitle, AppResources.MessageRentalProcessOpenLockFinishedText, AppResources.MessageAnswerOk); BikesViewModel.RentalProcess.State = CurrentRentalProcess.None; BikesViewModel.IsIdle = true; return; } } }