using Humanizer; using ShellProgressBar; using System.Buffers; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Map.Models.Stateless; internal record Record(int DirectoryNumber, bool? IsDefault, int? LinksCount, FilePath MappedFaceFilePath, string? PersonDisplayDirectoryName, string PersonKeyFormatted); internal abstract class DistanceLogic { internal record TicksDirectory(DateTime AlternateDirectoryDateTime, string Directory, DateTime DirectoryDateTime, string DirectoryName, bool? IsLocationContainerDebugDirectory, float? TotalDays); private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames) { string checkFile; string actionDirectoryName = Path.GetFileName(actionDirectory); string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName); if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); foreach (string file in files) { if (facesFileNames.Contains(file)) { checkFile = Path.Combine(checkDirectory, Path.GetFileName(file)); if (File.Exists(checkFile)) continue; File.Move(file, checkFile); continue; } File.Delete(file); } } private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory) { string[] files; string checkFile; string? checkDirectory; string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory)); if (!Directory.Exists(checkDirectory)) Directory.Move(directory, checkDirectory); else { files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories); foreach (string file in files) { if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted)) continue; checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted); checkDirectory = Path.GetDirectoryName(checkFile); if (checkDirectory is null) continue; if (File.Exists(checkFile)) continue; if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); File.Move(file, checkFile); } } } _ = IPath.DeleteEmptyDirectories(personKeyDirectory); } private static List UpdateDateVerifyAndGetTicksDirectories(Configuration configuration, string eDistanceContentDirectory) { List results = []; float? totalDays; long? next = null; string? checkDirectory; string ticksDirectoryName; DateTime directoryDateTime; DirectoryInfo directoryInfo; long? lastDirectoryTicks = null; DateTime dateTime = DateTime.Now; DateTime alternateDirectoryDateTime; bool? isLocationContainerDebugDirectory; long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks; for (int i = 1; i < 5; i++) _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); if (!Directory.Exists(eDistanceContentDirectory)) _ = Directory.CreateDirectory(eDistanceContentDirectory); string[] ticksDirectories = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string ticksDirectory in ticksDirectories) { ticksDirectoryName = Path.GetFileName(ticksDirectory); if (ticksDirectoryName.Length < 3) continue; if (!long.TryParse(ticksDirectoryName, out long directoryTicks)) throw new NotSupportedException(); if (next is null) next = new DateTime(directoryTicks).Ticks; else { next += month; checkDirectory = Path.GetDirectoryName(ticksDirectory); if (string.IsNullOrEmpty(checkDirectory)) { if (string.IsNullOrEmpty(checkDirectory)) continue; checkDirectory = Path.Combine(checkDirectory, next.Value.ToString()); if (ticksDirectory == checkDirectory || !checkDirectory.EndsWith(configuration.LocationContainerDirectoryPattern)) continue; Directory.Move(ticksDirectory, checkDirectory); continue; } } directoryInfo = new(ticksDirectory); directoryDateTime = new DateTime(directoryTicks); if (directoryInfo.CreationTime.Ticks != directoryTicks) Directory.SetCreationTime(ticksDirectory, new DateTime(directoryTicks)); if (directoryInfo.LastWriteTime.Ticks != directoryTicks) Directory.SetLastWriteTime(ticksDirectory, new DateTime(directoryTicks)); alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1); isLocationContainerDebugDirectory = configuration.LocationContainerDebugDirectory is null ? null : ticksDirectoryName.EndsWith(configuration.LocationContainerDebugDirectory); totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays; results.Add(new(alternateDirectoryDateTime, ticksDirectory, new(directoryTicks), ticksDirectoryName, isLocationContainerDebugDirectory, totalDays)); if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0) continue; lastDirectoryTicks = directoryTicks; } string[] compare = (from l in results where l.TotalDays is not null and < 9.95f select l.Directory).ToArray(); if (compare.Length > 0 && configuration.ReMap) throw new Exception($"Please Consolidate <{string.Join(Environment.NewLine, compare)}>"); return results; } private static void Individually(Configuration configuration, TicksDirectory ticksDirectory, string directory) { bool isDefault; string[] files; FileInfo[] collection; string[] facesFileNames; string yearDirectoryName; string[] yearDirectories; string alphaDirectoryName; string matchDirectoryName; string personKeyFormatted; string[] alphaDirectories; string[] matchDirectories; string[] actionDirectories; string personDisplayDirectory; string[] personKeyDirectories; string[] segmentCDirectories = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly); foreach (string segmentCDirectory in segmentCDirectories) { personKeyDirectories = Directory.GetDirectories(segmentCDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string personKeyDirectory in personKeyDirectories) { personKeyFormatted = Path.GetFileName(personKeyDirectory); yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string yearDirectory in yearDirectories) { yearDirectoryName = Path.GetFileName(yearDirectory); if (yearDirectoryName.StartsWith('=')) Directory.Move(yearDirectory, yearDirectory.Replace('=', '~')); } yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string yearDirectory in yearDirectories) { yearDirectoryName = Path.GetFileName(yearDirectory); matchDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); alphaDirectories = matchDirectories.Where(l => !long.TryParse(Path.GetFileName(l), out long a)).ToArray(); if (alphaDirectories.Length == 0) continue; alphaDirectoryName = Path.GetFileName(alphaDirectories[0]); foreach (string matchDirectory in matchDirectories) { matchDirectoryName = Path.GetFileName(matchDirectory); files = Directory.GetFiles(matchDirectory, "*", SearchOption.TopDirectoryOnly); if (files.Length != 4) continue; collection = files.Select(l => new FileInfo(l)).ToArray(); isDefault = IPerson.IsDefaultName(alphaDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]); if (isDefault) facesFileNames = (from l in collection where l.Extension == configuration.FacesFileNameExtension select l.FullName).ToArray(); else facesFileNames = (from l in collection where l.Extension == configuration.FacesFileNameExtension && l.Name.Contains(matchDirectoryName) select l.FullName).ToArray(); if (facesFileNames.Length == 0) continue; personDisplayDirectory = Path.Combine(matchDirectory, alphaDirectoryName); if (!Directory.Exists(personDisplayDirectory) || !Directory.Exists(matchDirectory)) continue; _ = Process.Start("explorer", matchDirectory); for (int i = 0; i < int.MaxValue; i++) { Thread.Sleep(500); actionDirectories = Directory.GetDirectories(matchDirectory, "*", SearchOption.TopDirectoryOnly).Where(l => l != personDisplayDirectory && !l.EndsWith("Maybe")).ToArray(); if (actionDirectories.Length > 0) { MoveTo(actionDirectories[0], ticksDirectory, directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName, files, facesFileNames); break; } } } } } } } private static List GetRecords(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List distinct, string? personDisplayDirectoryName) { List results = []; string fileName; string checkFile; FilePath filePath; FileHolder fileHolder; int? wholePercentages; foreach (string file in files) { if (file.EndsWith(".lnk")) continue; fileHolder = IFileHolder.Get(file); filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); if (filePath.Id is null) continue; wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath); if (wholePercentages is null) continue; fileName = Path.GetFileName(file); if (distinct.Contains(fileName)) { checkFile = $"{file}.dup"; if (File.Exists(checkFile)) continue; File.Move(file, checkFile); continue; } distinct.Add(fileName); results.Add(new(directoryNumber, isDefault, linksCount, filePath, personDisplayDirectoryName, personKeyFormatted)); } return results; } private static string[] RenameBirth(string[] files) { List results = []; string checkFile; foreach (string file in files) { if (file.EndsWith(".brt")) { results.Add(file); continue; } checkFile = $"{file}.brt"; if (File.Exists(checkFile)) { results.Add(file); continue; } File.Move(file, checkFile); results.Add(checkFile); } return results.ToArray(); } private static void MovedToNewestPersonKeyFormatted(string personKeyFormatted, string newestPersonKeyFormatted, TicksDirectory ticksDirectory, string personKeyDirectory) { string newestPersonKeyDirectory = Path.Combine(ticksDirectory.Directory, newestPersonKeyFormatted); if (Directory.Exists(newestPersonKeyDirectory)) MoveFiles(personKeyFormatted, personKeyDirectory, newestPersonKeyFormatted, newestPersonKeyDirectory); else Directory.Move(personKeyDirectory, newestPersonKeyDirectory); } private static int? GetLinksCount(string yearDirectory) { int? result; string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-'); if (yearDirectoryNameSegments.Length != 3) result = null; else { string lastSegment = yearDirectoryNameSegments[^1]; if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0])) result = null; else result = lastSegment[0] - 65; } return result; } internal static List DeleteEmptyDirectoriesAndGetCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, string eDistanceContentDirectory, ReadOnlyDictionary personKeyFormattedToNewestPersonKeyFormatted, ReadOnlyCollection personKeyFormattedCollection) { List results = []; bool check; string message; string[] files; bool? isDefault; int? linksCount; int totalSeconds; DateTime dateTime; TimeSpan timeSpan; int directoryNumber; string? checkDirectory; ProgressBar progressBar; string[] yearDirectories; string personKeyFormatted; List distinct = []; string? personFirstInitial; bool isReservedDirectoryName; string[] personNameDirectories; string? newestPersonKeyFormatted; string? personDisplayDirectoryName; string[] personNameLinkDirectories; string? personFirstInitialDirectory; List ticksDirectories; string[] personKeyFormattedDirectories; string manualCopyHumanized = nameof(Shared.Models.Stateless.IMapLogic.ManualCopy).Humanize(LetterCasing.Title); string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title); ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; for (int i = 1; i < 6; i++) { check = false; results.Clear(); distinct.Clear(); directoryNumber = 0; ticksDirectories = UpdateDateVerifyAndGetTicksDirectories(configuration, eDistanceContentDirectory); totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); message = $"{i}) {ticksDirectories.Count:000} compile from and clean ticks Director(ies) - B - {totalSeconds} total second(s)"; progressBar = new(ticksDirectories.Count, message, options); foreach (TicksDirectory ticksDirectory in ticksDirectories) { if (i == 1) progressBar.Tick(); personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly); foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories) { personKeyFormatted = Path.GetFileName(personKeyFormattedDirectory); isReservedDirectoryName = personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Sorting)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Mapping)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.ManualCopy)); if (!isReservedDirectoryName && personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Individually))) { Individually(configuration, ticksDirectory, personKeyFormattedDirectory); throw new Exception($"B) Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!"); } _ = personKeyFormattedToNewestPersonKeyFormatted.TryGetValue(personKeyFormatted, out newestPersonKeyFormatted); if (personKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null) { timeSpan = new TimeSpan(DateTime.Now.Ticks - ticksDirectory.DirectoryDateTime.Ticks); if (timeSpan.TotalDays > 6) throw new Exception($"{configuration.MappingDefaultName} <{ticksDirectory.DirectoryDateTime}> are only allowed within x days!"); } yearDirectories = Directory.GetDirectories(personKeyFormattedDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string yearDirectory in yearDirectories) { if (check && !Directory.Exists(yearDirectory)) continue; if (ticksDirectory.IsLocationContainerDebugDirectory is null || !ticksDirectory.IsLocationContainerDebugDirectory.Value) linksCount = null; else linksCount = GetLinksCount(yearDirectory); if (ticksDirectory.DirectoryName != configuration.LocationContainerDebugDirectory) { files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) File.Delete(file); } if (ticksDirectory.DirectoryName == configuration.LocationContainerDebugDirectory) { isDefault = null; personDisplayDirectoryName = null; files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly); results.AddRange(GetRecords(propertyConfiguration, configuration, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName)); files = Directory.GetFiles(yearDirectory, "*.lnk", SearchOption.AllDirectories); foreach (string file in files) File.Delete(file); continue; } personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); if (personNameDirectories.Length > 1) throw new NotSupportedException("Try deleting *.lnk files!"); foreach (string personNameDirectory in personNameDirectories) { directoryNumber++; personDisplayDirectoryName = Path.GetFileName(personNameDirectory); isDefault = IPerson.IsDefaultName(personDisplayDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]); if (isDefault.Value && personDisplayDirectoryName.Length == 1) { if (personKeyFormatted.Length != configuration.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormatted, configuration.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) continue; checkDirectory = Path.Combine(yearDirectory, $"X+{dateTime.Ticks}"); if (Directory.Exists(checkDirectory)) { Directory.Delete(yearDirectory, recursive: true); continue; } Directory.Move(personNameDirectory, checkDirectory); if (!check) check = true; continue; } if (isDefault.Value && (ticksDirectory.DirectoryDateTime.Hour != 0 || ticksDirectory.DirectoryDateTime.Minute != 0 || ticksDirectory.DirectoryDateTime.Second != 0)) { checkDirectory = Path.GetDirectoryName(ticksDirectory.Directory); if (checkDirectory is null) continue; checkDirectory = Path.Combine(checkDirectory, ticksDirectory.AlternateDirectoryDateTime.Ticks.ToString()); if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); checkDirectory = Path.Combine(checkDirectory, personKeyFormatted); if (!Directory.Exists(checkDirectory)) { Directory.Move(personKeyFormattedDirectory, checkDirectory); if (!check) check = true; break; } } files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly); if (isReservedDirectoryName && files.Length > 0) throw new Exception($"Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!"); if (personKeyFormatted == manualCopyHumanized && files.Length > 0) throw new Exception($"Move personKey directories up one from {manualCopyHumanized} and delete {manualCopyHumanized} directory!"); if (personKeyFormatted == forceSingleImageHumanized && files.Length > 0) throw new Exception($"Move personKey directories up one from {forceSingleImageHumanized} and delete {forceSingleImageHumanized} directory!"); if (!isDefault.Value) { if (personKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null) files = RenameBirth(files); else if (newestPersonKeyFormatted is not null && personKeyFormatted != newestPersonKeyFormatted) { if (!check) check = true; MovedToNewestPersonKeyFormatted(personKeyFormatted, newestPersonKeyFormatted, ticksDirectory, personKeyFormattedDirectory); continue; } } if (personKeyFormatted.Length != configuration.PersonBirthdayFormat.Length) continue; if (personDisplayDirectoryName.Length == 1 || isDefault.Value || !personKeyFormattedCollection.Contains(personKeyFormatted)) personFirstInitialDirectory = personNameDirectory; else { personFirstInitial = personDisplayDirectoryName[..1]; if (personFirstInitial.All(char.IsDigit)) { foreach (string file in files) File.Delete(file); files = Directory.GetFiles(personNameDirectory, "*", SearchOption.AllDirectories); foreach (string file in files) File.Delete(file); _ = IPath.DeleteEmptyDirectories(personNameDirectory); continue; } personFirstInitialDirectory = Path.Combine(yearDirectory, personFirstInitial.ToString()); if (Directory.Exists(personFirstInitialDirectory)) throw new Exception("Forgot to ..."); Directory.Move(personNameDirectory, personFirstInitialDirectory); files = Directory.GetFiles(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly); } results.AddRange(GetRecords(propertyConfiguration, configuration, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName)); personNameLinkDirectories = Directory.GetDirectories(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string personNameLinkDirectory in personNameLinkDirectories) { files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) { if (!file.EndsWith(".lnk")) continue; File.Delete(file); } _ = IPath.DeleteEmptyDirectories(personNameLinkDirectory); } _ = IPath.DeleteEmptyDirectories(personFirstInitialDirectory); } _ = IPath.DeleteEmptyDirectories(yearDirectory); } _ = IPath.DeleteEmptyDirectories(personKeyFormattedDirectory); } _ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory); _ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory); } progressBar.Dispose(); if (check) continue; break; } return results; } }