using System; using System.Threading.Tasks; using ShareeBike.Model.Connector; using ShareeBike.MultilingualResources; using ShareeBike.View; using Serilog; using ConnectAndGetStateCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.ConnectAndGetStateCommand; using AuthCommand = ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.AuthCommand; using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock; namespace ShareeBike.ViewModel.Bikes.Bike { /// /// Return bike action. /// /// Type of owner. public class ConnectLockActionViewModel : AuthCommand.IAuthCommandListener, ConnectAndGetStateCommand.IConnectAndGetStateCommandListener { /// /// 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 private IBikeInfoMutable SelectedBike { get; set; } /// Provides a connector object. protected Func ConnectorFactory { get; } /// /// Constructs the object. /// /// Bike to close. /// 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 ConnectLockActionViewModel( IBikeInfoMutable selectedBike, Func viewUpdateManager, IViewService viewService, IBikesViewModel bikesViewModel) { SelectedBike = selectedBike; ViewUpdateManager = viewUpdateManager; ViewService = viewService; BikesViewModel = bikesViewModel ?? throw new ArgumentException($"Can not construct {typeof(EndRentalActionViewModel)}-object. {nameof(bikesViewModel)} must not be null."); } /// /// Processes the start reservation progress. /// /// Current step to process. public void ReportStep(AuthCommand.Step step) { switch (step) { case AuthCommand.Step.Authenticate: BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessRequestBikeFirstStepAuthenticateInfo; BikesViewModel.ActionText = AppResources.ActivityTextAuthenticate; break; } } /// /// Processes the authentication state. /// /// State to process. /// Textual details describing current state. public async Task ReportStateAsync(AuthCommand.State state, string details) { switch (state) { case AuthCommand.State.WebConnectFailed: BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed; BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorNoConnectionTitle, AppResources.ErrorNoWeb, AppResources.MessageAnswerOk); break; case AuthCommand.State.GeneralAuthError: BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed; BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAdvancedAlert( AppResources.ErrorAccountInvalidAuthorization, details, AppResources.ErrorTryAgain, AppResources.MessageAnswerOk); break; } } /// /// Processes the connect to lock progress. /// /// Current step to process. public void ReportStep(ConnectAndGetStateCommand.Step step) { switch (step) { case ConnectAndGetStateCommand.Step.ConnectLock: BikesViewModel.RentalProcess.StepInfoText = AppResources.MarkingRentalProcessRequestBikeFirstStepConnect; BikesViewModel.ActionText = AppResources.ActivityTextSearchingLock; break; case ConnectAndGetStateCommand.Step.GetLockingState: break; } } /// /// Processes the connect to lock state. /// /// State to process. /// Textual details describing current state. public async Task ReportStateAsync(ConnectAndGetStateCommand.State state, string details) { switch (state) { case ConnectAndGetStateCommand.State.OutOfReachError: BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorConnectLockTitle, AppResources.ErrorLockOutOfReach, AppResources.MessageAnswerOk); break; case ConnectAndGetStateCommand.State.BluetoothOff: BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorConnectLockTitle, AppResources.ErrorLockBluetoothNotOn, AppResources.MessageAnswerOk); break; case ConnectAndGetStateCommand.State.NoLocationPermission: BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorConnectLockTitle, AppResources.ErrorNoLocationPermission, AppResources.MessageAnswerOk); break; case ConnectAndGetStateCommand.State.LocationServicesOff: BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAlert( AppResources.ErrorConnectLockTitle, AppResources.ErrorLockLocationOff, AppResources.MessageAnswerOk); break; case ConnectAndGetStateCommand.State.GeneralConnectLockError: BikesViewModel.ActionText = string.Empty; await ViewService.DisplayAdvancedAlert( AppResources.ErrorConnectLockTitle, details, AppResources.ErrorTryAgain, AppResources.MessageAnswerOk); break; } } /// Search and connect to lock. public async Task ConnectLockAsync() { Log.ForContext().Information("User request to end rental of bike {bikeId}.", SelectedBike.Id); // lock GUI BikesViewModel.IsIdle = false; // Stop Updater BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; await ViewUpdateManager().StopAsync(); // 1a.Step: Authenticate try { #if USELOCALINSTANCE var command = new AuthCommand(SelectedBike, ConnectorFactory, ViewUpdateManager); await command.Invoke(this); #else await SelectedBike.AuthAsync(this); #endif Log.ForContext().Information("User authenticated for bike {bikeId} successfully.", SelectedBike.Id); } catch (Exception exception) { Log.ForContext().Information("User could not be authenticated for bike {bikeId}. {@exception}", SelectedBike.Id, exception); BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; await ViewUpdateManager().StartAsync(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; } // 1b.Step: Get locking state try { #if USELOCALINSTANCE var command = new ConnectLockAndGetLockingStateCommand(SelectedBike, LockService, ConnectorFactory, ViewUpdateManager); await command.Invoke(this); #else await SelectedBike.ConnectAsync(this); #endif Log.ForContext().Information("Lock of {bikeId} connected successfully.", SelectedBike.Id); } catch (Exception exception) { Log.ForContext().Information("Lock of bike {bikeId} could not be connected. {@exception}", SelectedBike.Id, exception); BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; await ViewUpdateManager().StartAsync(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; } BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease; await ViewUpdateManager().StartAsync(); BikesViewModel.ActionText = string.Empty; BikesViewModel.IsIdle = true; return; } } }