using System.Collections.ObjectModel; using System.Globalization; using View_by_Distance.Metadata.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless.Methods; using WindowsShortcutFactory; namespace View_by_Distance.People.Models.Stateless.Methods; internal static class People { private record RecordA(List Changes, List PersonContainers); private record RecordB(string Directory, string DirectoryName, string DirectoryNameSplitFirst); internal static List GetPersonBirthdays(string personBirthdayFormat, string[] personKeyDirectories, string personDisplayDirectoryName) { List results = []; string personKeyFormatted; PersonBirthday? personBirthday; PersonKeyFormattedAndPersonBirthday personKeyFormattedAndPersonBirthday; foreach (string personKeyDirectory in personKeyDirectories) { personKeyFormatted = Path.GetFileName(personKeyDirectory); if (DateTime.TryParseExact(personKeyFormatted, "MM.dd.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime birthday)) continue; personBirthday = IPersonBirthday.GetPersonBirthday(personBirthdayFormat, personKeyFormatted); if (personBirthday is null) continue; if (!IPersonBirthday.IsCounterPersonBirthday(personBirthday) && ((!personKeyDirectory.Contains('#') && (personDisplayDirectoryName.Contains('~') || personDisplayDirectoryName.Contains('#'))) || (personKeyDirectory.Contains('#') && !personDisplayDirectoryName.Contains('#')))) throw new NotSupportedException(); personKeyFormattedAndPersonBirthday = new(personKeyFormatted, personBirthday); results.Add(personKeyFormattedAndPersonBirthday); } return results; } private static List GetPersonContainersCollections(ResultSettings resultSettings, MetadataSettings metadataSettings, PersonDirectory personDirectory, char numberSign, string personDisplayDirectory, string personDisplayDirectoryName, int? approximateYears, List personKeyFormattedAndPersonBirthdayCollection) { List results = []; long personKey; string[] files; FilePath filePath; const int zero = 0; string personKeyDirectory; FileHolder fileHolder; PersonContainer personContainer; PersonBirthday[] orderedPersonBirthdays; List personDisplayDirectoryAllFilePaths = GetFilePaths(resultSettings, metadataSettings, personDisplayDirectory); foreach (PersonKeyFormattedAndPersonBirthday personKeyFormattedAndPersonBirthday in personKeyFormattedAndPersonBirthdayCollection) { orderedPersonBirthdays = (from l in personKeyFormattedAndPersonBirthdayCollection where !l.PersonKeyFormatted.Contains(numberSign) orderby l.PersonBirthday.Value.Ticks descending select l.PersonBirthday).ToArray(); if (orderedPersonBirthdays.Length == 0) personKey = personKeyFormattedAndPersonBirthdayCollection[zero].PersonBirthday.Value.Ticks; else { if (personKeyFormattedAndPersonBirthday.PersonKeyFormatted.Contains(numberSign)) continue; personKey = orderedPersonBirthdays[zero].Value.Ticks; } personKeyDirectory = Path.Combine(personDisplayDirectory, personKeyFormattedAndPersonBirthday.PersonKeyFormatted); files = Directory.GetFiles(personKeyDirectory, "*", SearchOption.AllDirectories); if (files.Length == 0) continue; foreach (string file in files) { if (!file.EndsWith(".rel")) continue; fileHolder = FileHolder.Get(file); filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null); personDisplayDirectoryAllFilePaths.Add(filePath); } personContainer = new(approximateYears, orderedPersonBirthdays, new(personDisplayDirectoryAllFilePaths), personDisplayDirectoryName, personKey, personDirectory); results.Add(personContainer); } return results; } private static List GetFilePaths(ResultSettings resultSettings, MetadataSettings metadataSettings, string personDisplayDirectory) { List results = []; string[] files; string checkFile; FilePath filePath; string directoryName; FileHolder fileHolder; List distinct = []; string fileNameWithoutExtension; string personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory); files = Directory.GetFiles(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) { fileHolder = FileHolder.Get(file); filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null); results.Add(filePath); } string[] directories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { directoryName = Path.GetFileName(directory); files = Directory.GetFiles(directory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) { fileHolder = FileHolder.Get(file); filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null); if (filePath.ExtensionLowered is not ".json" and not ".pged") { results.Add(filePath); continue; } fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); if (string.IsNullOrEmpty(fileNameWithoutExtension) || string.IsNullOrEmpty(personDisplayDirectoryName)) continue; else if (fileNameWithoutExtension.Length == 1 && fileNameWithoutExtension[0] == personDisplayDirectoryName[0]) { if (distinct.Contains(file)) throw new NotSupportedException($"Move / Delete <{file}>"); distinct.Add(file); } else if (fileNameWithoutExtension != directoryName) { checkFile = Path.Combine(directory, $"{fileNameWithoutExtension}{filePath.ExtensionLowered}"); if (!File.Exists(checkFile)) { File.Move(file, checkFile); fileHolder = FileHolder.Get(checkFile); filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null); results.Add(filePath); } else { checkFile = Path.Combine(directory, $"{fileNameWithoutExtension}.txt"); if (File.Exists(checkFile)) File.Delete(checkFile); File.Move(file, checkFile); } continue; } results.Add(filePath); } } return results; } private static List GetFilePaths(ResultSettings resultSettings, MetadataSettings metadataSettings, ICompareSettings compareSettings, string personDisplayDirectory) { List results; string checkFile; FilePath filePath; FileHolder fileHolder; int? wholePercentages; string? checkDirectory; string[] files = Directory.GetFiles(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) { if (file.EndsWith(".lnk")) continue; fileHolder = FileHolder.Get(file); filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null); wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath); if (filePath.Id is not null && wholePercentages is not null) continue; checkDirectory = Path.GetDirectoryName(file); if (string.IsNullOrEmpty(checkDirectory)) continue; checkDirectory = Path.Combine(checkDirectory, "_ Invalid"); if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); checkFile = Path.Combine(checkDirectory, Path.GetFileName(file)); if (File.Exists(checkFile)) File.Delete(file); else File.Move(file, checkFile); } results = GetFilePaths(resultSettings, metadataSettings, personDisplayDirectory); return results; } private static RecordA GetPersonContainersGroup(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, PersonDirectory personDirectory, string[] personDisplayDirectories) { RecordA result; string? minusOne; char numberSign = '#'; int? approximateYears; List changes = []; string[] personKeyDirectories; PersonContainer personContainer; List results = []; string? personDisplayDirectoryName; List personContainers; List personDisplayDirectoryAllFilePaths; char[] personCharacters = peopleSettings.PersonCharacters.ToArray(); List personKeyFormattedAndPersonBirthdayCollection; foreach (string personDisplayDirectory in personDisplayDirectories) { personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory); if (string.IsNullOrEmpty(personDisplayDirectoryName)) continue; approximateYears = IAge.GetApproximateYears(personCharacters, personDisplayDirectoryName); personKeyDirectories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly); personKeyFormattedAndPersonBirthdayCollection = GetPersonBirthdays(peopleSettings.PersonBirthdayFormat, personKeyDirectories, personDisplayDirectoryName); if (personDisplayDirectoryName.Contains('^')) { minusOne = Path.GetDirectoryName(personDisplayDirectory); if (minusOne is null) continue; changes.Add(VerifyAge(numberSign, personDisplayDirectory, minusOne, personDisplayDirectoryName, approximateYears, personKeyFormattedAndPersonBirthdayCollection)); } if (changes.Any(l => l is not null)) continue; if (personKeyFormattedAndPersonBirthdayCollection.Count > 0) { personContainers = GetPersonContainersCollections(resultSettings, metadataSettings, personDirectory, numberSign, personDisplayDirectory, personDisplayDirectoryName, approximateYears, personKeyFormattedAndPersonBirthdayCollection); results.AddRange(personContainers); } else { personDisplayDirectoryAllFilePaths = GetFilePaths(resultSettings, metadataSettings, compareSettings, personDisplayDirectory); personContainer = PersonContainer.Get(approximateYears, new(personDisplayDirectoryAllFilePaths), personDisplayDirectoryName, personDirectory); results.Add(personContainer); } } result = new(changes, results); return result; } private static RecordA GetPersonContainersInnerGroups(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, string groupDirectory, string groupDirectoryName) { RecordA result; string[] segments; const int zero = 0; List changes; List allChanges = []; string innerGroupDirectoryName; PersonDirectory personDirectory; List collection; const char exclamationPoint = '!'; string[] personDisplayDirectories; List results = []; char @char = groupDirectoryName[zero]; string[] innerGroupDirectories = Directory.GetDirectories(groupDirectory, "*", SearchOption.TopDirectoryOnly); if (@char == exclamationPoint) { personDirectory = new(@char, "Ignore", 'U', 'U', 'U'); (changes, collection) = GetPersonContainersGroup(resultSettings, metadataSettings, peopleSettings, compareSettings, personDirectory, innerGroupDirectories); allChanges.AddRange(changes); results.AddRange(collection); } else { foreach (string innerGroupDirectory in innerGroupDirectories) { innerGroupDirectoryName = Path.GetFileName(innerGroupDirectory); segments = innerGroupDirectoryName.Split('-'); if (segments.Length == 0) throw new NotSupportedException("Misplaced directory!"); if (segments.Length != 3) continue; if (segments[zero] is not "Alive" and not "Dead" and not "Unknown") continue; if (segments[1] is not "Male" and not "Female" and not "Unknown") continue; if (segments[2] is not "Yes" and not "No" and not "Unknown") continue; personDirectory = new(@char, innerGroupDirectoryName, segments[zero][zero], segments[1][zero], segments[2][zero]); personDisplayDirectories = Directory.GetDirectories(innerGroupDirectory, "*", SearchOption.TopDirectoryOnly); (changes, collection) = GetPersonContainersGroup(resultSettings, metadataSettings, peopleSettings, compareSettings, personDirectory, personDisplayDirectories); allChanges.AddRange(changes); results.AddRange(collection); } } result = new(allChanges, results); return result; } private static List GetPersonContainersGroups(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, string[] groupDirectories) { List results; List changes; string groupDirectoryName; List allChanges = []; List collection; List personContainers = []; for (int i = 0; i < peopleSettings.PersonCharacters.Length; i++) { foreach (string groupDirectory in groupDirectories) { groupDirectoryName = Path.GetFileName(groupDirectory); if (peopleSettings.PersonCharacters[i] != groupDirectoryName.First()) continue; (changes, collection) = GetPersonContainersInnerGroups(resultSettings, metadataSettings, peopleSettings, compareSettings, groupDirectory, groupDirectoryName); allChanges.AddRange(changes); personContainers.AddRange(collection); } } if (allChanges.Any(l => l is not null)) throw new NotImplementedException($"A directory was changed restart to look for more! {string.Join(Environment.NewLine, (from l in allChanges where l is not null select l).ToArray())}"); results = (from l in personContainers orderby l.Key is not null, l.Key select l).ToList(); return results; } internal static ReadOnlyCollection GetPersonContainers(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings) { List results; string a2PeopleSingletonDirectoryChar; string a2PeopleSingletonDirectory = Path.GetFullPath(IResult.GetResultsDateGroupDirectory(resultSettings, nameof(A2_People), resultSettings.ResultSingleton)); if (!Directory.Exists(a2PeopleSingletonDirectory)) _ = Directory.CreateDirectory(a2PeopleSingletonDirectory); foreach (char personCharacter in peopleSettings.PersonCharacters) { a2PeopleSingletonDirectoryChar = Path.Combine(a2PeopleSingletonDirectory, personCharacter.ToString()); if (!Directory.Exists(a2PeopleSingletonDirectoryChar)) _ = Directory.CreateDirectory(a2PeopleSingletonDirectoryChar); } string[] groupDirectories = Directory.GetDirectories(a2PeopleSingletonDirectory, "*", SearchOption.TopDirectoryOnly); if (groupDirectories.Length == 0) results = []; else results = GetPersonContainersGroups(resultSettings, metadataSettings, peopleSettings, compareSettings, groupDirectories); return results.AsReadOnly(); } internal static ReadOnlyCollection GetPersonKeys(ReadOnlyCollection personContainers) { List results = []; long personKey; foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0) continue; foreach (PersonBirthday personBirthday in personContainer.Birthdays) { personKey = personBirthday.Value.Ticks; results.Add(personKey); } } return results.AsReadOnly(); } internal static string? VerifyAge(char numberSign, string personDisplayDirectory, string? minusOne, string personDisplayDirectoryName, int? approximateYears, List personKeyFormattedAndPersonBirthdayCollection) { string? result; if (approximateYears is null) throw new NotSupportedException(); if (personKeyFormattedAndPersonBirthdayCollection.Count == 0) throw new NotSupportedException(); const int zero = 0; int? updateApproximateYears; DateTime dateTime = DateTime.Now; PersonBirthday[] orderedPersonBirthdays = (from l in personKeyFormattedAndPersonBirthdayCollection where !l.PersonKeyFormatted.Contains(numberSign) orderby l.PersonBirthday.Value.Ticks descending select l.PersonBirthday).ToArray(); TimeSpan timeSpan = new(orderedPersonBirthdays[zero].Value.Ticks - dateTime.AddYears(-approximateYears.Value).Ticks); if (timeSpan.TotalDays < -366) updateApproximateYears = approximateYears.Value + 1; else if (timeSpan.TotalDays > 1) updateApproximateYears = approximateYears.Value - 1; else updateApproximateYears = null; if (minusOne is null || updateApproximateYears is null) result = null; else { result = Path.Combine(minusOne, $"{personDisplayDirectoryName.Split('^')[0]}^{updateApproximateYears}"); if (Directory.Exists(result)) result = null; else Directory.Move(personDisplayDirectory, result); } return result; } private static ReadOnlyCollection GetDirectoryAndTicksCollection(PeopleSettings peopleSettings, string? rootDirectory) { List results = []; string directory; DateTime dateTime; string[] personKeyDirectories; string[] personDisplayDirectoryNames; string personKeyFormattedDirectoryName; PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName; foreach (string jLink in peopleSettings.JLinks) { if (rootDirectory is null) continue; directory = Path.Combine(rootDirectory, jLink); if (!Directory.Exists(directory)) continue; personDisplayDirectoryNames = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly); foreach (string personDisplayDirectoryName in personDisplayDirectoryNames) { personKeyDirectories = Directory.GetDirectories(personDisplayDirectoryName, "*", SearchOption.TopDirectoryOnly); foreach (string personKeyFormattedDirectory in personKeyDirectories) { personKeyFormattedDirectoryName = Path.GetFileName(personKeyFormattedDirectory); if (personKeyFormattedDirectoryName.Length != peopleSettings.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormattedDirectoryName, peopleSettings.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) continue; personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(directory, dateTime.Ticks, personKeyFormattedDirectoryName); results.Add(personKeyFormattedAndKeyTicksAndDisplayDirectoryName); } } } return results.AsReadOnly(); } private static ReadOnlyCollection GetGenealogicalDataCommunicationDirectories(PeopleSettings peopleSettings) { ReadOnlyCollection results; string? genealogicalDataCommunicationDirectory = Path.GetDirectoryName(peopleSettings.GenealogicalDataCommunicationFile); results = GetDirectoryAndTicksCollection(peopleSettings, genealogicalDataCommunicationDirectory); return results; } private static string? TryToFind(PeopleSettings peopleSettings, string a2PeopleSingletonDirectory, List a2PeopleSingletonDirectories, string file, string path) { string? result; string? pathName = Path.GetFileName(path); string? group = Path.GetDirectoryName(path); string? groupName = Path.GetFileName(group); if (pathName is null || group is null || groupName is null) result = null; else { string[] matches; matches = (from l in a2PeopleSingletonDirectories where l.DirectoryName == pathName select l.Directory).ToArray(); if (matches.Length == 1) result = matches.First(); else { string pathNameSplitFirst = pathName.Split(peopleSettings.PersonCharacters).First(); matches = (from l in a2PeopleSingletonDirectories where l.DirectoryNameSplitFirst == pathNameSplitFirst select l.Directory).ToArray(); if (matches.Length == 1) result = matches.First(); else { string checkDirectory = Path.Combine(a2PeopleSingletonDirectory, groupName, pathName); if (!Directory.Exists(checkDirectory)) result = null; else result = checkDirectory; } } if (!string.IsNullOrEmpty(result)) { try { WindowsShortcut windowsShortcut; windowsShortcut = new() { Path = result }; windowsShortcut.Save(file); windowsShortcut.Dispose(); } catch (Exception) { } } } return result; } private static ReadOnlyCollection GetJLinkResolvedDirectories(PeopleSettings peopleSettings, List resolvedDirectories) { List results = []; DateTime dateTime; string directoryName; string[] directories; PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName; foreach (string resolvedDirectory in resolvedDirectories) { directories = Directory.GetDirectories(resolvedDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { directoryName = Path.GetFileName(directory); if (directoryName.Length != peopleSettings.PersonBirthdayFormat.Length || !DateTime.TryParseExact(directoryName, peopleSettings.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) continue; personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(resolvedDirectory, dateTime.Ticks, directoryName); results.Add(personKeyFormattedAndKeyTicksAndDisplayDirectoryName); } } if (results.Count != resolvedDirectories.Count) results.Clear(); return results.AsReadOnly(); } internal static ReadOnlyCollection GetJLinkDirectories(ResultSettings resultSettings, PeopleSettings peopleSettings) { ReadOnlyCollection results; string[] files; string? foundPath; int totalFiles = 0; string checkDirectory; WindowsShortcut windowsShortcut; List resolvedDirectories = []; string a2PeopleContentDirectory = Path.GetFullPath(IResult.GetResultsDateGroupDirectory(resultSettings, nameof(A2_People), resultSettings.ResultContent)); if (!Directory.Exists(a2PeopleContentDirectory)) _ = Directory.CreateDirectory(a2PeopleContentDirectory); string a2PeopleSingletonDirectory = Path.GetFullPath(IResult.GetResultsDateGroupDirectory(resultSettings, nameof(A2_People), resultSettings.ResultSingleton)); if (!Directory.Exists(a2PeopleSingletonDirectory)) _ = Directory.CreateDirectory(a2PeopleSingletonDirectory); if (string.IsNullOrEmpty(peopleSettings.GenealogicalDataCommunicationFile)) results = GetDirectoryAndTicksCollection(peopleSettings, a2PeopleContentDirectory); else results = GetGenealogicalDataCommunicationDirectories(peopleSettings); if (results.Count == 0 || results.Count < peopleSettings.JLinks.Length) { RecordB recordB; List a2PeopleSingletonDirectories = []; foreach (string directory in Directory.GetDirectories(a2PeopleSingletonDirectory, "*", SearchOption.AllDirectories)) { recordB = new(directory, Path.GetFileName(directory), Path.GetFileName(directory).Split(peopleSettings.PersonCharacters).First()); a2PeopleSingletonDirectories.Add(recordB); } foreach (string directoryName in peopleSettings.JLinks) { checkDirectory = Path.Combine(a2PeopleContentDirectory, directoryName); if (!Directory.Exists(checkDirectory)) continue; files = Directory.GetFiles(checkDirectory, "*.lnk", SearchOption.TopDirectoryOnly); totalFiles += files.Length; foreach (string file in files) { windowsShortcut = WindowsShortcut.Load(file); if (windowsShortcut.Path is null) continue; if (Directory.Exists(windowsShortcut.Path)) resolvedDirectories.Add(windowsShortcut.Path); else { foundPath = TryToFind(peopleSettings, a2PeopleSingletonDirectory, a2PeopleSingletonDirectories, file, windowsShortcut.Path); if (string.IsNullOrEmpty(foundPath)) continue; resolvedDirectories.Add(foundPath); } } } if (totalFiles == resolvedDirectories.Count) results = GetJLinkResolvedDirectories(peopleSettings, resolvedDirectories); else { resolvedDirectories.Clear(); results = new([]); } } return results; } internal static ReadOnlyCollection GetJLinkResolvedDirectories(ResultSettings resultSettings, PeopleSettings peopleSettings) { ReadOnlyCollection jLinkResolvedDirectories; if (!peopleSettings.JLinks.Where(l => !string.IsNullOrEmpty(l)).Any()) jLinkResolvedDirectories = new([]); else { jLinkResolvedDirectories = GetJLinkDirectories(resultSettings, peopleSettings); if (jLinkResolvedDirectories.Count == 0) throw new Exception(nameof(GetJLinkDirectories)); } return jLinkResolvedDirectories; } }