using System;
namespace TINK.Model.State
{
using System.ComponentModel;
using System.Runtime.Serialization;
///
/// Manges the state of a bike.
///
[DataContract]
public class StateInfoMutable : INotifyPropertyChanged, IStateInfoMutable
{
///
/// Provider for current date time to calculate remainig time on demand for state of type reserved.
///
private readonly Func m_oDateTimeNowProvider;
// Holds the current disposable state value
private StateInfo m_oStateInfo;
///
/// Backs up remaining time of child object.
///
private TimeSpan? m_oRemainingTime = null;
/// Notifies clients about state changes.
public event PropertyChangedEventHandler PropertyChanged;
///
/// Constructs a state object from source.
///
/// State info to load from.
public StateInfoMutable(
Func p_oDateTimeNowProvider = null,
IStateInfo p_oState = null)
{
// Back up date time provider to be able to pass this to requested- object if state changes to requested.
m_oDateTimeNowProvider = p_oDateTimeNowProvider != null
? p_oDateTimeNowProvider
: () => DateTime.Now;
m_oStateInfo = Create(p_oState, p_oDateTimeNowProvider);
}
///
/// Loads state from immutable source.
///
/// State to load from.
public void Load(IStateInfo p_oState)
{
if (p_oState == 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 = m_oRemainingTime;
// Create new state info object from source.
m_oStateInfo = Create(p_oState, m_oDateTimeNowProvider);
// Update remaining time value.
m_oStateInfo.GetIsStillReserved(out m_oRemainingTime);
if (l_oLastState == m_oStateInfo.Value
&& l_oLastRemainingTime == m_oRemainingTime)
{
return;
}
// State has changed, notify clients.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
}
///
/// Creates a state info object.
///
/// State to load from.
private static StateInfo Create(
IStateInfo p_oState,
Func p_oDateTimeNowProvider)
{
switch (p_oState != null ? p_oState.Value : InUseStateEnum.Disposable)
{
case InUseStateEnum.Disposable:
return new StateInfo();
case InUseStateEnum.Reserved:
// Todo: Handle p_oFrom == null here.
// Todo: Handle p_oDuration == null here.
return new StateInfo(
p_oDateTimeNowProvider,
p_oState.From.Value,
p_oState.MailAddress,
p_oState.Code);
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.
return new StateInfo(
p_oState.From.Value,
p_oState.MailAddress,
p_oState.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.", p_oState.Value));
}
}
///
/// Gets the state value of object.
///
public InUseStateEnum Value
{
get { return m_oStateInfo.Value; }
}
///
/// Member for serialization purposes.
///
[DataMember]
private BaseState StateInfoObject
{
get { return m_oStateInfo.StateInfoObject; }
set
{
var l_oStateOccupied = value as StateOccupiedInfo;
if (l_oStateOccupied != null)
{
m_oStateInfo = new StateInfo(l_oStateOccupied.From, l_oStateOccupied.MailAddress, l_oStateOccupied.Code);
return;
}
var l_oStateRequested = value as StateRequestedInfo;
if (l_oStateRequested != null)
{
m_oStateInfo = new StateInfo(m_oDateTimeNowProvider, l_oStateRequested.From, l_oStateRequested.MailAddress, l_oStateRequested.Code);
return;
}
m_oStateInfo = new StateInfo();
}
}
/// Transforms object to string.
///
///
public new string ToString()
{
return m_oStateInfo.ToString();
}
///
/// Checks and updates state if required.
///
/// Value indicating wheter state has changed
public void UpdateOnTimeElapsed()
{
switch (m_oStateInfo.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 (!m_oStateInfo.GetIsStillReserved(out m_oRemainingTime))
{
// Time has elapsed, switch state to disposable and notfiy client
m_oStateInfo = new StateInfo();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
return;
}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RemainingTime)));
}
/// Updates state from webserver.
/// State of the bike.
/// Date time when bike was reserved/ booked.
/// Lenght of time span for which bike remains booked.
/// Mailaddress of the one which reserved/ booked.
/// Booking code if bike is booked or reserved.
/// Controls whether notify property changed events are fired or not.
public void Load(
InUseStateEnum p_oState,
DateTime? p_oFrom = null,
string p_strMailAddress = null,
string p_strCode = null,
Bikes.Bike.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.Bike.BC.NotifyPropertyChangedLevel.All)
{
var l_oLastState = m_oStateInfo.Value;
switch (p_oState)
{
case InUseStateEnum.Disposable:
m_oStateInfo = new StateInfo();
// Set value to null. Otherwise potentially obsolete value will be taken remaining time.
m_oRemainingTime = null;
break;
case InUseStateEnum.Reserved:
// Todo: Handle p_oFrom == null here.
// Todo: Handle p_oDuration == null here.
m_oStateInfo = new StateInfo(
m_oDateTimeNowProvider,
p_oFrom.Value,
p_strMailAddress,
p_strCode);
// Set value to null. Otherwise potentially obsolete value will be taken remaining time.
m_oRemainingTime = 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.
m_oStateInfo = new StateInfo(
p_oFrom.Value,
p_strMailAddress,
p_strCode);
// Set value to null. Otherwise potentially obsolete value will be taken remaining time.
m_oRemainingTime = null;
break;
default:
// Todo: New state Busy has to be defined.
break;
}
if (l_oLastState != m_oStateInfo.Value
&& notifyLevel == Bikes.Bike.BC.NotifyPropertyChangedLevel.All)
{
// State has changed, notify clients.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
}
}
///
/// If bike is reserved time raimaining while bike stays reserved, null otherwise.
///
public TimeSpan? RemainingTime
{
get
{
switch (m_oStateInfo.Value)
{
// State is either available or occupied.
case InUseStateEnum.Disposable:
case InUseStateEnum.Booked:
return null;
}
if (m_oRemainingTime.HasValue == false)
{
// Value was not yet querried.
// Do querry before returning object.
m_oStateInfo.GetIsStillReserved(out m_oRemainingTime);
}
return m_oRemainingTime;
}
}
public DateTime? From
{
get
{
return m_oStateInfo.From;
}
}
public string MailAddress
{
get
{
return m_oStateInfo.MailAddress;
}
}
public string Code
{
get
{
return m_oStateInfo.Code;
}
}
}
}