// 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 } }