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.BluetoothLock.BikeInfoMutable; using System.Threading.Tasks; using TINK.Model.Device; namespace TINK.ViewModel.Bikes.Bike.BluetoothLock { /// /// 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; } private ILocksService LockService { 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, ILocksService lockService, 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, lockService, viewUpdateManager, smartDevice, viewService, bikesViewModel, user) : new 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."); LockService = lockService ?? throw new ArgumentException($"Can not instantiate {this.GetType().Name}-object. Parameter {nameof(lockService)} 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, LockService, 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; } }