using ShellProgressBar; using System.Collections.ObjectModel; using System.Drawing; using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; using static View_by_Distance.Map.Models.Stateless.MapLogic; namespace View_by_Distance.Map.Models.Stateless; internal abstract class FaceFileLogic { private static void MappedParallelFor(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyDictionary> skipCollection, List locationContainers, MappedFile mappedFile) { int? id; string checkFile; DateOnly dateOnly; FilePath filePath; string[] fileMatches; FileHolder fileHolder; int? wholePercentages; const string lnk = ".lnk"; ExifDirectory? exifDirectory; string personDisplayDirectoryName; const bool fromDistanceContent = true; List<(string File, int WholePercentages)>? wholePercentagesCollection; if (!mappedFile.FilePath.Name.EndsWith(lnk)) { if (mappedFile.FilePath.Id is null) return; id = mappedFile.FilePath.Id; wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, mappedFile.FilePath); } else { fileHolder = IFileHolder.Get(mappedFile.FilePath.FullName[..^4]); filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); if (filePath.Id is null) return; id = filePath.Id; wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath); } if (wholePercentages is null) return; if (configuration.LinkedAlpha is null && string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory) && skipCollection.TryGetValue(id.Value, out wholePercentagesCollection)) { fileMatches = (from l in wholePercentagesCollection where l.WholePercentages == wholePercentages select l.File).ToArray(); foreach (string fileMatch in fileMatches) { if (string.IsNullOrEmpty(fileMatch) || !File.Exists(fileMatch)) continue; checkFile = $"{fileMatch}.dup"; if (File.Exists(checkFile)) continue; File.Move(fileMatch, checkFile); continue; } } dateOnly = DateOnly.FromDateTime(new DateTime(mappedFile.FilePath.CreationTicks)); if (mappedFile.FilePath.Name.EndsWith(lnk) || !File.Exists(mappedFile.FilePath.FullName)) exifDirectory = null; else exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(mappedFile.FilePath); RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value); personDisplayDirectoryName = mappedFile.PersonDisplayDirectoryName is null ? configuration.MappingDefaultName : mappedFile.PersonDisplayDirectoryName; LocationContainer locationContainer = new(dateOnly, exifDirectory, mappedFile.DirectoryNumber, personDisplayDirectoryName, null, null, mappedFile.FilePath, fromDistanceContent, id.Value, null, null, mappedFile.PersonKey, rectangle, wholePercentages.Value); lock (locationContainers) locationContainers.Add(locationContainer); } private static ReadOnlyDictionary> GetReadOnly(Dictionary> keyValuePairs) { Dictionary> results = []; foreach (KeyValuePair> keyValuePair in keyValuePairs) results.Add(keyValuePair.Key, new(keyValuePair.Value)); return results.AsReadOnly(); } internal static ReadOnlyDictionary> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) { Dictionary> results = []; List locationContainers = []; List personKeyFormattedCollection = []; Dictionary? keyValuePairs; Dictionary> skipCollection = []; Dictionary> skipNotSkipCollection = []; Dictionary personKeyFormattedToNewestPersonKeyFormatted = []; SetSkipCollections(configuration, personContainers, a2PeopleSingletonDirectory, skipCollection, skipNotSkipCollection); SetPersonCollectionsAfterSetSkipCollections(configuration, personContainers, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection); List records = DistanceLogic.DeleteEmptyDirectoriesAndGetCollection(propertyConfiguration, configuration, ticks, eDistanceContentDirectory, personKeyFormattedToNewestPersonKeyFormatted.AsReadOnly(), personKeyFormattedCollection.AsReadOnly()); List mappedFiles = GetMappedFiles(propertyConfiguration, configuration, personContainers, records); if (mappedFiles.Count > 0) { int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)"; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(mappedFiles.Count, message, options); _ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) => { progressBar.Tick(); MappedParallelFor(propertyConfiguration, configuration, skipCollection.AsReadOnly(), locationContainers, mappedFiles[i]); }); } foreach (LocationContainer locationContainer in locationContainers) { if (!results.TryGetValue(locationContainer.Id, out keyValuePairs)) { results.Add(locationContainer.Id, []); if (!results.TryGetValue(locationContainer.Id, out keyValuePairs)) throw new Exception(); } if (keyValuePairs.ContainsKey(locationContainer.WholePercentages)) continue; keyValuePairs.Add(locationContainer.WholePercentages, locationContainer); } return GetReadOnly(results); } private static void MoveUnableToMatch(FilePath filePath) { string checkFile = $"{filePath.FullName}.unk"; if (File.Exists(filePath.FullName) && !File.Exists(checkFile)) File.Move(filePath.FullName, checkFile); } private static void AvailableParallelFor(Configuration configuration, IFaceD dFace, List locationContainers, FilePath filePath) { string? json; const bool fromDistanceContent = false; if (filePath.Id is null) return; DateOnly dateOnly = DateOnly.FromDateTime(new DateTime(filePath.CreationTicks)); int? wholePercentages = IMapping.GetWholePercentages(dFace.FileNameExtension, filePath); if (wholePercentages is null) { if (configuration.DistanceMoveUnableToMatch) MoveUnableToMatch(filePath); return; } ExifDirectory exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePath); json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(exifDirectory); if (json is null || !json.Contains(nameof(DateTime))) { if (configuration.DistanceMoveUnableToMatch) MoveUnableToMatch(filePath); return; } FaceFile? faceFile = JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile); if (faceFile is null || faceFile.Location is null) { if (configuration.DistanceMoveUnableToMatch) MoveUnableToMatch(filePath); return; } RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value); if (rectangle is null) return; LocationContainer locationContainer = new(dateOnly, exifDirectory, null, null, null, faceFile, filePath, fromDistanceContent, filePath.Id.Value, null, null, null, rectangle, wholePercentages.Value); lock (locationContainers) locationContainers.Add(locationContainer); } internal static List GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection filePaths) { List results = []; int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; string message = $") Building Available Face Files Collection - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(filePaths.Count, message, options); _ = Parallel.For(0, filePaths.Count, parallelOptions, (i, state) => { progressBar.Tick(); AvailableParallelFor(configuration, dFace, results, filePaths[i]); }); return results; } }