2023-04-19 12:14:14 +02:00
|
|
|
using System;
|
2021-05-13 20:03:07 +02:00
|
|
|
|
2024-04-09 12:53:23 +02:00
|
|
|
namespace ShareeBike.Model.State
|
2021-05-13 20:03:07 +02:00
|
|
|
{
|
2022-09-06 16:08:19 +02:00
|
|
|
using System.ComponentModel;
|
|
|
|
using System.Runtime.Serialization;
|
2023-06-06 12:00:24 +02:00
|
|
|
|
2022-09-06 16:08:19 +02:00
|
|
|
/// <summary>
|
|
|
|
/// Manges the state of a bike.
|
|
|
|
/// </summary>
|
|
|
|
[DataContract]
|
|
|
|
public class StateInfoMutable : INotifyPropertyChanged, IStateInfoMutable
|
|
|
|
{
|
|
|
|
/// <summary>
|
2023-05-11 17:39:28 +02:00
|
|
|
/// Provider for current date time to calculate remaining time on demand for state of type reserved.
|
2022-09-06 16:08:19 +02:00
|
|
|
/// </summary>
|
|
|
|
private readonly Func<DateTime> _DateTimeNowProvider;
|
|
|
|
|
|
|
|
// Holds the current disposable state value
|
|
|
|
private StateInfo _StateInfo;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Backs up remaining time of child object.
|
|
|
|
/// </summary>
|
|
|
|
private TimeSpan? _RemainingTime = null;
|
|
|
|
|
|
|
|
/// <summary> Notifies clients about state changes. </summary>
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Constructs a state object from source.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="state">State info to load from.</param>
|
|
|
|
public StateInfoMutable(
|
|
|
|
Func<DateTime> dateTimeNowProvider = null,
|
|
|
|
IStateInfo state = null)
|
|
|
|
{
|
|
|
|
// Back up date time provider to be able to pass this to requested- object if state changes to requested.
|
|
|
|
_DateTimeNowProvider = dateTimeNowProvider != null
|
|
|
|
? dateTimeNowProvider
|
|
|
|
: () => DateTime.Now;
|
|
|
|
|
|
|
|
_StateInfo = Create(state, dateTimeNowProvider);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Loads state from immutable source.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="state">State to load from.</param>
|
|
|
|
public void Load(IStateInfo state)
|
|
|
|
{
|
|
|
|
if (state == null)
|
|
|
|
{
|
|
|
|
throw new ArgumentException("Can not load state info, object must not be null.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Back up last state value and remaining time value
|
|
|
|
// to be able to check whether an event has to be fired or not.
|
|
|
|
var l_oLastState = Value;
|
|
|
|
var l_oLastRemainingTime = _RemainingTime;
|
|
|
|
|
|
|
|
// Create new state info object from source.
|
|
|
|
_StateInfo = Create(state, _DateTimeNowProvider);
|
|
|
|
|
|
|
|
// Update remaining time value.
|
|
|
|
_StateInfo.GetIsStillReserved(out _RemainingTime);
|
|
|
|
|
|
|
|
if (l_oLastState == _StateInfo.Value
|
|
|
|
&& l_oLastRemainingTime == _RemainingTime)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// State has changed, notify clients.
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Creates a state info object.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="state">State to load from.</param>
|
|
|
|
private static StateInfo Create(
|
|
|
|
IStateInfo state,
|
|
|
|
Func<DateTime> dateTimeNowProvider)
|
|
|
|
{
|
|
|
|
switch (state != null ? state.Value : InUseStateEnum.Disposable)
|
|
|
|
{
|
|
|
|
case InUseStateEnum.Disposable:
|
|
|
|
case InUseStateEnum.FeedbackPending:
|
|
|
|
return new StateInfo(state != null ? state.Value == InUseStateEnum.FeedbackPending : false);
|
|
|
|
|
|
|
|
case InUseStateEnum.Reserved:
|
|
|
|
return new StateInfo(
|
2023-06-06 12:00:24 +02:00
|
|
|
state.From.HasValue ? dateTimeNowProvider : (() => DateTime.MaxValue),
|
|
|
|
state.From.HasValue ? state.From.Value : DateTime.MaxValue,
|
|
|
|
state.MaxReservationTimeSpan ?? StateRequestedInfo.UNKNOWNMAXRESERVATIONTIMESPAN,
|
2022-09-06 16:08:19 +02:00
|
|
|
state.MailAddress,
|
|
|
|
state.Code);
|
|
|
|
|
|
|
|
|
|
|
|
case InUseStateEnum.Booked:
|
|
|
|
// Todo: Handle p_oFrom == null here.
|
2023-06-06 12:00:24 +02:00
|
|
|
// Todo: Clarify question: What to do if code changes form one value to another? This should never happen.
|
|
|
|
// Todo: Clarify question: What to do if from time changes form one value to another? This should never happen.
|
2022-09-06 16:08:19 +02:00
|
|
|
return new StateInfo(
|
|
|
|
state.From.Value,
|
|
|
|
state.MailAddress,
|
|
|
|
state.Code);
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Todo: New state Busy has to be defined.
|
|
|
|
throw new Exception(string.Format("Can not create new state info object. Unknown state {0} detected.", state.Value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the state value of object.
|
|
|
|
/// </summary>
|
|
|
|
public InUseStateEnum Value
|
|
|
|
=> _StateInfo.Value;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Member for serialization purposes.
|
|
|
|
/// </summary>
|
|
|
|
[DataMember]
|
|
|
|
private BaseState StateInfoObject
|
|
|
|
{
|
|
|
|
get { return _StateInfo.StateInfoObject; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
var l_oStateOccupied = value as StateOccupiedInfo;
|
|
|
|
if (l_oStateOccupied != null)
|
|
|
|
{
|
|
|
|
_StateInfo = new StateInfo(l_oStateOccupied.From, l_oStateOccupied.MailAddress, l_oStateOccupied.Code);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var l_oStateRequested = value as StateRequestedInfo;
|
|
|
|
if (l_oStateRequested != null)
|
|
|
|
{
|
2023-06-06 12:00:24 +02:00
|
|
|
_StateInfo = new StateInfo(_DateTimeNowProvider, l_oStateRequested.From, l_oStateRequested.MaxReservationTimeSpan, l_oStateRequested.MailAddress, l_oStateRequested.Code);
|
2022-09-06 16:08:19 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_StateInfo = new StateInfo();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Transforms object to string.
|
|
|
|
/// </summary>
|
|
|
|
/// <returns></returns>
|
2023-11-06 12:23:09 +01:00
|
|
|
public override string ToString()
|
2022-09-06 16:08:19 +02:00
|
|
|
{
|
|
|
|
return _StateInfo.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Checks and updates state if required.
|
|
|
|
/// </summary>
|
2023-06-06 12:00:24 +02:00
|
|
|
/// <returns>Value indicating whether state has changed</returns>
|
2022-09-06 16:08:19 +02:00
|
|
|
public void UpdateOnTimeElapsed()
|
|
|
|
{
|
|
|
|
switch (_StateInfo.Value)
|
|
|
|
{
|
|
|
|
// State is disposable or booked. No need to update "OnTimeElapsed"
|
|
|
|
case InUseStateEnum.Disposable:
|
|
|
|
case InUseStateEnum.Booked:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if maximum reserved time has elapsed.
|
|
|
|
if (!_StateInfo.GetIsStillReserved(out _RemainingTime))
|
|
|
|
{
|
2023-06-06 12:00:24 +02:00
|
|
|
// Time has elapsed, switch state to disposable and notify client
|
2022-09-06 16:08:19 +02:00
|
|
|
_StateInfo = new StateInfo();
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RemainingTime)));
|
|
|
|
}
|
|
|
|
|
2023-06-06 12:00:24 +02:00
|
|
|
/// <summary> Updates state from web server. </summary>
|
2022-09-06 16:08:19 +02:00
|
|
|
/// <param name="state">State of the bike.</param>
|
|
|
|
/// <param name="from">Date time when bike was reserved/ booked.</param>
|
2023-06-06 12:00:24 +02:00
|
|
|
/// <param name="maxReservationTimeSpan">Time span for which a bike can be reserved.</param>
|
|
|
|
/// <param name="mailAddress">Mail address of the one which reserved/ booked.</param>
|
2022-09-06 16:08:19 +02:00
|
|
|
/// <param name="code">Booking code if bike is booked or reserved.</param>
|
|
|
|
/// <param name="supressNotifyPropertyChanged">Controls whether notify property changed events are fired or not.</param>
|
|
|
|
public void Load(
|
|
|
|
InUseStateEnum state,
|
|
|
|
DateTime? from = null,
|
2023-06-06 12:00:24 +02:00
|
|
|
TimeSpan? maxReservationTimeSpan = null,
|
2022-09-06 16:08:19 +02:00
|
|
|
string mailAddress = null,
|
|
|
|
string code = null,
|
|
|
|
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.All)
|
|
|
|
{
|
|
|
|
var lastState = _StateInfo.Value;
|
|
|
|
|
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case InUseStateEnum.Disposable:
|
|
|
|
_StateInfo = new StateInfo();
|
|
|
|
|
|
|
|
// Set value to null. Otherwise potentially obsolete value will be taken remaining time.
|
|
|
|
_RemainingTime = null;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InUseStateEnum.Reserved:
|
|
|
|
_StateInfo = new StateInfo(
|
2023-06-06 12:00:24 +02:00
|
|
|
from.HasValue ? _DateTimeNowProvider : (() => DateTime.MaxValue),
|
|
|
|
from.HasValue ? from.Value : DateTime.MaxValue,
|
|
|
|
maxReservationTimeSpan.HasValue ? maxReservationTimeSpan.Value : StateRequestedInfo.UNKNOWNMAXRESERVATIONTIMESPAN,
|
2022-09-06 16:08:19 +02:00
|
|
|
mailAddress,
|
|
|
|
code);
|
|
|
|
|
|
|
|
// Set value to null. Otherwise potentially obsolete value will be taken remaining time.
|
|
|
|
_RemainingTime = null;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InUseStateEnum.Booked:
|
|
|
|
// Todo: Handle p_oFrom == null here.
|
|
|
|
// Todo: Clearify question: What to do if code changes form one value to another? This should never happen.
|
|
|
|
// Todo: Clearify question: What to do if from time changes form one value to another? This should never happen.
|
|
|
|
_StateInfo = new StateInfo(
|
|
|
|
from.Value,
|
|
|
|
mailAddress,
|
|
|
|
code);
|
|
|
|
|
|
|
|
// Set value to null. Otherwise potentially obsolete value will be taken remaining time.
|
|
|
|
_RemainingTime = null;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Todo: New state Busy has to be defined.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastState != _StateInfo.Value
|
|
|
|
&& notifyLevel == Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.All)
|
|
|
|
{
|
|
|
|
// State has changed, notify clients.
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2023-06-06 12:00:24 +02:00
|
|
|
/// If bike is reserved time remaining while bike stays reserved, null otherwise.
|
2022-09-06 16:08:19 +02:00
|
|
|
/// </summary>
|
|
|
|
public TimeSpan? RemainingTime
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
switch (_StateInfo.Value)
|
|
|
|
{
|
|
|
|
// State is either available or occupied.
|
|
|
|
case InUseStateEnum.Disposable:
|
|
|
|
case InUseStateEnum.Booked:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_RemainingTime.HasValue == false)
|
|
|
|
{
|
2023-06-06 12:00:24 +02:00
|
|
|
// Value was not yet queried.
|
2023-04-19 12:14:14 +02:00
|
|
|
// Do query before returning object.
|
2022-09-06 16:08:19 +02:00
|
|
|
_StateInfo.GetIsStillReserved(out _RemainingTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
return _RemainingTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public DateTime? From
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return _StateInfo.From;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public string MailAddress
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return _StateInfo.MailAddress;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public string Code
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return _StateInfo.Code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-13 20:03:07 +02:00
|
|
|
}
|