using ShellProgressBar; using System.Collections.ObjectModel; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Map.Models.Stateless; internal abstract class RelationLogic { internal record Group(string Key, long PersonKey, ReadOnlyCollection> RelationContainersCollection); private static Dictionary>>> GetPersonKeyTo(Configuration configuration, List> locationContainers) { List>? collection; Dictionary>>? yearTo; Dictionary>>> personKeyTo = []; foreach (LocationContainer locationContainer in locationContainers) { if (!locationContainer.FromDistanceContent) continue; if (!locationContainer.File.Contains(configuration.LocationContainerDirectoryPattern)) continue; if (!personKeyTo.TryGetValue(locationContainer.PersonKey, out yearTo)) { personKeyTo.Add(locationContainer.PersonKey, []); if (!personKeyTo.TryGetValue(locationContainer.PersonKey, out yearTo)) throw new Exception(); } if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection)) { yearTo.Add(locationContainer.CreationDateOnly.Year, []); if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection)) throw new Exception(); } collection.Add(locationContainer); } return personKeyTo; } private static ReadOnlyCollection GetGroups(Configuration configuration, List> locationContainers) { List results = []; string key; int lastIndex; List years = []; List indices = []; List<(int Index, int Year)> sort = []; List> collection = []; KeyValuePair>> keyValue; Dictionary>>> personKeyTo = GetPersonKeyTo(configuration, locationContainers); foreach (KeyValuePair>>> keyValuePair in personKeyTo) { sort.Clear(); years.Clear(); indices.Clear(); for (int i = 0; i < keyValuePair.Value.Count; i++) sort.Add(new(i, keyValuePair.Value.ElementAt(i).Key)); if (sort.Count == 0) continue; foreach ((int index, int _) in sort.OrderBy(l => l.Year)) indices.Add(index); lastIndex = indices[^1]; foreach (int index in indices) { keyValue = keyValuePair.Value.ElementAt(index); if (keyValue.Value.Count == 0) continue; years.Add(keyValue.Key); collection.AddRange(keyValue.Value); if (index != lastIndex && years.Count < 6 && years.Sum() > configuration.LocationContainerDistanceGroupMinimum) continue; if (years.Count == 1) key = keyValue.Key.ToString(); else key = $"{years.Min()}-{keyValue.Key}"; if (collection.Count == 0) continue; results.Add(new(key, collection[0].PersonKey, new(collection))); collection = []; years.Clear(); } } return new(results); } private static ReadOnlyDictionary MoveFiles(Configuration configuration, string key, bool isCounterPersonYear, string? displayDirectoryName, ReadOnlyCollection relationContainers, List linked1, List linked2, List linked3, List linked4, List linked5, List linked6, List linked7, List linked8, List linked9) { Dictionary results = []; string value; string checkFile; string debugFile; string checkDirectory; string? yearDirectory; string? personNameDirectory; string? maybeTicksDirectoryName; string? personNameDirectoryName; string? personKeyFormattedDirectory; foreach ((FileHolder fileHolder, _) in relationContainers) { personNameDirectory = fileHolder.DirectoryName; yearDirectory = Path.GetDirectoryName(personNameDirectory); personNameDirectoryName = Path.GetFileName(personNameDirectory); personKeyFormattedDirectory = Path.GetDirectoryName(yearDirectory); maybeTicksDirectoryName = Path.GetFileName(personKeyFormattedDirectory); if (string.IsNullOrEmpty(personNameDirectory) || string.IsNullOrEmpty(yearDirectory) || string.IsNullOrEmpty(personKeyFormattedDirectory) || string.IsNullOrEmpty(personNameDirectoryName)) continue; if (linked9.Contains(fileHolder.FullName)) value = "JJJ"; else if (linked8.Contains(fileHolder.FullName)) value = "III"; else if (linked7.Contains(fileHolder.FullName)) value = "HHH"; else if (linked6.Contains(fileHolder.FullName)) value = "GGG"; else if (linked5.Contains(fileHolder.FullName)) value = "FFF"; else if (linked4.Contains(fileHolder.FullName)) value = "EEE"; else if (linked3.Contains(fileHolder.FullName)) value = "DDD"; else if (linked2.Contains(fileHolder.FullName)) value = "CCC"; else if (linked1.Contains(fileHolder.FullName)) value = "BBB"; else value = "AAA"; if (maybeTicksDirectoryName == configuration.LocationContainerDebugDirectory) checkDirectory = Path.Combine(yearDirectory, $"{key}-{value}"); else { if (!string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) continue; checkDirectory = Path.Combine(personKeyFormattedDirectory, $"{key}-{value}"); } if (maybeTicksDirectoryName != configuration.LocationContainerDebugDirectory) { if (isCounterPersonYear || string.IsNullOrEmpty(displayDirectoryName)) checkDirectory = Path.Combine(checkDirectory, personNameDirectoryName); else checkDirectory = Path.Combine(checkDirectory, displayDirectoryName[0].ToString()); } if (checkDirectory == personNameDirectory) continue; if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); checkFile = Path.Combine(checkDirectory, fileHolder.Name); if (File.Exists(checkFile)) continue; results.Add(fileHolder.FullName, checkFile); File.Move(fileHolder.FullName, checkFile); debugFile = $"{fileHolder.FullName[..^4]}.gif"; if (File.Exists(debugFile)) { checkFile = Path.Combine(checkDirectory, $"{Path.GetFileName(fileHolder.FullName)[..^4]}.gif"); if (File.Exists(checkFile)) continue; File.Move(debugFile, checkFile); } if (maybeTicksDirectoryName == configuration.LocationContainerDebugDirectory && !string.IsNullOrEmpty(displayDirectoryName)) { checkDirectory = Path.Combine(checkDirectory, displayDirectoryName); if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); } } return new(results); } private static string? GetDisplayDirectoryName(ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, long personKey, string personKeyFormatted) { string? result; PersonContainer? personContainer; List? collection; _ = readOnlyPersonKeyToPersonContainerCollection.TryGetValue(personKey, out collection); if (collection is not null) result = collection[0].DisplayDirectoryName; else { if (!readOnlyPersonKeyFormattedToPersonContainer.TryGetValue(personKeyFormatted, out personContainer)) result = null; else result = personContainer.DisplayDirectoryName; } return result; } private static int GetTake(int locationContainerDistanceTake, int count) { int result = locationContainerDistanceTake; int subtract = (int)(locationContainerDistanceTake * .05); if (subtract < 1) subtract = 1; if (count > 9000) result -= subtract; if (count > 8000) result -= subtract; if (count > 7000) result -= subtract; if (count > 6000) result -= subtract; if (count > 5000) result -= subtract; if (count > 4000) result -= subtract; if (count > 3000) result -= subtract; if (count > 2000) result -= subtract; if (count > 1000) result -= subtract; if (result < 3) result = 3; return result; } private static void WriteVsCodeFiles(string eDistanceContentDirectory, string? displayDirectoryName, string directory) { string json; string vsCodeDirectory = Path.Combine(directory, ".vscode"); if (!Directory.Exists(vsCodeDirectory)) _ = Directory.CreateDirectory(vsCodeDirectory); if (displayDirectoryName is not null) File.WriteAllText(Path.Combine(directory, $"_ {displayDirectoryName}.txt"), string.Empty); json = "{ \"[markdown]\": { \"editor.wordWrap\": \"off\" }, \"foam.links.hover.enable\": false, \"foam.graph.style\": { \"background\": \"#202020\", \"node\": { \"note\": \"#f2cb1d\", \"distance\": \"green\", \"image\": \"orange\", \"placeholder\": \"white\", } } }"; _ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "settings.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); json = string.Concat("{ \"version\": \"2.0.0\", \"tasks\": [ { \"label\": \"MKLink\", \"type\": \"shell\", \"command\": \"New-Item\", \"args\": [ \"-ItemType\", \"Junction\", \"-Path\", \"'", directory.Replace('\\', '/'), "/()'\", \"-Target\", \"'", eDistanceContentDirectory.Replace('\\', '/'), "'\" ], \"problemMatcher\": [] } ] }"); _ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "tasks.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); } private static ReadOnlyDictionary GetMoveFiles(Configuration configuration, string key, int take, bool isCounterPersonYear, string? displayDirectoryName, ReadOnlyCollection relationContainers) { ReadOnlyDictionary results; List linked1 = []; List linked2 = []; List linked3 = []; List linked4 = []; List linked5 = []; List linked6 = []; List linked7 = []; List linked8 = []; List linked9 = []; foreach ((FileHolder fileHolder, ReadOnlyCollection relations) in relationContainers) { foreach (Relation relation in relations.Take(take)) { if (!linked1.Contains(relation.File)) { linked1.Add(relation.File); continue; } if (!linked2.Contains(relation.File)) { linked2.Add(relation.File); continue; } if (!linked3.Contains(relation.File)) { linked3.Add(relation.File); continue; } if (!linked4.Contains(relation.File)) { linked4.Add(relation.File); continue; } if (!linked5.Contains(relation.File)) { linked5.Add(relation.File); continue; } if (!linked6.Contains(relation.File)) { linked6.Add(relation.File); continue; } if (!linked7.Contains(relation.File)) { linked7.Add(relation.File); continue; } if (!linked8.Contains(relation.File)) { linked8.Add(relation.File); continue; } if (!linked9.Contains(relation.File)) { linked9.Add(relation.File); continue; } } } results = MoveFiles(configuration, key, isCounterPersonYear, displayDirectoryName, relationContainers, linked1, linked2, linked3, linked4, linked5, linked6, linked7, linked8, linked9); return results; } private static void WriteFile(int take, long personKey, bool isCounterPersonYear, string personKeyFormatted, string? displayDirectoryName, string directory, long ticks, Uri uri, ReadOnlyCollection relationContainers, ReadOnlyDictionary movedFiles) { string a; string b; int years; string text; string? file; Relation relation; string markDownFile; FileHolder fileHolder; string originalString; List lines = []; string fileNameWithoutExtension; foreach ((FileHolder relationFileHolder, ReadOnlyCollection relations) in relationContainers) { lines.Clear(); if (movedFiles.TryGetValue(relationFileHolder.FullName, out file)) fileHolder = new(file); else fileHolder = new(relationFileHolder.FullName); if (!relationFileHolder.Exists || relationFileHolder.CreationTime is null) continue; if (isCounterPersonYear) (years, _) = IAge.GetAge(ticks, relationFileHolder.CreationTime.Value.Ticks); else (years, _) = IAge.GetAge(relationFileHolder.CreationTime.Value.Ticks, personKey); fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileHolder.NameWithoutExtension); markDownFile = $"{fileNameWithoutExtension}.md"; lines.Add("---"); lines.Add("type: \"distance\""); lines.Add("---"); lines.Add(string.Empty); if (displayDirectoryName is null) lines.Add($"## {personKeyFormatted}"); else lines.Add($"## {displayDirectoryName}"); lines.Add(string.Empty); lines.Add($"![0]({uri.MakeRelativeUri(new(fileHolder.FullName)).OriginalString})"); lines.Add($"- __{fileNameWithoutExtension}__"); if (isCounterPersonYear) lines.Add($"- #{years}yrs-ago"); else lines.Add($"- #{years}yrs-old"); lines.Add($"- ~~{relations.Count}~~"); lines.Add(string.Empty); for (int i = 0; i < relations.Count; i++) { relation = relations[i]; if (movedFiles.TryGetValue(relation.File, out file)) fileHolder = new(file); else fileHolder = new(relation.File); if (!fileHolder.Exists || fileHolder.CreationTime is null) continue; fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileHolder.NameWithoutExtension); originalString = uri.MakeRelativeUri(new(fileHolder.FullName)).OriginalString; if (i < take) (a, b) = ("[[", "]]"); else (a, b) = ("~~", "~~"); lines.Add($"![{relation.DistancePermyriad}]({originalString}){Environment.NewLine}{a}{fileNameWithoutExtension}{b}"); lines.Add(string.Empty); } lines.Add(""); text = string.Join(Environment.NewLine, lines); File.WriteAllText(Path.Combine(directory, markDownFile), text); } } private static void AddDisplayDirectoryNames(Configuration configuration, string eDistanceContentDirectory, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection, ReadOnlyCollection groups) { bool isCounterPersonYear; string personKeyFormatted; string? displayDirectoryName; string? checkDirectory = Path.Combine(eDistanceContentDirectory, configuration.LocationContainerDebugDirectory); _ = IPath.DeleteEmptyDirectories(checkDirectory); foreach (Group group in groups) { if (configuration.LocationContainerDistanceTolerance is null) break; isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(new DateTime(group.PersonKey).Year); if (isCounterPersonYear) continue; personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, group.PersonKey); checkDirectory = Path.Combine(eDistanceContentDirectory, configuration.LocationContainerDebugDirectory, personKeyFormatted); if (!Directory.Exists(checkDirectory)) continue; displayDirectoryName = GetDisplayDirectoryName(readOnlyPersonKeyToPersonContainerCollection, readOnlyPersonKeyFormattedToPersonContainer, group.PersonKey, personKeyFormatted); if (string.IsNullOrEmpty(displayDirectoryName)) continue; foreach (string yearDirectory in Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly)) { checkDirectory = Path.Combine(yearDirectory, displayDirectoryName); if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); } checkDirectory = Path.Combine(eDistanceContentDirectory, configuration.LocationContainerDebugDirectory, personKeyFormatted, displayDirectoryName); if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); } } internal static void SaveMappedRelations(Configuration configuration, Shared.Models.Methods.IDistance distance, string a2PeopleContentDirectory, string eDistanceContentDirectory, long ticks, List> locationContainers, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection) { int take; string directory; bool isCounterPersonYear; string personKeyFormatted; string? displayDirectoryName; Uri uri = new(eDistanceContentDirectory); ReadOnlyDictionary movedFiles; ReadOnlyCollection relationContainers; ReadOnlyCollection groups = GetGroups(configuration, locationContainers); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") Save Mapped Relations - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(groups.Count, message, options); foreach (Group group in groups) { if (configuration.LocationContainerDistanceTolerance is null) break; progressBar.Tick(); if (group.RelationContainersCollection.Count == 0) continue; take = GetTake(configuration.LocationContainerDistanceTake, group.RelationContainersCollection.Count); isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(new DateTime(group.PersonKey).Year); personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, group.PersonKey); displayDirectoryName = GetDisplayDirectoryName(readOnlyPersonKeyToPersonContainerCollection, readOnlyPersonKeyFormattedToPersonContainer, group.PersonKey, personKeyFormatted); directory = Path.Combine(a2PeopleContentDirectory, $"{ticks}-{configuration.LocationContainerDistanceTolerance.Value}", personKeyFormatted, group.Key); if (!Directory.Exists(directory)) _ = Directory.CreateDirectory(directory); WriteVsCodeFiles(eDistanceContentDirectory, displayDirectoryName, directory); relationContainers = distance.GetRelationContainers(configuration.FaceDistancePermyriad, configuration.LocationContainerDistanceTake, configuration.LocationContainerDistanceTolerance.Value, group.RelationContainersCollection); movedFiles = GetMoveFiles(configuration, group.Key, take, isCounterPersonYear, displayDirectoryName, relationContainers); WriteFile(take, group.PersonKey, isCounterPersonYear, personKeyFormatted, displayDirectoryName, directory, ticks, uri, relationContainers, movedFiles); } if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); else AddDisplayDirectoryNames(configuration, eDistanceContentDirectory, readOnlyPersonKeyFormattedToPersonContainer, readOnlyPersonKeyToPersonContainerCollection, groups); } }