using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Avalonia.Threading; namespace Logof_Client; public class CombineAddresses { private readonly ProgressWindow _progress; public CombineAddresses(ProgressWindow progressWindow) { _progress = progressWindow; } public enum CombineType { refsid, final_adress, none } public async Task<(KasAddressList, KasAddressList)> Perform(List address_lists, string type, CombineType comb_type, bool? exportUnused, bool? deleteOld) { try { var result = await Execute(address_lists,type,comb_type,exportUnused); if (deleteOld == true) { foreach (var list in address_lists) { Settings._instance.addressSets.addresses.Remove(list); } } return result; } catch (Exception ex) { Logger.Log($"Error while performing address combining: {ex.Message}", Logger.LogType.Error); } return (null,null); } private async Task<(KasAddressList, KasAddressList)> Execute(List address_lists, string type, CombineType comb_type, bool? exportUnused) { try { if (type == "difference") return await Difference(address_lists, comb_type, exportUnused); if (type == "union") return await Union(address_lists, comb_type, exportUnused); if (type == "intersection") return await Intersection(address_lists, comb_type, exportUnused); if (type == "symdiff") return await SymmetricDifference(address_lists, comb_type, exportUnused); return (null, null); } catch (Exception ex) { Logger.Log($"Error while executing address combining: {ex.Message}", Logger.LogType.Error); } return (null,null); } /// /// Returns true if the addresses are the same. /// /// First address to compare /// Second address to compare /// If true, only a refsid-check will be done /// public bool CompareAddresses(KasPerson first, KasPerson second, CombineType comb_type) { // A refsid of 0 means "missing", so it must not collapse unrelated entries. if(comb_type == CombineType.refsid) if (first.refsid != 0 && second.refsid != 0 && first.refsid == second.refsid) return true; if (comb_type == CombineType.final_adress) if (first.name == second.name && first.anrede == second.anrede && first.anredzus == second.anredzus && first.namezus == second.namezus && first.titel == second.titel && first.adel == second.adel && first.strasse == second.strasse && first.strasse2 == second.strasse2 && first.vorname == second.vorname && first.ort == second.ort && first.land == second.land && first.plz == second.plz && first.pplz == second.pplz && first.funktion == second.funktion && first.funktion2 == second.funktion2 && first.funktionad == second.funktionad && first.abteilung == second.abteilung && first.postfach == second.postfach && first.name1 == second.name1 && first.name2 == second.name2 && first.name3 == second.name3 && first.name4 == second.name4 && first.name5 == second.name5) return true; return false; } public async Task<(KasAddressList, KasAddressList)> Difference(List address_lists, CombineType comb_type, bool? return_unused, Progress? progress = null) { try { if (address_lists == null || address_lists.Count == 0) return (new KasAddressList(await KasAddressList.GenerateName("difference")), null); progress ??= new Progress { TotalPersons = address_lists.Sum(l => l.KasPersons.Count), ComparedPersons = 0 }; // Vereinigung aller Listen außer der ersten var restUnion = new List(); for (var i = 1; i < address_lists.Count; i++) restUnion.AddRange(address_lists[i].KasPersons); var result = new KasAddressList(await KasAddressList.GenerateName("difference")); var second_result = new KasAddressList("none"); if(return_unused == true) second_result = new KasAddressList(await KasAddressList.GenerateName("difference_rest", false)); foreach (var person in address_lists[0].KasPersons) { var isDouble = restUnion.Any(p => CompareAddresses(person, p, comb_type)); if (!isDouble) result.KasPersons.Add(person); else second_result.KasPersons.Add(person); progress.Increment(); if (progress.LogAction == null) continue; var logMessage = $"Person mit id {person.id} verglichen mit {restUnion.Count} Personen des Restes."; await Dispatcher.UIThread.InvokeAsync(() => progress.LogAction?.Invoke(logMessage)); } if (return_unused == true) return (result, second_result); return (result, null); } catch (Exception ex) { Logger.Log($"Error while performing difference-combining: {ex.Message}", Logger.LogType.Error); } return (null,null); } public async Task<(KasAddressList, KasAddressList)> Union(List address_lists, CombineType comb_type, bool? return_unused, Progress progress = null) { try { var result = new KasAddressList(await KasAddressList.GenerateName("union")); var second_result = new KasAddressList("none"); if(return_unused == true) second_result = new KasAddressList(await KasAddressList.GenerateName("union_rest", false)); if (address_lists == null || address_lists.Count == 0) return (result, null); var total = address_lists.Sum(l => l.KasPersons.Count); var processed = 0; foreach (var list in address_lists) foreach (var person in list.KasPersons) { if (!result.KasPersons.Any(existing => CompareAddresses(existing, person, comb_type))) result.KasPersons.Add(person); else second_result.KasPersons.Add(person); processed++; var percent = processed / (double)total * 100; var logMessage = $"{percent:F1}%: Person mit {person.id} hinzugefügt (aktuell {result.KasPersons.Count} Einträge)"; if (progress == null) continue; if (Dispatcher.UIThread.CheckAccess()) progress.LogAction?.Invoke(logMessage); else Dispatcher.UIThread.Post(() => progress.LogAction?.Invoke(logMessage)); } if (return_unused == true) return (result, second_result); return (result, null); } catch (Exception ex) { Logger.Log($"Error while performing union-combining: {ex.Message}", Logger.LogType.Error); } return (null,null); } public async Task MoveDuplicatesToNew() { return null; } public async Task<(KasAddressList, KasAddressList)> Intersection(List address_lists, CombineType comb_type, bool? return_unused, Progress progress = null) { try { var result = new KasAddressList(await KasAddressList.GenerateName("intersection")); var second_result = new KasAddressList("none"); if(return_unused == true) second_result = new KasAddressList(await KasAddressList.GenerateName("intersection_rest", false)); if (address_lists == null || address_lists.Count == 0) return (result, null); // Nur die erste Liste als Ausgangspunkt verwenden var baseList = address_lists[0]; var otherLists = address_lists.Skip(1).ToList(); var total = baseList.KasPersons.Count; var processed = 0; foreach (var person in baseList.KasPersons) { // Prüfen, ob Person in *allen* anderen Listen vorkommt var isInAll = otherLists.All(list => list.KasPersons.Any(existing => CompareAddresses(existing, person, comb_type))); if (isInAll) result.KasPersons.Add(person); else second_result.KasPersons.Add(person); processed++; var percent = processed / (double)total * 100; var logMessage = $"{percent:F1}%: Person mit {person.id} geprüft – {(isInAll ? "in allen enthalten" : "nicht überall vorhanden")}"; // Sicher und nicht blockierend loggen if (progress != null) { if (Dispatcher.UIThread.CheckAccess()) progress.LogAction?.Invoke(logMessage); else Dispatcher.UIThread.Post(() => progress.LogAction?.Invoke(logMessage)); } } if (return_unused == true) return (result, second_result); return (result, null); } catch (Exception ex) { Logger.Log($"Error while performing intersection-combining: {ex.Message}", Logger.LogType.Error); } return (null,null); } public async Task<(KasAddressList, KasAddressList)> SymmetricDifference(List address_lists, CombineType comb_type, bool? return_unused, Progress progress = null) { try { var result = new KasAddressList(await KasAddressList.GenerateName("symmetric_difference")); var second_result = new KasAddressList("none"); if(return_unused == true) second_result = new KasAddressList(await KasAddressList.GenerateName("symmetric_rest", false)); if (address_lists == null || address_lists.Count == 0) return (result, null); var total = address_lists.Sum(l => l.KasPersons.Count); var processed = 0; // Wir sammeln alle Personen + zählen, wie oft sie vorkommen var allPersons = new List<(KasPerson person, int count)>(); foreach (var list in address_lists) foreach (var person in list.KasPersons) { // Prüfen, ob schon vorhanden var existing = allPersons.FirstOrDefault(p => CompareAddresses(p.person, person, comb_type)); if (existing.person != null) { // Falls schon drin → Vorkommen erhöhen var index = allPersons.IndexOf(existing); allPersons[index] = (existing.person, existing.count + 1); } else { // Neu hinzufügen allPersons.Add((person, 1)); } processed++; var percent = processed / (double)total * 100; var logMessage = $"{percent:F1}%: Person mit {person.id} verarbeitet (Zwischengröße {allPersons.Count})"; if (progress != null) { if (Dispatcher.UIThread.CheckAccess()) progress.LogAction?.Invoke(logMessage); else Dispatcher.UIThread.Post(() => progress.LogAction?.Invoke(logMessage)); } } // Nur diejenigen übernehmen, die *genau einmal* vorkamen foreach (var (person, count) in allPersons) if (count == 1) result.KasPersons.Add(person); else second_result.KasPersons.Add(person); if (return_unused == true) return (result, second_result); return (result, null); } catch (Exception ex) { Logger.Log($"Error while performing symdiff-combining: {ex.Message}", Logger.LogType.Error); } return (null,null); } } public class Progress { public int TotalPersons { get; set; } // Gesamtzahl der zu prüfenden Personen public int ComparedPersons { get; set; } // Schon verglichene Personen public double Percentage => TotalPersons == 0 ? 0 : (double)ComparedPersons / TotalPersons * 100; public Action? LogAction { get; set; } // z.B. Dispatcher-UI-Callback public void Increment() { var comparedPersons = ComparedPersons; Interlocked.Increment(ref comparedPersons); } }