// Copyright (c) 2023 schick Informatik
// Description: Container model for shipcall related info
//
using BreCalClient.misc.Api;
using BreCalClient.misc.Model;
using System;
using System.Collections.Generic;
namespace BreCalClient
{
///
/// Container model to aggregate separate models for the Shipcall control data binding
///
public class ShipcallControlModel
{
#region Enumerations
[Flags]
public enum StatusFlags
{
RED,
GREEN,
YELLOW,
BLINK_1,
BLINK_2
};
#endregion
#region Properties
public Shipcall? Shipcall { get; set; }
public Ship? Ship { get; set; }
public string? Berth { get; set; }
internal Dictionary AssignedParticipants { get; } = new();
public List Times { get; set; } = new();
public DateTime? Eta
{
get
{
Times? agencyTimes = this.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
if((agencyTimes != null) && (agencyTimes.EtaBerth != null))
return agencyTimes.EtaBerth;
return Shipcall?.Eta;
}
}
public DateTime? Etd
{
get
{
Times? agencyTimes = this.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
if ((agencyTimes != null) && (agencyTimes.EtdBerth != null))
return agencyTimes.EtdBerth;
return Shipcall?.Etd;
}
}
public EvaluationType LightMode
{
get
{
EvaluationType elm = EvaluationType.Undefined;
if (this.Shipcall != null)
{
if(this.Shipcall.Evaluation.HasValue)
{
elm = this.Shipcall.Evaluation.Value;
}
}
return elm;
}
}
public string Title
{
get
{
if (this.Shipcall == null) return "";
return string.Format("{0} {1}", this.Shipcall.Type, this.Ship?.Name);
}
}
///
/// This property attempts to store the (hopefully short) shifting sequence in the topmost
/// byte of the "flags" integer which will not be used for the forseeable future
///
/// This is a workaround to avoid adding another field to the interface and is only used in the
/// client display anyway. You could say hack as well.
///
public byte? ShiftSequence
{
get
{
if((this.Shipcall == null) || (this.Shipcall.Flags == null)) return null;
return (byte?) (this.Shipcall?.Flags >> 24);
}
set
{
if ((value != null) && (this.Shipcall != null))
{
int currentFlag = this.Shipcall.Flags ?? 0;
int moveUp = ((value ?? 0) << 24);
this.Shipcall.Flags = (currentFlag & 0xffffff) | moveUp;
}
}
}
#endregion
#region public methods
public void AssignParticipants()
{
this.AssignedParticipants.Clear();
if (Shipcall != null)
{
foreach (ParticipantAssignment participantAssignment in Shipcall.Participants)
{
AssignedParticipants[(Extensions.ParticipantType)participantAssignment.Type] = participantAssignment;
}
}
}
internal Times? GetTimesForParticipantType(Extensions.ParticipantType type)
{
if (AssignedParticipants.ContainsKey(type)) {
int participantId = AssignedParticipants[type].ParticipantId;
foreach (Times times in this.Times)
{
if ((times.ParticipantId == participantId) && (times.ParticipantType == (int) type))
return times;
}
if(type == Extensions.ParticipantType.AGENCY)
{
// if I am BSMD and no agency entry was found this means we are editing the agency entry
if(App.Participant.Type == (int) Extensions.ParticipantType.BSMD)
{
foreach(Times times in this.Times)
{
if ((times.ParticipantId == App.Participant.Id) && (times.ParticipantType == (int) Extensions.ParticipantType.AGENCY))
return times;
}
}
}
}
return null;
}
public bool ContainsRemarkText(string text)
{
if(Shipcall != null)
{
foreach(Times times in this.Times)
{
if (times.Remarks == null) continue;
if(times.Remarks.Contains(text)) return true;
}
}
return false;
}
///
/// Get berth display text for columns AGENT and TERMINAL
///
public string? GetBerthText(Times? times)
{
string? timesBerthText = null;
string? scArrivalBerthText = null;
string? scDepartureBerthText = null;
string? berthText = null;
Berth? berth;
if((times != null) && times.BerthId.HasValue)
{
berth = BreCalLists.AllBerths.Find((x) => x.Id == times.BerthId.Value);
timesBerthText = berth?.Name;
}
berth = BreCalLists.AllBerths?.Find((x) => x.Id == this.Shipcall?.ArrivalBerthId);
scArrivalBerthText = berth?.Name;
berth = BreCalLists.AllBerths?.Find((x) => x.Id == this.Shipcall?.DepartureBerthId);
scDepartureBerthText= berth?.Name;
switch(this.Shipcall?.Type)
{
case ShipcallType.Arrival:
berthText = timesBerthText ?? scArrivalBerthText;
break;
case ShipcallType.Departure:
berthText = timesBerthText ?? scDepartureBerthText;
break;
case ShipcallType.Shifting:
if (times?.ParticipantType != (int)Extensions.ParticipantType.TERMINAL)
berthText = (scDepartureBerthText ?? "") + " / " + (timesBerthText ?? scArrivalBerthText);
else
berthText = scDepartureBerthText ?? "";
break;
default: break;
}
return berthText;
}
public string GetETAETD()
{
DateTime theDate = DateTime.Now;
if(this.Shipcall != null)
{
if (this.Shipcall.Type == ShipcallType.Arrival)
theDate = this.Shipcall.Eta ?? DateTime.Now;
else
theDate = this.Shipcall.Etd ?? DateTime.Now;
}
Times? agentTimes = this.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
if(agentTimes != null)
{
if(this.Shipcall?.Type == ShipcallType.Arrival)
{
if (agentTimes.EtaBerth != null)
theDate = agentTimes.EtaBerth.Value;
}
else
{
if (agentTimes.EtdBerth != null)
theDate = agentTimes.EtdBerth.Value;
}
}
return theDate.ToString();
}
///
/// After closing the edit shipcall or edit agency dialogs, the times assignments may have changed.
/// This function updates the assignments for existing times records accordingly and saves them.
///
/// API reference to PUT eidted times
internal async void UpdateTimesAssignments(TimesApi api)
{
foreach (Extensions.ParticipantType participantType in this.AssignedParticipants.Keys)
{
Times? times = this.GetTimesForParticipantType(participantType);
if(times == null) continue;
if(times.ParticipantId != this.AssignedParticipants[participantType].ParticipantId)
{
times.ParticipantId = this.AssignedParticipants[participantType].ParticipantId;
await api.TimesUpdateAsync(times);
}
}
// if somebody just removed an assignment it is gone fom AssignedParticipants, but there is still a
// times record left which must be removed
List deleteTimes = new();
foreach (Times times in this.Times)
{
bool foundTimes = false;
foreach (ParticipantAssignment pa in this.AssignedParticipants.Values)
{
if((pa.ParticipantId == times.ParticipantId) && (pa.Type == times.ParticipantType))
{
foundTimes = true;
break;
}
}
if (!foundTimes)
{
deleteTimes.Add(times);
}
}
foreach(Times times in deleteTimes)
{
api.TimesDelete(times.Id);
this.Times.Remove(times);
}
}
#endregion
#region helper
internal Participant? GetParticipantForType(Extensions.ParticipantType participantType)
{
if(AssignedParticipants.ContainsKey(participantType) && BreCalLists.ParticipantLookupDict.ContainsKey(AssignedParticipants[participantType].ParticipantId))
return BreCalLists.ParticipantLookupDict[AssignedParticipants[participantType].ParticipantId];
return null;
}
private bool IsFlagSet(StatusFlags flag)
{
if(this.Shipcall == null) return false;
return (this.Shipcall.Flags & (int) flag) != 0;
}
#endregion
}
}