using System; using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; using System.Windows; using System.Windows.Documents; using System.Linq; using System.Windows.Input; using System.Diagnostics; using C4IT.FASD.Base; using C4IT.Logging; using C4IT.FASD.Cockpit.Communication; using C4IT.MultiLanguage; using static C4IT.Logging.cLogManager; using FasdDesktopUi.Basics.Helper; using FasdDesktopUi.Basics.Models; using FasdDesktopUi.Basics.UserControls; using FasdDesktopUi.Pages; using FasdDesktopUi.Pages.CustomMessageBox; using FasdDesktopUi.Config; using FasdDesktopUi.Pages.SearchPage; using FasdDesktopUi.Pages.TicketCompletion; using Newtonsoft.Json; using FasdDesktopUi.Basics.Services.ProtocollService; using FasdDesktopUi.Basics.Services.SupportCase; using FasdDesktopUi.Basics.Services.RelationService; namespace FasdDesktopUi.Basics { public sealed class cSupportCaseDataProvider { public class cSearchResultRelationsByClass : Dictionary> { } public FlowDocument CaseNotes { get; private set; } = new FlowDocument(); private static cSupportCaseDataProvider CurrentProvider; public static Pages.SlimPage.SlimPageView slimPage { get; set; } public static Pages.DetailsPage.DetailsPageView detailsPage { get; set; } private static readonly Dictionary DataProviders = new Dictionary(); public EventHandler NotepadDocumentUpdated { get; set; } #region Properties private readonly object caseIdLockObject = new object(); public Guid CaseId { get; private set; } public bool IsActive { get { return CaseId != Guid.Empty || slimPage?.IsActive is true || detailsPage?.IsActive is true; } } public SupportCasePageBase ActiveSupportCaseWindow { get { if (detailsPage?.IsVisible is true) return detailsPage; if (slimPage?.IsVisible is true) return slimPage; return null; } } System.Timers.Timer caseAliveTimer; public cHealthCardDataHelper HealthCardDataHelper { get; private set; } public cDirectConnectionHelper DirectConnectionHelper { get; private set; } public cQuickActionProtocollHelper QuickActionProtocollHelper { get; private set; } public cSearchResultRelationsByClass CaseRelations { get; } = new cSearchResultRelationsByClass(); public cNamedParameterList NamedParameterEntries { get; private set; } = new cNamedParameterList(); public cF4sdIdentityList Identities { get; set; } #if DEBUG public string lastCaseRelationsJson = null; #endif #endregion public static event EventHandler CaseChanged; private cSupportCaseDataProvider() { HealthCardDataHelper = new cHealthCardDataHelper(this); DirectConnectionHelper = new cDirectConnectionHelper(this); QuickActionProtocollHelper = new cQuickActionProtocollHelper(this); } public static async Task GetDataProviderForAsync(List storedRelations, cF4sdApiSearchResultRelation selectedRelation, IRelationService relationService) { try { if (selectedRelation == null) { Debug.Assert(true, "The selected relation must not be null here!"); LogEntry("The selected relation must not be null here!", LogLevels.Error); return null; } // get the identities of the selected relation var Identities = selectedRelation.Identities.Clone(); // get the main indentities from all identites (normally this is the user, in a special case, where a computer was searched and found without any user activities, it could be the computer) var MainIdentity = Identities.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.User); if (MainIdentity == null) MainIdentity = Identities.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.Computer); if (MainIdentity == null) MainIdentity = Identities.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.VirtualSession); if (MainIdentity == null) MainIdentity = Identities.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.MobileDevice); if (MainIdentity == null) { LogEntry("Couldn't find neither a user identity nor a computer identity for the selected realtion.", LogLevels.Error); return null; } var firstHealthCardInformationClass = cF4sdIdentityEntry.GetFromSearchResult(selectedRelation.Type); // if we have the special relation computer -> user, the initial information class is changed to computer ( user -> computer ) if (firstHealthCardInformationClass == enumFasdInformationClass.User) if (Identities.Any(v => (v.Class == enumFasdInformationClass.Computer))) firstHealthCardInformationClass = enumFasdInformationClass.Computer; if (Identities.Any(v => (v.Class == enumFasdInformationClass.VirtualSession))) firstHealthCardInformationClass = enumFasdInformationClass.VirtualSession; if (Identities.Any(v => (v.Class == enumFasdInformationClass.MobileDevice))) firstHealthCardInformationClass = enumFasdInformationClass.MobileDevice; var requiredInformationClasses = new List() { firstHealthCardInformationClass }; // if we have an opened support case, close it immediately if (cSupportCaseDataProvider.CurrentProvider != null) { await cSupportCaseDataProvider.CurrentProvider.CloseCaseAsync(); cSupportCaseDataProvider.CurrentProvider = null; } if (!DataProviders.TryGetValue(MainIdentity.Id, out var _result)) { _result = new cSupportCaseDataProvider(); DataProviders.Add(MainIdentity.Id, _result); } _result.NamedParameterEntries = new cNamedParameterList(_result); _result.StartCase(MainIdentity.Id); var supportCase = SupportCaseFactory.Get(MainIdentity, relationService, _result); cSupportCaseDataProvider.detailsPage.SetSupportCase(supportCase); cSupportCaseDataProvider.slimPage.SetSupportCase(supportCase); var Status = await _result.SetViewDataAsync(requiredInformationClasses, Identities, true); if (!Status) return null; CurrentProvider = _result; // start the direct connection if (_result.DirectConnectionHelper.IsDirectConnectionActive is false) _ = Task.Run(async () => { try { await _result.DirectConnectionHelper.DirectConnectionStartAsync(); } catch { } }); // start the slim or detaild page bool shouldSkipSlimView = Identities.Any(identity => identity.Class is enumFasdInformationClass.Ticket) || cFasdCockpitConfig.Instance.Global.ShouldSkipSlimView; if (shouldSkipSlimView) cSupportCaseDataProvider.detailsPage?.Show(); else cSupportCaseDataProvider.slimPage.Show(); return _result; } catch (Exception E) { LogException(E); } return null; } public void StartCase(Guid userId) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { lock (caseIdLockObject) { CaseId = Guid.NewGuid(); } if (cFasdCockpitConfig.IsAnalyticsActive) { _ = Task.Run(async () => { try { var caseParameter = new cF4SDCaseParameters() { CaseId = CaseId, SessionId = cFasdCockpitConfig.SessionId, UserId = userId }; var successfullyStartedCase = await cFasdCockpitCommunicationBase.Instance.StartCase(caseParameter); if (successfullyStartedCase is false) { LogEntry($"Could not start case '{caseParameter.CaseId}' for user '{caseParameter.UserId}' in session '{caseParameter.SessionId}'.", LogLevels.Warning); return; } caseAliveTimer = new System.Timers.Timer(1000 * 60 * 1); caseAliveTimer.Elapsed += (async (sender, e) => { await cFasdCockpitCommunicationBase.Instance.KeepAliveCase(CaseId); }); caseAliveTimer.Start(); } catch (Exception E) { LogException(E); } }); } TimerView.ResetTimer(); CaseChanged?.Invoke(this, new EventArgs()); } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } } public async Task CloseCaseAsync() { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { Guid tempCaseId; lock (caseIdLockObject) { tempCaseId = CaseId; if (CaseId.Equals(Guid.Empty)) return; CaseId = Guid.Empty; } detailsPage?.EndPause(); List tasks = new List(); tasks.Add(DirectConnectionHelper.DirectConnectionStopAsync()); if (cFasdCockpitConfig.IsAnalyticsActive) { var workTimes = TimerView.GetWorkTimes(); var nettoTime = workTimes["NettoWorkingTime"]; var caseParameter = new cF4SDCaseStatusParameters() { CaseId = tempCaseId, StatusId = CaseStatus.Finished, ActiveTime = (double)nettoTime }; tasks.Add(cFasdCockpitCommunicationBase.Instance.UpdateCase(caseParameter, TimerView.caseTimes)); caseAliveTimer?.Stop(); caseAliveTimer?.Dispose(); } await Task.WhenAll(tasks); ResetCachedData(); } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } } private void ResetCachedData() { try { CaseNotes = new FlowDocument(); NotepadDocumentUpdated?.Invoke(this, true); DirectConnectionHelper.Reset(); HealthCardDataHelper.Reset(); F4SDProtocoll.Instance.Clear(); } catch (Exception E) { LogException(E); } } static bool IsFlowDocumentNotEmpty(FlowDocument doc) { if (doc == null) return false; // Erstellt einen TextRange, der den gesamten Inhalt des Dokuments abdeckt TextRange textRange = new TextRange(doc.ContentStart, doc.ContentEnd); return !string.IsNullOrWhiteSpace(textRange.Text); } public static async Task SupportTicketActiveNoticeAsync() { // returns true, if we can continue with the search process // returns false, if we have to stop the search process, because the current case remains opened MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (cSupportCaseDataProvider.CurrentProvider?.IsActive != true) return true; Mouse.OverrideCursor = null; var ownerWindow = cSupportCaseDataProvider.CurrentProvider.ActiveSupportCaseWindow; var ButtonText = new List() { cMultiLanguageSupport.GetItem("Searchbar.NewSearch.ActiveSupportCase.CloseCase"), cMultiLanguageSupport.GetItem("Searchbar.NewSearch.ActiveSupportCase.CancelCase"), cMultiLanguageSupport.GetItem("Searchbar.NewSearch.ActiveSupportCase.ContinueCase") }; int ResultIndex = 1; var completitionPolicy = cFasdCockpitConfig.Instance.Global.TicketConfiguration.CompletitionPolicy; if (completitionPolicy == enumShowDocumentCaseDialog.always || (completitionPolicy == enumShowDocumentCaseDialog.ifRequired && (F4SDProtocoll.Instance.GetOfType().Count() > 0 || IsFlowDocumentNotEmpty(CurrentProvider.CaseNotes)))) { ResultIndex = CustomMessageBox.Show(cMultiLanguageSupport.GetItem("Searchbar.NewSearch.ActiveSupportCase"), ButtonText, "First Aid Service Desk", enumHealthCardStateLevel.Warning, Owner: ownerWindow, TopMost: true, CenterScreen: ownerWindow is Pages.SlimPage.SlimPageView, MaxWidth: 700); } switch (ResultIndex) { case 0: // close case with ticket completion var _res = CloseCaseWithTicket(ownerWindow); if (_res) { ownerWindow?.Hide(); return true; } break; case 1: // close case immediately await cSupportCaseDataProvider.CurrentProvider?.CloseCaseAsync(); ownerWindow?.Hide(); return true; default: // return to current support case break; } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return false; } public static async Task CloseSupportCaseAsync() { MethodBase CM = null; if (DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var doClose = await SupportTicketActiveNoticeAsync(); if (doClose) { if (cSearchManager.Instance?.HistoryList?.LastOrDefault()?.isSeen != true) { SearchPageView.Instance.ActivateSearchView(); } } return doClose; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return false; } public static bool CloseCaseWithTicket(Window ownerWindow) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var closeCaseDialogResult = TicketCompletion.Show(cSupportCaseDataProvider.CurrentProvider, ownerWindow); if (closeCaseDialogResult != null) { var _h = cSupportCaseDataProvider.CurrentProvider?.CloseCaseAsync(); } return closeCaseDialogResult != null; } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } return false; } public async Task SetViewDataAsync(List requiredInformationClasses, cF4sdIdentityList Identities, bool isNewCase) { try { var couldGetHealthCard = HealthCardDataHelper.TrySetSelectedHealthcard(requiredInformationClasses); if (!couldGetHealthCard) { LogEntry("Couldn't find a matching healthcard.", LogLevels.Error); CustomMessageBox.Show(cMultiLanguageSupport.GetItem("SearchBar.NoValidHealthcard"), "Load new case", enumHealthCardStateLevel.Error); return false; } var requiredTables = cHealthCard.GetRequiredTables(HealthCardDataHelper.SelectedHealthCard); if (cF4SDCustomDialogConfig.Instance != null) { var requiredTablesForCustomDialogs = cF4SDCustomDialogConfig.Instance.GetRequiredTables(HealthCardDataHelper.SelectedHealthCard.InformationClasses); foreach (var table in requiredTablesForCustomDialogs) { requiredTables.Add(table); } } var isOk = await HealthCardDataHelper.LoadingHelper.GetHealthCardRawDataAsync(new cF4sdHealthCardRawDataRequest() { Identities = Identities, Tables = requiredTables.ToList(), MaxAge = cF4SDCockpitXmlConfig.Instance?.HealthCardConfig?.SearchResultAge ?? 14 }); if (isNewCase) HealthCardDataHelper.LoadingHelper.LastDataRequest = DateTime.Now; if (isOk == false) { string identityString = ""; foreach (var identity in Identities) { identityString += "\n" + identity.Id + " (Type: " + identity.Class + ")"; } LogEntry($"Error trying to receive healthcard data for following identities: {identityString}", LogLevels.Error); CustomMessageBox.Show($"Error trying to receive healthcard data for following identities: {identityString}", "Data Error", enumHealthCardStateLevel.Error); return false; } var slimPageData = await HealthCardDataHelper.SlimCard.GetDataAsync(); var detailsPageData = await HealthCardDataHelper.DetailPage.GetDataAsync(); slimPage?.SetPropertyValues(slimPageData); detailsPage?.SetPropertyValues(detailsPageData); return true; } catch (Exception E) { LogException(E); } return false; } public async Task ChangeHealthCardAsync(enumFasdInformationClass informationClass) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await SetViewDataAsync(new List() { informationClass }, Identities, false); } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } } public async Task ExchangeCaseIdentitiesAsync(cF4sdApiSearchResultRelation searchResultRelation) { #if DEBUG var jsonRels = JsonConvert.SerializeObject(CaseRelations); if (lastCaseRelationsJson != null && lastCaseRelationsJson.Equals(jsonRels) is false) LogEntry($"CaseRelations changed: {jsonRels}", LogLevels.Debug); lastCaseRelationsJson = jsonRels; var jsonResult = JsonConvert.SerializeObject(searchResultRelation); var jsonIds = JsonConvert.SerializeObject(Identities); #endif var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var ComputerRemoved = false; var ComputerAdded = false; List updatedInformationClasses = new List(); foreach (var relationIdentity in searchResultRelation.Identities) { var existingIdentity = Identities.FirstOrDefault(identity => identity.Class == relationIdentity.Class); if (existingIdentity != null) { if (existingIdentity.Id.Equals(relationIdentity.Id) is false) { updatedInformationClasses.Add(existingIdentity.Class); ComputerRemoved |= relationIdentity.Class == enumFasdInformationClass.Computer; ComputerAdded |= relationIdentity.Class == enumFasdInformationClass.Computer; } relationIdentity.CopyTo(existingIdentity); } else { Identities.Add(relationIdentity.CreateCopy()); ComputerAdded |= relationIdentity.Class == enumFasdInformationClass.Computer; } } HealthCardDataHelper.RemoveTablesWithUpdatedIdentities(updatedInformationClasses); if (ComputerRemoved) await CurrentProvider?.DirectConnectionHelper?.DirectConnectionStopAsync(); var searchResultInfoClass = cF4sdIdentityEntry.GetFromSearchResult(searchResultRelation.Type); await SetViewDataAsync(new List() { searchResultInfoClass }, Identities, false); if (ComputerAdded) _ = Task.Run(async () => { try { await DirectConnectionHelper.DirectConnectionStartAsync(); } catch { } }); } catch (Exception E) { LogException(E); } finally { #if DEBUG jsonRels = JsonConvert.SerializeObject(CaseRelations); jsonResult = JsonConvert.SerializeObject(searchResultRelation); jsonIds = JsonConvert.SerializeObject(Identities); if (lastCaseRelationsJson != null && lastCaseRelationsJson.Equals(jsonRels) is false) LogEntry($"CaseRelations changed: {jsonRels}", LogLevels.Debug); lastCaseRelationsJson = jsonRels; #endif LogMethodEnd(CM); } } #region Case Notes public static event EventHandler CaseNotesChanged; public void SaveCaseNotes() => CaseNotesChanged?.Invoke(this, EventArgs.Empty); #endregion } }