using System; using System.ComponentModel; using System.Runtime.CompilerServices; using TINK.Model.Connector; using TINK.Services.BluetoothLock; using TINK.Services.Geolocation; using TINK.Model.User; using TINK.View; using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable; using System.Threading.Tasks; using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.CopriLock { using IRequestHandler = BluetoothLock.IRequestHandler; /// /// View model for a ILockIt bike. /// Provides functionality for views /// - MyBikes /// - BikesAtStation /// public class BikeViewModel : BikeViewModelBase, INotifyPropertyChanged { /// Notifies GUI about changes. public override event PropertyChangedEventHandler PropertyChanged; private IGeolocation Geolocation { get; } /// Holds object which manages requests. private IRequestHandler RequestHandler { get; set; } /// Raises events in order to update GUI. public override void RaisePropertyChanged(object sender, PropertyChangedEventArgs eventArgs) => PropertyChanged?.Invoke(sender, eventArgs); /// Raises events if property values changed in order to update GUI. private void RaisePropertyChangedEvent( IRequestHandler lastHandler, string lastStateText = null, Xamarin.Forms.Color? lastStateColor = null) { if (lastHandler.ButtonText != ButtonText) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonText))); } if (lastHandler.IsButtonVisible != IsButtonVisible) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsButtonVisible))); } if (lastHandler.LockitButtonText != LockitButtonText) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LockitButtonText))); } if (lastHandler.IsLockitButtonVisible != IsLockitButtonVisible) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLockitButtonVisible))); } if (RequestHandler.ErrorText != lastHandler.ErrorText) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ErrorText))); } if (!string.IsNullOrEmpty(lastStateText) && lastStateText != StateText) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StateText))); } if (lastStateColor != null && lastStateColor != StateColor) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StateColor))); } } /// /// Constructs a bike view model object. /// /// Provides info about the smart device (phone, tablet, ...) /// Bike to be displayed. /// Object holding logged in user or an empty user object. /// Provides in use state information. /// View model to be used for progress report and unlocking/ locking view. /// Delegate to open browser. public BikeViewModel( Func isConnectedDelegate, Func connectorFactory, IGeolocation geolocation, Action bikeRemoveDelegate, Func viewUpdateManager, ISmartDevice smartDevice, IViewService viewService, BikeInfoMutable selectedBike, IUser user, IInUseStateInfoProvider stateInfoProvider, IBikesViewModel bikesViewModel, Action openUrlInBrowser) : base(isConnectedDelegate, connectorFactory, bikeRemoveDelegate, viewUpdateManager, smartDevice, viewService, selectedBike, user, stateInfoProvider, bikesViewModel, openUrlInBrowser) { RequestHandler = user.IsLoggedIn ? RequestHandlerFactory.Create( selectedBike, isConnectedDelegate, connectorFactory, geolocation, viewUpdateManager, smartDevice, viewService, bikesViewModel, user) : new BluetoothLock.NotLoggedIn( selectedBike.State.Value, viewService, bikesViewModel); Geolocation = geolocation ?? throw new ArgumentException($"Can not instantiate {this.GetType().Name}-object. Parameter {nameof(geolocation)} can not be null."); } /// /// Handles BikeInfoMutable events. /// Helper member to raise events. Maps model event change notification to view model events. /// Todo: Check which events are received here and filter, to avoid event storm. /// public override void OnSelectedBikeStateChanged() { var lastHandler = RequestHandler; RequestHandler = RequestHandlerFactory.Create( Bike, IsConnectedDelegate, ConnectorFactory, Geolocation, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser); RaisePropertyChangedEvent(lastHandler); } /// Gets visiblity of the copri command button. public bool IsButtonVisible => RequestHandler.IsButtonVisible; /// Gets the text of the copri command button. public string ButtonText => RequestHandler.ButtonText; /// Gets visiblity of the ILockIt command button. public bool IsLockitButtonVisible => RequestHandler.IsLockitButtonVisible; /// Gets the text of the ILockIt command button. public string LockitButtonText => RequestHandler.LockitButtonText; /// Processes request to perform a copri action (reserve bike and cancel reservation). public System.Windows.Input.ICommand OnButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption1())); /// Processes request to perform a ILockIt action (unlock bike and lock bike). public System.Windows.Input.ICommand OnLockitButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption2())); /// Processes request to perform a copri action (reserve bike and cancel reservation). private async Task ClickButton(Task handleRequest) { var lastHandler = RequestHandler; var lastStateText = StateText; var lastStateColor = StateColor; RequestHandler = await handleRequest; if (lastHandler.IsRemoveBikeRequired) { BikeRemoveDelegate(Id); } if (RuntimeHelpers.Equals(lastHandler, RequestHandler)) { // No state change occurred (same instance is returned). return; } RaisePropertyChangedEvent( lastHandler, lastStateText, lastStateColor); } public string ErrorText => RequestHandler.ErrorText; } }