using System.Collections.ObjectModel; using System.Text.Json; namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Container { private record FilePair(string Path, string? Directory, bool IsUnique, List Collection, 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 = Array.Empty(); else results = new DateTime[] { containerMinimumDateTime.Value, containerMaximumDateTime.Value }; return results; } internal static Models.Item[] GetFilterItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container) { List results = new(); 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, List filesCollection) { int renamed; const bool useCeilingAverage = true; List? filePairs = null; List? jsonFilesCollection = null; IReadOnlyDictionary>? compareFileNamesToFiles = null; IReadOnlyDictionary> fileNamesToFiles = IDirectory.GetFilesKeyValuePairs(filesCollection); for (int i = 0; i < int.MaxValue; i++) { renamed = 0; jsonFilesCollection = IDirectory.GetFilesCollection(aPropertySingletonDirectory, directorySearchFilter, extension, useCeilingAverage); compareFileNamesToFiles = IDirectory.GetFilesKeyValuePairs(jsonFilesCollection); renamed += IDirectory.LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension); filePairs = IDirectory.GetFiles(filesCollection, fileNamesToFiles, extension, compareFileNamesToFiles); renamed += IDirectory.MaybeMove(propertyConfiguration.RootDirectory, propertyConfiguration.ResultAllInOne, propertyConfiguration.ResultAllInOneSubdirectoryLength, 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? property; if (filePair.Match is null) property = null; else { string json = File.ReadAllText(filePair.Match); if (string.IsNullOrEmpty(json)) property = null; else property = JsonSerializer.Deserialize(json); } return property; } private static void ParallelFor(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string extension, int length, Models.FilePair filePair, List results) { string fileName; bool abandoned = false; Models.FileHolder sourceDirectoryFileHolder; Models.Property? property = GetProperty(filePair); Models.FileHolder imageFileInfo = new(filePair.Path); bool? fileSizeChanged = property is not null ? property.FileSize != imageFileInfo.Length : null; string relativePath = IPath.GetRelativePath(filePair.Path, length, forceExtensionToLower: true); bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(imageFileInfo.ExtensionLowered); bool? lastWriteTimeChanged = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime != imageFileInfo.LastWriteTime : null; if (filePair.Match is not null) sourceDirectoryFileHolder = new(filePair.Match); else if (!filePair.IsUnique) sourceDirectoryFileHolder = new(Path.GetFullPath(string.Concat(aPropertySingletonDirectory, relativePath, extension))); else { fileName = Path.GetFileName(filePair.Path); (string directoryName, _) = IPath.GetDirectoryNameAndIndex(propertyConfiguration.ResultAllInOneSubdirectoryLength, fileName); sourceDirectoryFileHolder = new(Path.Combine(aPropertySingletonDirectory, propertyConfiguration.ResultAllInOne, directoryName, $"{fileName}{extension}")); } if (imageFileInfo.LastWriteTime is not null && sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && imageFileInfo.LastWriteTime.Value != sourceDirectoryFileHolder.CreationTime.Value) { File.SetCreationTime(sourceDirectoryFileHolder.FullName, imageFileInfo.LastWriteTime.Value); File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value); } Models.Item item = new(sourceDirectoryFileHolder, relativePath, imageFileInfo, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, property, abandoned, fileSizeChanged, lastWriteTimeChanged); lock (results) results.Add(new(filePair.Path, imageFileInfo.DirectoryName, filePair.IsUnique, filePair.Collection, item)); } private static List GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string extension, List filePairs) { List results = new(); int length = propertyConfiguration.RootDirectory.Length; int maxDegreeOfParallelism = Environment.ProcessorCount; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(propertyConfiguration, aPropertySingletonDirectory, extension, length, filePairs[i], results)); return results; } private static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string directorySearchFilter, List filesCollection) { List results = new(); string? directory; List? items; Models.Container container; const string extension = ".json"; List directories = new(); Dictionary> directoryToItems = new(); 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, new()); if (!directoryToItems.TryGetValue(directory, out items)) throw new Exception(); } } List filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, aPropertySingletonDirectory, filesCollection); List collection = GetFilePairs(propertyConfiguration, aPropertySingletonDirectory, extension, filePairs); foreach (FilePair filePair in collection) { if (filePair.Directory is null) continue; if (!directoryToItems.TryGetValue(filePair.Directory, out items)) { directoryToItems.Add(filePair.Directory, new()); if (!directoryToItems.TryGetValue(filePair.Directory, 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, List filesCollection) { int count; Models.Container[] results; const string directorySearchFilter = "*"; (count, results) = GetContainers(propertyConfiguration, aPropertySingletonDirectory, directorySearchFilter, filesCollection); 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 = "*"; List filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); (count, results) = GetContainers(propertyConfiguration, aPropertySingletonDirectory, directorySearchFilter, filesCollection); return (count, results); } internal static List GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, Models.Container[] containers) { List results = new(); Models.Item[] filteredItems; foreach (Models.Container container in containers) { 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 = new(); List distinct = new(); 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); } }