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 bool IsIgnoreRelativePath(Properties.IPropertyConfiguration propertyConfiguration, string[] ignoreRelativePaths, string directory) { bool result = false; string? checkDirectory = Path.GetFullPath(directory); for (int i = 0; i < int.MaxValue; i++) { if (ignoreRelativePaths.Contains(Path.GetFileName(checkDirectory))) { result = true; break; } checkDirectory = Path.GetDirectoryName(checkDirectory); if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == propertyConfiguration.RootDirectory) break; } return result; } internal static Models.Container[] SortContainers(Properties.IPropertyConfiguration propertyConfiguration, string[] ignoreRelativePaths, bool argZeroIsConfigurationRootDirectory, string argZero, Models.Container[] containers) { List results = new(); bool isIgnoreRelativePath; for (int i = 1; i < 3; i++) { foreach (Models.Container container in containers) { if (!container.Items.Any()) continue; if (!argZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) continue; isIgnoreRelativePath = ignoreRelativePaths.Any(l => container.SourceDirectory.Contains(l)) && IsIgnoreRelativePath(propertyConfiguration, ignoreRelativePaths, container.SourceDirectory); if (i == 1 && isIgnoreRelativePath) continue; if (i == 2 && !isIgnoreRelativePath) continue; results.Add(container); } } return results.ToArray(); } internal static List GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string aPropertySingletonDirectory, List filesCollection) { int renamed; 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); compareFileNamesToFiles = IDirectory.GetFilesKeyValuePairs(jsonFilesCollection); renamed += IDirectory.LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension); filePairs = IDirectory.GetFiles(filesCollection, fileNamesToFiles, extension, compareFileNamesToFiles); renamed += IDirectory.MaybeMove(propertyConfiguration.RootDirectory, propertyConfiguration.ResultAllInOne, 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) { char directory; 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); directory = IDirectory.GetDirectory(fileName); sourceDirectoryFileHolder = new(Path.Combine(aPropertySingletonDirectory, propertyConfiguration.ResultAllInOne, directory.ToString(), $"{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.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 void CreateShell(Properties.IPropertyConfiguration propertyConfiguration, List directories) { string checkDirectory; int startIndex = propertyConfiguration.RootDirectory.Length; foreach (string directory in directories) { checkDirectory = directory.Insert(startIndex, "-Shell"); if (Directory.Exists(checkDirectory)) continue; _ = Directory.CreateDirectory(checkDirectory); } } internal static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) { List results = new(); string? directory; List? items; Models.Container container; const string extension = ".json"; List directories = new(); const string fileSearchFilter = "*"; const string directorySearchFilter = "*"; Dictionary> directoryToItems = new(); List filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter); foreach (string[] files in filesCollection) { if (!files.Any()) 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(); } } CreateShell(propertyConfiguration, directories); 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.Any()) continue; container = new(keyValuePair.Key, keyValuePair.Value); results.Add(container); } return (collection.Count, results.ToArray()); } 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.Any()) continue; 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 (results.Contains(item.Property.Id.Value)) continue; results.Add(item.Property.Id.Value); } } return results; } internal static List GetItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container[] containers, bool distinctItems, bool filterItems) { List results = new(); List distinct = new(); IEnumerable filteredItems; foreach (Models.Container container in containers) { if (!container.Items.Any()) 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 results; } }