using C4IT.FASD.Base; using C4IT.FASD.Cockpit.Communication; using FasdCockpitCommunication.RemoteDesktopCommunication; using FasdDesktopUi.Basics.CustomEvents; using FasdDesktopUi.Basics.Enums; using FasdDesktopUi.Basics.Models; using FasdDesktopUi.Basics.Services.Models; using FasdDesktopUi.Basics.Services.SupportCase.Processors; using FasdDesktopUi.Basics.UiActions; using FasdDesktopUi.Pages.DetailsPage.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using static C4IT.Logging.cLogManager; namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers { /// /// Used to manage the for the UI via the /// public class SupportCaseController { private ISupportCaseProcessor _supportCaseProcessor; private cF4sdApiSearchResultRelation _focusedRelation; private readonly Dictionary _selectedRelations = new Dictionary(); private cHealthCard _selectedHealthcard = null; private bool _hasDirectionConnection = false; public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get => _supportCaseProcessor?.SupportCaseDataProviderArtifact; } internal void SetSupportCaseProcessor(ISupportCaseProcessor supportCaseProcessor, IEnumerable preselectedIdentities) { IEnumerable preselectedRelations = GetPreselectedRelations(supportCaseProcessor.GetCaseRelations(), preselectedIdentities); ResetSelectedRelations(preselectedRelations); _hasDirectionConnection = false; if (_supportCaseProcessor != null) { _supportCaseProcessor.AvailableCaseRelationsAdded -= HandleAvailableCaseRelationsAdded; _supportCaseProcessor.CaseDataChanged -= HandleCaseDataChanged; _supportCaseProcessor.SupportCaseDataProviderArtifact.DirectConnectionHelper.DirectConnectionChanged -= HandleDirectConnectionChanged; } _supportCaseProcessor = supportCaseProcessor; _supportCaseProcessor.AvailableCaseRelationsAdded += HandleAvailableCaseRelationsAdded; _supportCaseProcessor.CaseDataChanged += HandleCaseDataChanged; _supportCaseProcessor.SupportCaseDataProviderArtifact.DirectConnectionHelper.DirectConnectionChanged += HandleDirectConnectionChanged; } private IEnumerable GetPreselectedRelations (ILookup loadedRelations, IEnumerable preselectedIdentities) { List preselectedRelations = new List(); foreach (var relationType in loadedRelations) { foreach (var relation in relationType) { if (relation.Identities.All(ri => preselectedIdentities.Any(i => i.Id == ri.Id))) preselectedRelations.Add(relation); } } return preselectedRelations; } private void ResetSelectedRelations(IEnumerable preselectedRelations) { try { _selectedRelations.Clear(); if (preselectedRelations is null) return; foreach (var relation in preselectedRelations) { _selectedRelations[cF4sdIdentityEntry.GetFromSearchResult(relation.Type)] = relation; } } catch (Exception ex) { LogException(ex); } } private void HandleAvailableCaseRelationsAdded(object sender, RelationEventArgs e) => AvailableCaseRelationsAdded?.Invoke(this, e); private void HandleCaseDataChanged(object sender, SupportCaseDataEventArgs e) { CaseDataChanged?.Invoke(this, e); if (e.DataTables.Any(t => !t.Name.StartsWith("Computation_"))) _supportCaseProcessor.ProcessComputations(e.Relation, _supportCaseProcessor.GetHealthcardFor(e.Relation).Prerequisites.Computations.Values); _ = Task.Run(async () => { await UpdateStatusOfSelectedRelations(); if (!_hasDirectionConnection) await SupportCaseDataProviderArtifact.DirectConnectionHelper.DirectConnectionStartAsync(); }); } private void HandleDirectConnectionChanged(object sender, EventArgs e) { _hasDirectionConnection = SupportCaseDataProviderArtifact.DirectConnectionHelper.IsDirectConnectionActive; } /// /// Updates the currently for a support case relevant and shown relation. /// /// Raises internal void UpdateFocusedCaseRelation(cF4sdApiSearchResultRelation relation) { try { if (_focusedRelation == relation) return; _selectedHealthcard = _supportCaseProcessor.GetHealthcardFor(relation); HashSet requiredTables = cHealthCard.GetRequiredTables(_selectedHealthcard); _ = Task.Run(async () => await _supportCaseProcessor.LoadSupportCaseDataAsync(relation, requiredTables)); List requiredInformationClasses = relation.Identities .Where(i => i.Class != enumFasdInformationClass.User) .Select(i => i.Class) .ToList(); if (relation.Type == enumF4sdSearchResultClass.User) requiredInformationClasses = new List() { enumFasdInformationClass.User }; if (requiredInformationClasses.Count == 0) requiredInformationClasses.Add(enumFasdInformationClass.User); SupportCaseDataProviderArtifact.HealthCardDataHelper.TrySetSelectedHealthcard(requiredInformationClasses); _focusedRelation = relation; if (!_selectedRelations.Values.Contains(relation) || relation.Type == enumF4sdSearchResultClass.Computer) { _hasDirectionConnection = false; SupportCaseDataProviderArtifact.DirectConnectionHelper.Reset(); } _selectedRelations[cF4sdIdentityEntry.GetFromSearchResult(relation.Type)] = relation; _ = Task.Run(async () => await UpdateStatusOfSelectedRelations()); var focusedRelations = new cF4sdApiSearchResultRelation[1] { _focusedRelation }; FocusedRelationsChanged?.Invoke(this, new RelationEventArgs() { Relations = focusedRelations.ToLookup(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type), r => r) }); } catch (Exception ex) { LogException(ex); } } private async Task UpdateStatusOfSelectedRelations() { const string StatusString = "Status"; try { int? agentUserId = null; int? agentDeviceId = null; // todo these values should not be recieved from the NamedParameters, instead from the relation it self if (SupportCaseDataProviderArtifact.NamedParameterEntries.TryGetValue("AgentUserId", out var userNamedParameter)) { var value = userNamedParameter.GetValue(); if (!string.IsNullOrWhiteSpace(value)) agentUserId = int.Parse(value); } // todo these values should not be recieved from the NamedParameters, instead from the relation it self if (SupportCaseDataProviderArtifact.NamedParameterEntries.TryGetValue("AgentDeviceId", out var deviceNamedParameter)) { var value = deviceNamedParameter.GetValue(); if (!string.IsNullOrWhiteSpace(value)) agentDeviceId = int.Parse(deviceNamedParameter.GetValue()); } foreach (var relationEntry in _selectedRelations) { if (relationEntry.Value is null) continue; string statusValue = "Unknown"; switch (relationEntry.Key) { case enumFasdInformationClass.Computer: if (!agentDeviceId.HasValue) continue; bool isDeviceOnline = await cFasdCockpitCommunicationBase.Instance.GetAgentOnlineStatus(agentDeviceId.Value); statusValue = isDeviceOnline ? "Online" : "Offline"; break; case enumFasdInformationClass.User: if (!agentDeviceId.HasValue || !agentUserId.HasValue) continue; bool isUserOnline = await cFasdCockpitCommunicationBase.Instance.GetAgentOnlineStatus(agentDeviceId.Value, agentUserId.Value); statusValue = isUserOnline ? "Online" : "Offline"; break; case enumFasdInformationClass.Ticket: if (relationEntry.Value.Infos != null && relationEntry.Value.Infos.TryGetValue("StatusId", out var statusIdValue)) if (Enum.TryParse(statusIdValue, out var ticketStatus)) statusValue = ticketStatus.ToString(); break; case enumFasdInformationClass.VirtualSession: case enumFasdInformationClass.MobileDevice: default: continue; } if (relationEntry.Value.Infos is null) relationEntry.Value.Infos = new Dictionary(); relationEntry.Value.Infos[StatusString] = statusValue; } IEnumerable newHeadingData = GetHeadingData(); HeadingDataChanged?.Invoke(this, new HeadingDataEventArgs(newHeadingData)); } catch (Exception ex) { LogException(ex); } } public async Task RefreshDataForCurrentlyFocusedRelationAsync() { SupportCaseDataProviderArtifact.HealthCardDataHelper.LoadingHelper.LastDataRequest = DateTime.Now; await _supportCaseProcessor.UpdateLatestCaseDataFor(_focusedRelation); } public cCopyTemplate GetCopyTemplate() { string defaultCopyTemplate = _selectedHealthcard?.DefaultCopyTemplate ?? cF4SDCockpitXmlConfig.Instance?.CopyTemplateConfig?.CopyTemplates.DefaultTemplate.Name ?? string.Empty; Dictionary copyTemplates = cF4SDCockpitXmlConfig.Instance?.CopyTemplateConfig?.CopyTemplates?.CopyTemplates; if (copyTemplates?.TryGetValue(defaultCopyTemplate, out cCopyTemplate _defaultCopyTemplate) ?? false) return _defaultCopyTemplate; else return null; } public List> GetWidgetsData() { try { List> widgetsData = new List>(); cHealthCard currentHealthCard = _supportCaseProcessor.GetHealthcardFor(_focusedRelation); IEnumerable widgetsDefinition = currentHealthCard.CategoriesStatic.StateCategories; foreach (var widgetDefinition in widgetsDefinition) { List widgetData = new List(); foreach (var widgetValueDefinition in widgetDefinition.States) { CockpitValueDisplayData displayData = _supportCaseProcessor.GetCockpitValueDisplayData(widgetValueDefinition, _focusedRelation, true); cUiActionBase uiAction = _supportCaseProcessor.GetUiAction(widgetValueDefinition); var widgetValue = new cWidgetValueModel(displayData, GetHighlightColor(displayData?.Levels?.FirstOrDefault() ?? enumHealthCardStateLevel.None), uiAction); if (widgetValue?.UiActionValue != null && ShouldHideUiActionValue(widgetValue.Value)) widgetValue.UiActionValue.DisplayType = enumActionDisplayType.hidden; widgetData.Add(widgetValue); } widgetsData.Add(widgetData); } return widgetsData; } catch (Exception ex) { LogException(ex); return null; } bool ShouldHideUiActionValue(string widgetValue) => string.IsNullOrWhiteSpace(widgetValue) || widgetValue == "0"; } public cDetailsPageDataHistoryDataModel GetHistoryData() => ((SupportCaseProcessor)_supportCaseProcessor).GetHistoryData(_focusedRelation); public List GetContainerData() => ((SupportCaseProcessor)_supportCaseProcessor).GetContainerData(_focusedRelation); public IEnumerable GetMenuData() { var menuBarDatas = cF4SDCockpitXmlConfig.Instance.MenuItems.ToDictionary(config => config.Key, config => (Config: config.Value, MenuValue: MenuDataFactory.Create(config.Value, _supportCaseProcessor.SupportCaseDataProviderArtifact.NamedParameterEntries, _selectedRelations.Values.Select(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type)).ToList()))); try { List menuBarDatasAddedToSection = new List(); foreach (var menuBarData in menuBarDatas.Values) { bool isPinned = cF4SDCockpitXmlConfig.Instance.HealthCardConfig.QuickActionsPinned.Contains(menuBarData.Config.Name); menuBarData.MenuValue.IconPositionIndex = isPinned ? cF4SDCockpitXmlConfig.Instance.HealthCardConfig.QuickActionsPinned.IndexOf(menuBarData.Config.Name) : -1; // Same Logic in GetFilteredMenuData() bool hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, menuBarData.Config.Section, menuBarData.MenuValue); foreach (var section in menuBarData.Config.Sections) { // todo add test for adding to multiple sections hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, section, menuBarData.MenuValue) || hasBeenAddedToSection; // order is relevent, because data has to be added to section } if (hasBeenAddedToSection) menuBarDatasAddedToSection.Add(menuBarData.Config.Name); } foreach (var menuBarData in menuBarDatasAddedToSection) { if (cF4SDCockpitXmlConfig.Instance.HealthCardConfig.QuickActionsPinned.Contains(menuBarData)) continue; menuBarDatas.Remove(menuBarData); } return menuBarDatas.Values.Select(data => data.MenuValue); } catch (Exception ex) { LogException(ex); return new List(); } } public IEnumerable GetFilteredMenuData(MenuDataFilter filterQuery) { Dictionary menuBarDatas = cF4SDCockpitXmlConfig.Instance.MenuItems.ToDictionary(config => config.Key, config => (Config: config.Value, MenuValue: MenuDataFactory.Create(config.Value, _supportCaseProcessor.SupportCaseDataProviderArtifact.NamedParameterEntries, _selectedRelations.Values.Select(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type)).ToList()))); try { foreach (var menuBarData in menuBarDatas.Values) { // Same Logic in GetMenuData() bool hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, menuBarData.Config.Section, menuBarData.MenuValue); foreach (var section in menuBarData.Config.Sections) { // todo add test for adding to multiple sections hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, section, menuBarData.MenuValue) || hasBeenAddedToSection; // order is relevent, because data has to be added to section } } return menuBarDatas.Values.Select(data => data.MenuValue) .Where(md => md.MenuText.ToLower().Contains(filterQuery?.SearchString?.ToLower())); } catch (Exception ex) { LogException(ex); return new List(); } } private bool TryAddMenuDataToSection(ref Dictionary menuBarDatas, string section, cMenuDataBase menuData) { if (string.IsNullOrEmpty(section)) return false; if (!menuBarDatas.TryGetValue(section, out var sectionMenuBar)) return false; if (!(sectionMenuBar.MenuValue is cMenuDataContainer containerData)) return false; if (containerData.UiAction is cSubMenuAction subMenuAction) { if (!subMenuAction.SubMenuData.Any(item => item.MenuText == menuData.MenuText)) // todo SubMenuData maybe better as Dictionary subMenuAction.SubMenuData.Add(menuData); if (!containerData.SubMenuData.Any(item => item.MenuText == menuData.MenuText)) containerData.SubMenuData.Add(menuData); } return true; } internal IEnumerable GetHeadingData() => SupportCaseHeadingController.GetHeadingData(_selectedRelations); private static enumHighlightColor GetHighlightColor(enumHealthCardStateLevel level) { switch (level) { case enumHealthCardStateLevel.None: return enumHighlightColor.none; case enumHealthCardStateLevel.Ok: return enumHighlightColor.green; case enumHealthCardStateLevel.Warning: return enumHighlightColor.orange; case enumHealthCardStateLevel.Error: return enumHighlightColor.red; case enumHealthCardStateLevel.Info: return enumHighlightColor.blue; default: return enumHighlightColor.none; } } internal IEnumerable GetRelationsOf(enumFasdInformationClass informationClass) { return _supportCaseProcessor.GetCaseRelations().FirstOrDefault(r => r.Key == informationClass); } internal AgentRemoteClientInfo GetAgentClientInfo() { if (_focusedRelation.Type != enumF4sdSearchResultClass.Computer) return null; int deviceId = 0; int userId = 0; bool hasAllRequiredNamedParameters = _supportCaseProcessor.TryGetNamedParameterValue(_focusedRelation, SupportCaseProcessor.AgentOrganisationCodeNamedParameterName, out int organisationId) && _supportCaseProcessor.TryGetNamedParameterValue(_focusedRelation, SupportCaseProcessor.AgentUserIdNamedParameterName, out userId) && _supportCaseProcessor.TryGetNamedParameterValue(_focusedRelation, SupportCaseProcessor.AgentDeviceIdNamedParameterName, out deviceId); if (!hasAllRequiredNamedParameters) return null; return new AgentRemoteClientInfo() { DeviceCode = deviceId, AccountCode = userId, OrganisationCode = organisationId }; } /// /// Raised when the currently for a support case relevant and shown relations have been updated. /// public event EventHandler FocusedRelationsChanged; /// /// Raised when newly available relations for a support case were added. /// public event EventHandler AvailableCaseRelationsAdded; /// /// Raised when the data set of a support case has changed. /// public event EventHandler CaseDataChanged; public event EventHandler HeadingDataChanged; } public class MenuDataFilter { public string SearchString { get; set; } public MenuDataFilter(string searchString = null) { SearchString = searchString; } } }