278 lines
14 KiB
C#

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<string> Collection, Models.Item Item) { }
internal static DateTime[] GetContainerDateTimes(IEnumerable<Models.Item> 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<DateTime>();
else
results = new DateTime[] { containerMinimumDateTime.Value, containerMaximumDateTime.Value };
return results;
}
internal static Models.Item[] GetFilterItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container)
{
List<Models.Item> 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<Models.FilePair> GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string aPropertySingletonDirectory, List<string[]> filesCollection)
{
int renamed;
const bool useCeilingAverage = true;
List<Models.FilePair>? filePairs = null;
List<string[]>? jsonFilesCollection = null;
IReadOnlyDictionary<string, List<string>>? compareFileNamesToFiles = null;
IReadOnlyDictionary<string, List<string>> 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 string[] GetAlternateFileLines(string? alternateFilesCollectionDirectory, Models.FilePair filePair)
{
string[]? results;
string pathDirectoryName = Path.GetFileName(Path.GetDirectoryName(filePair.Path) ?? filePair.Path);
string? alternateFile = alternateFilesCollectionDirectory is null ? null : Path.Combine(alternateFilesCollectionDirectory, pathDirectoryName, $"{Path.GetFileName(filePair.Path)}.tsv");
if (!File.Exists(alternateFile))
results = Array.Empty<string>();
else
results = File.ReadAllLines(alternateFile);
return results;
}
private static (Models.Property?, string[]) GetProperty(Models.FilePair filePair, string? alternateFilesCollectionDirectory)
{
string[] results;
Models.Property? result;
if (filePair.Match is null)
(result, results) = (null, Array.Empty<string>());
else
{
string json = File.ReadAllText(filePair.Match);
if (string.IsNullOrEmpty(json))
(result, results) = (null, Array.Empty<string>());
else
{
result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property);
if (result is null)
results = Array.Empty<string>();
else
{
results = alternateFilesCollectionDirectory is null ? Array.Empty<string>() : GetAlternateFileLines(alternateFilesCollectionDirectory, filePair);
if (results.Any(l => l.Contains('!')))
{
if (result.Keywords is null)
result = Models.Property.GetProperty(result, new string[] { "!" });
else
result = Models.Property.GetProperty(result, result.Keywords.Concat(new string[] { "!" }).ToArray());
}
}
}
}
return (result, results);
}
private static void ParallelFor(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string extension, int rootDirectoryLength, string? alternateFilesCollectionDirectory, Models.FilePair filePair, List<FilePair> results)
{
string fileName;
bool abandoned = false;
Models.FileHolder sourceDirectoryFileHolder;
Models.FileHolder imageFileInfo = new(filePair.Path);
(Models.Property? property, string[] alternateFileLines) = GetProperty(filePair, alternateFilesCollectionDirectory);
bool? fileSizeChanged = property is not null ? property.FileSize != imageFileInfo.Length : null;
string relativePath = IPath.GetRelativePath(filePair.Path, rootDirectoryLength, 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, alternateFileLines, abandoned, fileSizeChanged, lastWriteTimeChanged);
lock (results)
results.Add(new(filePair.Path, imageFileInfo.DirectoryName, filePair.IsUnique, filePair.Collection, item));
}
private static List<FilePair> GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, string extension, List<Models.FilePair> filePairs)
{
List<FilePair> results = new();
int maxDegreeOfParallelism = Environment.ProcessorCount;
string? alternateResultAllInOne = !propertyConfiguration.ResultAllInOne.Contains(' ') ? null : propertyConfiguration.ResultAllInOne.Replace(' ', '-');
string? alternateFilesCollectionDirectory = alternateResultAllInOne is null ? null : Path.Combine(Path.GetDirectoryName(filesCollectionDirectory) ?? filesCollectionDirectory, alternateResultAllInOne);
int filesCollectionDirectoryLength = filesCollectionDirectory.Length;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
_ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(propertyConfiguration, aPropertySingletonDirectory, extension, filesCollectionDirectoryLength, alternateFilesCollectionDirectory, filePairs[i], results));
return results;
}
private static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, List<string[]> filesCollection, string directorySearchFilter)
{
List<Models.Container> results = new();
string? directory;
List<Models.Item>? items;
Models.Container container;
const string extension = ".json";
List<string> directories = new();
Dictionary<string, List<Models.Item>> 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<Models.FilePair> filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, aPropertySingletonDirectory, filesCollection);
List<FilePair> collection = GetFilePairs(propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, 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<string, List<Models.Item>> 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, List<string[]> 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 = "*";
List<string[]> filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
(count, results) = GetContainers(propertyConfiguration, aPropertySingletonDirectory, propertyConfiguration.RootDirectory, filesCollection, directorySearchFilter);
return (count, results);
}
internal static List<int> GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, Models.Container[] containers)
{
List<int> 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<Models.Item> GetItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> containers, bool distinctItems, bool filterItems)
{
List<Models.Item> results = new();
List<int> distinct = new();
IEnumerable<Models.Item> 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);
}
}