// Copyright (c) 2023 schick Informatik
// Description: Bremen calling main window
//
using BreCalClient.misc.Api;
using BreCalClient.misc.Client;
using BreCalClient.misc.Model;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace BreCalClient
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
#region Fields
private const int SHIPCALL_UPDATE_INTERVAL_SECONDS = 30;
private readonly DefaultApi _api;
private readonly ObservableCollection _controlModels = new();
private List _ships = new();
private readonly ConcurrentDictionary _shipLookupDict = new();
private List _berths = new();
private readonly ConcurrentDictionary _berthLookupDict = new();
private List _participants = new();
private readonly Dictionary _participantLookupDict = new();
private readonly Dictionary _shipCallControlDict = new();
private readonly CancellationTokenSource _tokenSource = new();
private LoginResult? _loginResult;
#endregion
#region Enums
private enum ConnectionStatus
{
UNDEFINED,
SUCCESSFUL,
FAILED
}
#endregion
#region Construction
public MainWindow()
{
InitializeComponent();
_api = new DefaultApi();
_api.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
}
#endregion
#region event handler
private void Window_Loaded(object sender, RoutedEventArgs e)
{
labelGeneralStatus.Text = $"Connection {ConnectionStatus.UNDEFINED}";
labelVersion.Text = "V. " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_tokenSource.Cancel();
}
private async void buttonLogin_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(this.textPassword.Password) || string.IsNullOrEmpty(this.textUsername.Text))
{
this.labelLoginResult.Content = Application.Current.FindResource("textUserNamePasswordEmpty").ToString();
return;
}
Credentials credentials = new(username: textUsername.Text.Trim(),
password: textPassword.Password.Trim());
try
{
_loginResult = await _api.LoginPostAsync(credentials);
if(_loginResult != null)
{
if(_loginResult.Id > 0)
{
this.busyIndicator.IsBusy = false;
this._api.Configuration.ApiKey["Authorization"] = _loginResult.Token;
this.LoadStaticLists();
this.labelUsername.Text = $"{_loginResult.FirstName} {_loginResult.LastName}";
}
}
labelGeneralStatus.Text = $"Connection {ConnectionStatus.SUCCESSFUL}";
}
catch (ApiException ex)
{
this.labelLoginResult.Content = ex.Message;
labelGeneralStatus.Text = $"Connection {ConnectionStatus.FAILED}";
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
labelGeneralStatus.Text = $"Connection {ConnectionStatus.FAILED}";
}
}
private void buttonExit_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void buttonNew_Click(object sender, RoutedEventArgs e)
{
EditShipcallControl esc = new()
{
Participants = this._participants,
Ships = this._ships,
Berths = this._berths
};
if (esc.ShowDialog() ?? false)
{
// save new dialog model
// add dialog model to list
}
}
private void buttonInfo_Click(object sender, RoutedEventArgs e)
{
AboutDialog ad = new();
ad.ChangePasswordRequested += async (oldPw, newPw) =>
{
if (_loginResult != null)
{
UserDetails ud = new()
{
Id = _loginResult.Id,
FirstName = _loginResult.FirstName,
LastName = _loginResult.LastName,
UserPhone = _loginResult.UserPhone,
OldPassword = oldPw,
NewPassword = newPw
};
try
{
await _api.UserPutAsync(ud);
}
catch (Exception ex)
{
this.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}));
}
}
};
ad.ShowDialog();
}
#endregion
#region private methods
private async void LoadStaticLists()
{
this._berths = await _api.BerthsGetAsync();
foreach(var berth in this._berths)
_berthLookupDict[berth.Id] = berth;
this.searchFilterControl.SetBerths(this._berths);
this._ships = await _api.ShipsGetAsync();
foreach(var ship in this._ships)
_shipLookupDict[ship.Id] = ship;
this._participants = await _api.ParticipantsGetAsync();
List agencies = new();
foreach (Participant participant in this._participants)
{
this._participantLookupDict[participant.Id] = participant;
if (_loginResult?.ParticipantId == participant.Id)
{
App.Participant = participant;
EnableControlsForParticipant();
}
if(participant.IsFlagSet(Extensions.ParticipantType.AGENCY))
agencies.Add(participant);
}
this.searchFilterControl.SetAgencies(agencies);
_ = Task.Run(() => RefreshShipcalls());
}
private void EnableControlsForParticipant()
{
if (App.Participant.IsFlagSet(Extensions.ParticipantType.BSMD))
this.buttonNew.Visibility = Visibility.Visible;
}
public async Task RefreshShipcalls()
{
while (!_tokenSource.Token.IsCancellationRequested)
{
List? shipcalls = null;
try
{
shipcalls = await _api.ShipcallsGetAsync();
this.Dispatcher.Invoke(new Action(() =>
{
labelGeneralStatus.Text = $"Connection {ConnectionStatus.SUCCESSFUL}";
labelGeneralStatus.Text = $"Ok";
}));
}
catch (Exception ex)
{
this.Dispatcher.Invoke(new Action(() =>
{
labelGeneralStatus.Text = $"Connection {ConnectionStatus.FAILED}";
labelStatusBar.Text = ex.Message;
}));
}
if (shipcalls != null)
{
foreach (Shipcall shipcall in shipcalls)
{
ShipcallControlModel? selectedSCMModel = null;
foreach (ShipcallControlModel scm in this._controlModels)
{
if (scm.Shipcall?.Id == shipcall.Id)
{
selectedSCMModel = scm;
break;
}
}
if (selectedSCMModel != null)
{
selectedSCMModel.Shipcall = shipcall;
}
else
{
// no: create new entry
selectedSCMModel = new()
{
Shipcall = shipcall
};
if (this._shipLookupDict.ContainsKey(shipcall.ShipId))
selectedSCMModel.Ship = this._shipLookupDict[shipcall.ShipId];
if (this._berthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0))
selectedSCMModel.Berth = this._berthLookupDict[shipcall.ArrivalBerthId ?? 0].Name;
_controlModels.Add(selectedSCMModel);
this.Dispatcher.Invoke(new Action(() =>
{
ShipcallControl sc = new()
{
Height = 120,
ShipcallControlModel = selectedSCMModel
};
sc.TimesRequested += Sc_TimesRequested;
sc.EditRequested += Sc_EditRequested;
this.stackPanel.Children.Add(sc);
this._shipCallControlDict[shipcall.Id] = sc;
}));
}
selectedSCMModel.AssignParticipants(this._participants);
this.Dispatcher.Invoke((Action)(() =>
{
this._shipCallControlDict[shipcall.Id].RefreshData();
}));
}
List removeList = new();
foreach (ShipcallControlModel scm in this._controlModels)
{
if (shipcalls.Find(s => s.Id == scm.Shipcall?.Id) == null) // the model is no longer in the search result
{
if((scm.Shipcall != null) && this._shipCallControlDict.ContainsKey(scm.Shipcall.Id))
{
this.Dispatcher.Invoke((Action)(() =>
{
this.stackPanel.Children.Remove(this._shipCallControlDict[scm.Shipcall.Id]);
}));
this._shipCallControlDict.Remove(scm.Shipcall.Id);
}
}
}
}
await Task.Delay(TimeSpan.FromSeconds(SHIPCALL_UPDATE_INTERVAL_SECONDS), _tokenSource.Token);
}
}
private async void Sc_EditRequested(ShipcallControl obj)
{
Shipcall? sc = obj.ShipcallControlModel?.Shipcall;
if (sc != null)
{
EditShipcallControl esc = new()
{
Shipcall = sc,
Ships = _ships,
Participants = _participants,
Berths = _berths
};
if(esc.ShowDialog() ?? false)
{
try
{
await _api.ShipcallsPutAsync(sc);
obj.RefreshData();
}
catch (Exception ex)
{
ShowErrorDialog(ex.Message, "Error saving edited shipcall");
}
}
}
}
private void Sc_TimesRequested(ShipcallControl obj)
{
// TODO: get the shipcall, load and show a list of the times
}
private void ShowErrorDialog(string message, string caption)
{
Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Error);
}));
}
#endregion
}
}