using C4IT.FASD.Base; using C4IT.FASD.Cockpit.Communication; using FasdDesktopUi.Basics.CustomEvents; using FasdDesktopUi.Basics.Services.RelationService; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using static C4IT.Logging.cLogManager; namespace FasdDesktopUi.Basics.Services.SupportCase { public class SupportCase : ISupportCase { private readonly Dictionary> _caseRelations = new Dictionary>(); private readonly Dictionary> _supportCaseDataCache = new Dictionary>(); private readonly Dictionary _rawDataCache = new Dictionary(); // todo remove, currently only used for SupportCaseDataProviderArtifact internal readonly Guid Id; private readonly IRelationService _relationService; public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get; } internal SupportCase(Guid id, IRelationService relationService, cSupportCaseDataProvider supportCaseDataProvider) { Id = id; _relationService = relationService; SupportCaseDataProviderArtifact = supportCaseDataProvider; AddCaseRelations(_relationService?.GetLoadedRelations()); } ~SupportCase() { if (_relationService != null) _relationService.RelationsFound -= HandleRelationsFound; } public void Initialize() { if (_relationService != null) _relationService.RelationsFound += HandleRelationsFound; } public ILookup GetCaseRelations() { try { IEnumerable<(enumFasdInformationClass InformationClass, cF4sdApiSearchResultRelation Relation)> flatList = _caseRelations.SelectMany(i => i.Value.Select(v => (i.Key, v))); return flatList.ToLookup(v => v.InformationClass, v => v.Relation); } catch (Exception ex) { LogException(ex); } return null; } public void AddCaseRelations(ILookup relations) { try { if (relations is null) return; foreach (var relationType in relations) { if (_caseRelations.TryGetValue(relationType.Key, out var caseRelation)) caseRelation = caseRelation.Union(relationType).ToList(); else _caseRelations.Add(relationType.Key, relationType.ToList()); if (SupportCaseDataProviderArtifact?.CaseRelations?.TryGetValue(relationType.Key, out var caseRelations) ?? false) caseRelations = caseRelations.Union(relationType).ToList(); else SupportCaseDataProviderArtifact?.CaseRelations?.Add(relationType.Key, relationType.ToList()); } CaseRelationsAdded?.Invoke(this, new RelationEventArgs() { Relations = relations }); } catch (Exception ex) { LogException(ex); } } public async Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable tablesToLoad) { try { cF4SDHealthCardRawData rawData = null; // todo this is only a temporary fix. Currently the tablesToLoad contain also detail tables // and tables won't be loaded e.g. the QuickActionHistory bool isDataComplete = IsRawDataCacheComplete() && IsSupportCaseDataCacheComplete(); if (isDataComplete) { await SupportCaseDataProviderArtifact.HealthCardDataHelper.LoadingHelper.SetHealthCardRawData(_rawDataCache[relation], relation.Identities); return; } var rawDataRequest = new cF4sdHealthCardRawDataRequest() { Identities = relation.Identities, Tables = tablesToLoad.ToList(), MaxAge = cF4SDCockpitXmlConfig.Instance?.HealthCardConfig?.SearchResultAge ?? 14 }; while (!isDataComplete) { if (rawData is null) { rawData = await cFasdCockpitCommunicationBase.Instance.GetHealthCardData(rawDataRequest); _rawDataCache[relation] = rawData; await SupportCaseDataProviderArtifact.HealthCardDataHelper.LoadingHelper.SetHealthCardRawData(rawData, rawDataRequest.Identities); } else { rawData = await cFasdCockpitCommunicationBase.Instance.GetHealthCardData(rawData.Id); _rawDataCache[relation] = _rawDataCache[relation].Combine(rawData); await SupportCaseDataProviderArtifact.HealthCardDataHelper.LoadingHelper.UpdateHealthcardRawData(rawData); } UpdateSupportCaseDataCache(relation, rawData?.Tables?.Values); isDataComplete = rawData?.Tables? .Where(table => table.Key.StartsWith("Computation_") == false) .All(table => !table.Value.IsIncomplete && !table.Value.Columns.Values.Any(c => c.IsIncomplete)) ?? false; } } catch (Exception ex) { LogException(ex); } bool IsRawDataCacheComplete() { return tablesToLoad.All(t => _rawDataCache.TryGetValue(relation, out var cachedRawData) && (cachedRawData?.Tables?.TryGetValue(t, out var cachedTable) ?? false) && cachedTable != null && !cachedTable.IsIncomplete && !cachedTable.Columns.Values.Any(c => c.IsIncomplete) ); } bool IsSupportCaseDataCacheComplete() { return tablesToLoad.Any(t => _supportCaseDataCache.TryGetValue(t, out var cachedTables) && cachedTables.TryGetValue(relation, out var table) && !table.IsIncomplete && !table.Columns.Values.Any(c => c.IsIncomplete) ); } } public void UpdateSupportCaseDataCache(cF4sdApiSearchResultRelation relation, IEnumerable tables) { try { if (tables is null) return; List dataTables = tables?.ToList(); foreach (var table in dataTables) { if (string.IsNullOrEmpty(table.Name)) continue; if (!_supportCaseDataCache.ContainsKey(table.Name)) _supportCaseDataCache.Add(table.Name, new Dictionary()); if (!_supportCaseDataCache[table.Name].ContainsKey(relation)) _supportCaseDataCache[table.Name][relation] = table; else if (_supportCaseDataCache[table.Name][relation].IsIncomplete) MergeTable(_supportCaseDataCache[table.Name][relation], table); } SupportCaseDataCacheHasChanged?.Invoke(this, new SupportCaseDataEventArgs() { Relation = relation, DataTables = dataTables }); } catch (Exception ex) { LogException(ex); } void MergeTable(cF4SDHealthCardRawData.cHealthCardTable existingTable, cF4SDHealthCardRawData.cHealthCardTable newTable) { try { existingTable.IsIncomplete = newTable.IsIncomplete; foreach (var newColumn in newTable.Columns) { existingTable.Columns[newColumn.Key] = newColumn.Value; } } catch (Exception ex) { LogException(ex); } } } public void InvalidateCaseDataCacheFor(cF4sdApiSearchResultRelation relation) { try { _rawDataCache.Remove(relation); foreach (var tableCache in _supportCaseDataCache.Values) { tableCache.Remove(relation); } // todo: invoke SupportCaseDataChache with empty tables // SupportCaseDataCacheHasChanged?.Invoke(this, new SupportCaseDataEventArgs() { Relation = relation }); } catch (Exception ex) { LogException(ex); } } public void InvalidateLatestCaseDataCacheFor(cF4sdApiSearchResultRelation relation, out ICollection invalidatedTables) { invalidatedTables = new HashSet(); try { foreach (var tableCache in _supportCaseDataCache.Values) { if (!tableCache.TryGetValue(relation, out cF4SDHealthCardRawData.cHealthCardTable table)) continue; table.IsIncomplete = true; foreach (var column in table.Columns.Values) { if (column.Values != null && column.Values.Count > 0) column.Values[0] = null; column.IsIncomplete = true; } invalidatedTables.Add(table); } if (!_rawDataCache.TryGetValue(relation, out var cachedRawData) || cachedRawData?.Tables is null) return; foreach (var table in cachedRawData.Tables.Values) { table.IsIncomplete = true; foreach (var column in table.Columns.Values) { if (column.Values != null && column.Values.Count > 0) column.Values[0] = null; column.IsIncomplete = true; } } } catch (Exception ex) { LogException(ex); } } public IEnumerable GetSupportCaseHealthcardData(cF4sdApiSearchResultRelation relation, cValueAddress valueAddress) { try { if (!_supportCaseDataCache.TryGetValue(valueAddress.ValueTable, out var tables)) return null; if (!tables.TryGetValue(relation, out var table)) return null; if (!table.Columns.TryGetValue(valueAddress.ValueColumn, out var column)) return null; return column.Values; } catch (Exception ex) { LogException(ex); } return null; } private void HandleRelationsFound(object sender, StagedSearchResultRelationsEventArgs e) { AddCaseRelations(e.StagedResultRelations.Relations); if (e.StagedResultRelations.IsComplete) _relationService.RelationsFound -= HandleRelationsFound; } private void AddCaseRelations(IEnumerable relations) => AddCaseRelations(relations?.ToLookup(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type), r => r)); public event EventHandler CaseRelationsAdded; public event EventHandler SupportCaseDataCacheHasChanged; } }