using System.Collections.ObjectModel; using System.Text.Json; namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Container { private record FilePair(bool IsUnique, List Collection, FilePath FilePath, Models.Item Item) { } internal static DateTime[] GetContainerDateTimes(IEnumerable items) { DateTime[] results; DateTime? containerMinimumDateTime; DateTime? containerMaximumDateTime; containerMinimumDateTime = (from l in items select l.ImageFileHolder.LastWriteTime).Min(); if (containerMinimumDateTime is null) containerMaximumDateTime = null; else containerMaximumDateTime = (from l in items select l.ImageFileHolder.LastWriteTime).Max(); if (containerMinimumDateTime is null || containerMaximumDateTime is null) results = []; else results = [containerMinimumDateTime.Value, containerMaximumDateTime.Value]; return results; } internal static Models.Item[] GetFilterItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container) { List results = []; foreach (Models.Item item in container.Items) { if (item.ImageFileHolder is not null && item.IsValidImageFormatExtension && !propertyConfiguration.IgnoreExtensions.Contains(item.ImageFileHolder.ExtensionLowered)) results.Add(item); } return results.ToArray(); } internal static List GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string aPropertySingletonDirectory, ReadOnlyCollection filesCollection) { int renamed; const bool useCeilingAverage = true; List? filePairs = null; ReadOnlyCollection? jsonFilesCollection = null; IReadOnlyDictionary>? compareFileNamesToFiles = null; IReadOnlyDictionary> fileNamesToFiles = XDirectory.GetFilesKeyValuePairs(filesCollection); for (int i = 0; i < int.MaxValue; i++) { renamed = 0; jsonFilesCollection = IDirectory.GetFilesCollection(aPropertySingletonDirectory, directorySearchFilter, extension, useCeilingAverage); compareFileNamesToFiles = XDirectory.GetFilesKeyValuePairs(jsonFilesCollection); renamed += XDirectory.LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension); filePairs = XDirectory.GetFiles(filesCollection, fileNamesToFiles, extension, compareFileNamesToFiles); renamed += XDirectory.MaybeMove(propertyConfiguration, filePairs, aPropertySingletonDirectory, extension); if (renamed == 0) break; } if (filePairs is null || jsonFilesCollection is null || compareFileNamesToFiles is null) throw new NullReferenceException(nameof(filePairs)); return filePairs; } private static Models.Property? GetProperty(Models.FilePair filePair) { Models.Property? result; if (filePair.Match is null) result = null; else { string json = File.ReadAllText(filePair.Match); if (string.IsNullOrEmpty(json)) result = null; else result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property); } return result; } private static void ParallelFor(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string extension, int rootDirectoryLength, Models.FilePair filePair, List results) { bool abandoned = false; Models.FileHolder sourceDirectoryFileHolder; Models.Property? property = GetProperty(filePair); Models.FileHolder imageFileInfo = IFileHolder.Get(filePair.Path); FilePath filePath = FilePath.Get(propertyConfiguration, imageFileInfo, index: null); bool? fileSizeChanged = property is not null ? property.FileSize != filePath.Length : null; bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered); if (property is not null && property.Keywords is not null && !IId.IsIgnore(filePath) && propertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l))) { if (filePath.DirectoryName.Contains("Results") && filePath.DirectoryName.Contains("Resize")) File.Delete(filePath.FullName); else throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>"); } string relativePath = IPath.GetRelativePath(filePair.Path, rootDirectoryLength, forceExtensionToLower: true); bool? lastWriteTimeChanged = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime.Ticks != filePath.LastWriteTicks : null; if (filePair.Match is not null) sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match); else if (!filePair.IsUnique) sourceDirectoryFileHolder = IFileHolder.Get(Path.GetFullPath(string.Concat(aPropertySingletonDirectory, relativePath, extension))); else { string fileName = Path.GetFileName(filePair.Path); (string directoryName, _) = IPath.GetDirectoryNameAndIndex(propertyConfiguration, filePath); sourceDirectoryFileHolder = IFileHolder.Get(Path.Combine(aPropertySingletonDirectory, directoryName, $"{fileName}{extension}")); } if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks) { File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePath.LastWriteTicks)); File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value); } Models.Item item = new(filePath, sourceDirectoryFileHolder, relativePath, imageFileInfo, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, property, abandoned, fileSizeChanged, lastWriteTimeChanged); lock (results) results.Add(new(filePair.IsUnique, filePair.Collection, filePath, item)); } private static List GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, string extension, List filePairs) { List results = []; int maxDegreeOfParallelism = Environment.ProcessorCount; int filesCollectionDirectoryLength = filesCollectionDirectory.Length; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(propertyConfiguration, aPropertySingletonDirectory, extension, filesCollectionDirectoryLength, filePairs[i], results)); return results; } private static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection, string directorySearchFilter) { List results = []; string? directory; List? items; Models.Container container; List directories = []; const string extension = ".json"; Dictionary> directoryToItems = []; foreach (string[] files in filesCollection) { if (files.Length == 0) continue; directory = Path.GetDirectoryName(files.First()); if (directory is null) continue; if (!directories.Contains(directory)) directories.Add(directory); if (!directoryToItems.TryGetValue(directory, out items)) { directoryToItems.Add(directory, []); if (!directoryToItems.TryGetValue(directory, out items)) throw new Exception(); } } List filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, aPropertySingletonDirectory, filesCollection); List collection = GetFilePairs(propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, extension, filePairs); foreach (FilePair filePair in collection) { if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryName, out items)) { directoryToItems.Add(filePair.FilePath.DirectoryName, []); if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryName, out items)) throw new Exception(); } items.Add(filePair.Item); } foreach (KeyValuePair> keyValuePair in directoryToItems) { if (keyValuePair.Value.Count == 0) continue; container = new(keyValuePair.Key, keyValuePair.Value); results.Add(container); } return (collection.Count, results.ToArray()); } internal static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection) { int count; Models.Container[] results; const string directorySearchFilter = "*"; (count, results) = GetContainers(propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection, directorySearchFilter); return (count, results); } internal static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) { int count; Models.Container[] results; const bool useCeilingAverage = true; const string fileSearchFilter = "*"; const string directorySearchFilter = "*"; ReadOnlyCollection filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); (count, results) = GetContainers(propertyConfiguration, aPropertySingletonDirectory, propertyConfiguration.RootDirectory, filesCollection, directorySearchFilter); return (count, results); } internal static List GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) { List results = []; Models.Item[] filteredItems; foreach (Models.Container container in readOnlyContainers) { if (container.Items.Count == 0) continue; filteredItems = GetFilterItems(propertyConfiguration, container); if (filteredItems.Length == 0) continue; foreach (Models.Item item in filteredItems) { if (item.Property?.Id is null || item.ResizedFileHolder is null) continue; if (results.Contains(item.Property.Id.Value)) continue; results.Add(item.Property.Id.Value); } } return results; } internal static ReadOnlyCollection GetItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) { List results = []; List distinct = []; IEnumerable filteredItems; foreach (Models.Container container in containers) { if (container.Items.Count == 0) continue; if (!filterItems) filteredItems = container.Items; else { filteredItems = GetFilterItems(propertyConfiguration, container); if (!filteredItems.Any()) continue; } foreach (Models.Item item in filteredItems) { if (item.Property?.Id is null || item.ResizedFileHolder is null) continue; if (distinctItems) { if (distinct.Contains(item.Property.Id.Value)) continue; distinct.Add(item.Property.Id.Value); } results.Add(item); } } return new(results); } }