using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using LightlessSync.API.Dto.Group; using LightlessSync.PlayerData.Factories; using LightlessSync.PlayerData.Pairs; using LightlessSync.Services.Mediator; using LightlessSync.UI.Models; using Microsoft.Extensions.Logging; namespace LightlessSync.UI.Services; public sealed class PairUiService : DisposableMediatorSubscriberBase { private readonly PairLedger _pairLedger; private readonly PairFactory _pairFactory; private readonly PairManager _pairManager; private readonly object _snapshotGate = new(); private PairUiSnapshot _snapshot = PairUiSnapshot.Empty; private Pair? _lastAddedPair; private bool _needsRefresh = true; public PairUiService( ILogger logger, LightlessMediator mediator, PairLedger pairLedger, PairFactory pairFactory, PairManager pairManager) : base(logger, mediator) { _pairLedger = pairLedger; _pairFactory = pairFactory; _pairManager = pairManager; Mediator.Subscribe(this, _ => MarkDirty()); Mediator.Subscribe(this, _ => MarkDirty()); Mediator.Subscribe(this, _ => MarkDirty()); EnsureSnapshot(); } public PairUiSnapshot GetSnapshot() { EnsureSnapshot(); lock (_snapshotGate) { return _snapshot; } } public Pair? GetLastAddedPair() { EnsureSnapshot(); lock (_snapshotGate) { return _lastAddedPair; } } public void ClearLastAddedPair() { _pairManager.ClearLastAddedUser(); lock (_snapshotGate) { _lastAddedPair = null; } } private void MarkDirty() { lock (_snapshotGate) { _needsRefresh = true; } } private void EnsureSnapshot() { bool shouldBuild; lock (_snapshotGate) { shouldBuild = _needsRefresh; if (shouldBuild) { _needsRefresh = false; } } if (!shouldBuild) { return; } PairUiSnapshot snapshot; Pair? lastAddedPair; try { (snapshot, lastAddedPair) = BuildSnapshot(); } catch { lock (_snapshotGate) { _needsRefresh = true; } throw; } lock (_snapshotGate) { _snapshot = snapshot; _lastAddedPair = lastAddedPair; } Mediator.Publish(new PairUiUpdatedMessage(snapshot)); } private (PairUiSnapshot Snapshot, Pair? LastAddedPair) BuildSnapshot() { var entries = _pairLedger.GetAllEntries(); var pairByUid = new Dictionary(StringComparer.Ordinal); var directPairsList = new List(); var groupPairsTemp = new Dictionary>(); var pairsWithGroupsTemp = new Dictionary>(); foreach (var entry in entries) { var pair = _pairFactory.Create(entry); if (pair is null) { continue; } pairByUid[entry.Ident.UserId] = pair; if (entry.IsDirectlyPaired) { directPairsList.Add(pair); } var uniqueGroups = new HashSet(StringComparer.Ordinal); var groupList = new List(); foreach (var group in entry.Groups) { if (!uniqueGroups.Add(group.Group.GID)) { continue; } if (!groupPairsTemp.TryGetValue(group, out var members)) { members = new List(); groupPairsTemp[group] = members; } members.Add(pair); groupList.Add(group); } pairsWithGroupsTemp[pair] = groupList; } var allGroupsList = _pairLedger.GetAllSyncshells() .Values .Select(s => s.GroupFullInfo) .ToList(); foreach (var group in allGroupsList) { if (!groupPairsTemp.ContainsKey(group)) { groupPairsTemp[group] = new List(); } } var directPairs = new ReadOnlyCollection(directPairsList); var groupPairsFinal = new Dictionary>(); foreach (var (group, members) in groupPairsTemp) { groupPairsFinal[group] = new ReadOnlyCollection(members); } var pairsWithGroupsFinal = new Dictionary>(); foreach (var (pair, groups) in pairsWithGroupsTemp) { pairsWithGroupsFinal[pair] = new ReadOnlyCollection(groups); } var groupsReadOnly = new ReadOnlyCollection(allGroupsList); var pairsByUidReadOnly = new ReadOnlyDictionary(pairByUid); var groupsByGidReadOnly = new ReadOnlyDictionary(allGroupsList.ToDictionary(g => g.Group.GID, g => g, StringComparer.Ordinal)); Pair? lastAddedPair = null; var lastAdded = _pairManager.GetLastAddedUser(); if (lastAdded is not null) { if (!pairByUid.TryGetValue(lastAdded.User.UID, out lastAddedPair)) { var groups = lastAdded.Groups.Keys .Select(gid => { var result = _pairManager.GetGroup(gid); return result.Success ? result.Value.GroupFullInfo : null; }) .Where(g => g is not null) .Cast() .ToList(); var entry = new PairDisplayEntry(new PairUniqueIdentifier(lastAdded.User.UID), lastAdded, groups, null); lastAddedPair = _pairFactory.Create(entry); } } var snapshot = new PairUiSnapshot( pairsByUidReadOnly, directPairs, new ReadOnlyDictionary>(groupPairsFinal), new ReadOnlyDictionary>(pairsWithGroupsFinal), groupsByGidReadOnly, groupsReadOnly); return (snapshot, lastAddedPair); } }