using System;
using System.Threading.Tasks;
using TINK.Model.Bike.BluetoothLock;
using TINK.Repository;
using TINK.Repository.Exception;
using TINK.Repository.Request;
using TINK.Repository.Response;
using TINK.Model.User.Account;
using TINK.Model.Device;
using System.Collections.Generic;
using TINK.Model.MiniSurvey;
namespace TINK.Model.Connector
{
public class CommandLoggedIn : BaseLoggedIn, ICommand
{
/// True if connector has access to copri server, false if cached values are used.
public bool IsConnected => CopriServer.IsConnected;
/// Is raised whenever login state has changed.
public event LoginStateChangedEventHandler LoginStateChanged;
/// Constructs a copri query object.
/// Server which implements communication.
public CommandLoggedIn(ICopriServerBase p_oCopriServer,
string sessionCookie,
string mail,
Func dateTimeProvider) : base(p_oCopriServer, sessionCookie, mail, dateTimeProvider)
{
}
///
/// Logs user in.
/// If log in succeeds either and session might be updated if it was no more valid (logged in by an different device).
/// If log in fails (password modified) session cookie is set to empty.
/// If communication fails an TINK.Repository.Exception is thrown.
///
/// Account to use for login.
public Task DoLogin(string mail, string password, string deviceId)
{
if (string.IsNullOrEmpty(mail))
{
throw new ArgumentNullException("Can not loging user. Mail address must not be null or empty.");
}
throw new Exception($"Fehler beim Anmelden von unter {mail}. Benutzer {Mail} ist bereits angemeldet.");
}
/// Logs user out.
public async Task DoLogout()
{
AuthorizationoutResponse l_oResponse = null;
try
{
l_oResponse = (await CopriServer.DoAuthoutAsync()).GetIsResponseOk();
}
catch (AuthcookieNotDefinedException)
{
// Cookie is no more defined, i.e. no need to logout user at copri because user is already logged out.
// Just ignore this error.
// User logged out, log in state changed. Notify parent object to update.
LoginStateChanged?.Invoke(this, new LoginStateChangedEventArgs());
return;
}
catch (Exception)
{
throw;
}
// User logged out, log in state changed. Notify parent object to update.
LoginStateChanged?.Invoke(this, new LoginStateChangedEventArgs());
}
///
/// Request to reserve a bike.
///
/// Bike to book.
public async Task DoReserve(Bikes.Bike.BC.IBikeInfoMutable bike)
{
if (bike == null)
{
throw new ArgumentNullException("Can not reserve bike. No bike object available.");
}
BikeInfoReservedOrBooked response;
try
{
response = (await CopriServer.DoReserveAsync(bike.Id, bike.OperatorUri)).GetIsReserveResponseOk(bike.Id);
}
catch (Exception)
{
// Exception was not expected or too many subsequent excepitons detected.
throw;
}
bike.Load(response, Mail, DateTimeProvider, Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
}
/// Request to cancel a reservation.
/// Bike to cancel reservation.
public async Task DoCancelReservation(
Bikes.Bike.BC.IBikeInfoMutable bike)
{
if (bike == null)
{
throw new ArgumentNullException("Can not cancel reservation of bike. No bike object available.");
}
ReservationCancelReturnResponse response;
try
{
response = (await CopriServer.DoCancelReservationAsync(bike.Id, bike.OperatorUri)).GetIsCancelReservationResponseOk(bike.Id);
}
catch (Exception)
{
// Exception was not expected or too many subsequent excepitons detected.
throw;
}
bike.Load(Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
}
/// Get authentication keys.
/// Bike to get new keys for.
public async Task CalculateAuthKeys(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
{
if (bike == null)
{
throw new ArgumentNullException("Can not calculate auth keys. No bike object available.");
}
switch (bike.State.Value)
{
case State.InUseStateEnum.Reserved:
case State.InUseStateEnum.Booked:
break;
default:
throw new ArgumentNullException($"Can not calculate auth keys. Unexpected bike state {bike.State.Value} detected.");
}
BikeInfoReservedOrBooked response;
Guid guid = (bike is BikeInfoMutable btBike) ? btBike.LockInfo.Guid : new Guid();
try
{
response = (await CopriServer.CalculateAuthKeysAsync(bike.Id, bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
}
catch (Exception)
{
// Exception was not expected or too many subsequent excepitons detected.
throw;
}
UpdaterJSON.Load(
bike,
response,
Mail,
DateTimeProvider,
Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
}
/// Updates COPRI lock state for a booked bike.
/// Bike to update locking state for.
/// Response on updating locking state.
public async Task UpdateLockingStateAsync(
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto location)
{
if (bike == null)
{
throw new ArgumentNullException("Can not book bike. No bike object available.");
}
if (bike.State.Value != State.InUseStateEnum.Booked)
{
throw new ArgumentNullException($"Can not update locking state of bike. Unexpected booking state {bike.State} detected.");
}
lock_state? state = null;
switch (bike.LockInfo.State)
{
case LockingState.Open:
state = lock_state.unlocked;
break;
case LockingState.Closed:
state = lock_state.locked;
break;
}
if (!state.HasValue)
{
throw new ArgumentNullException($"Can not update locking state of bike. Unexpected locking state {bike.LockInfo.State} detected.");
}
try
{
(await CopriServer.UpdateLockingStateAsync(
bike.Id,
location,
state.Value,
bike.LockInfo.BatteryPercentage,
bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
}
catch (Exception)
{
// Exception was not expected or too many subsequent excepitons detected.
throw;
}
}
/// Request to book a bike.
/// Bike to book.
public async Task DoBook(
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
{
if (bike == null)
{
throw new ArgumentNullException(nameof(bike), "Can not book bike. No bike object available.");
}
BikeInfoReservedOrBooked response;
var btBike = bike as BikeInfoMutable;
Guid guid = btBike != null ? btBike.LockInfo.Guid : new Guid();
double batteryPercentage = btBike != null ? btBike.LockInfo.BatteryPercentage : double.NaN;
try
{
response = (await CopriServer.DoBookAsync(
bike.Id,
guid,
batteryPercentage,
bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
}
catch (Exception)
{
// Exception was not expected or too many subsequent excepitons detected.
throw;
}
bike.Load(
response,
Mail,
DateTimeProvider,
Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
}
/// Request to return a bike.
/// Bike to return.
/// Position of the bike.
/// Provides info about hard and software.
public async Task DoReturn(
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
LocationDto location,
ISmartDevice smartDevice)
{
if (bike == null)
{
throw new ArgumentNullException("Can not return bike. No bike object available.");
}
DoReturnResponse response;
try
{
response = (await CopriServer.DoReturn(bike.Id, location, smartDevice, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
}
catch (Exception)
{
// Exception was not expected or too many subsequent exceptions detected.
throw;
}
bike.Load(Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
return response?.Create() ?? new BookingFinishedModel();
}
///
/// Submits feedback to copri server.
///
/// Feedback to submit.
#if USCSHARP9
public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri)
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
#else
public async Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri)
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
#endif
/// Submits mini survey to copri server.
/// Collection of answers.
public async Task DoSubmitMiniSurvey(IDictionary answers)
=> await CopriServer.DoSubmitMiniSurvey(answers);
}
}