namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract partial class XDirectory { private record SortedRecord(Models.FileHolder FileHolder, bool NameWithoutExtensionIsIdFormat, int? Id); private static int GetCeilingAverage(List fileCollection) { List counts = new(); foreach (string[] files in fileCollection) counts.Add(files.Length); int average = (int)Math.Ceiling(counts.Average()); return average; } private static List GetFilesCollection(List fileCollection, int ceilingAverage) { List results = new(); foreach (string[] files in fileCollection) { if (files.Length < ceilingAverage) results.Add(files); } foreach (string[] files in fileCollection) { if (files.Length >= ceilingAverage) results.Add(files); } return results; } internal static List GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) { List results = new(); if (!fileSearchFilter.Contains('*')) fileSearchFilter = string.Concat('*', fileSearchFilter); if (!directorySearchFilter.Contains('*')) directorySearchFilter = string.Concat('*', directorySearchFilter); results.Add(Directory.GetFiles(directory, fileSearchFilter, SearchOption.TopDirectoryOnly)); string[] directories = Directory.GetDirectories(directory, directorySearchFilter, SearchOption.TopDirectoryOnly); foreach (string innerDirectory in directories) { try { results.Add(Directory.GetFiles(innerDirectory, fileSearchFilter, SearchOption.AllDirectories)); } catch (UnauthorizedAccessException) { continue; } } int ceilingAverage = directory[^1] == '_' || !results.Any() ? 0 : GetCeilingAverage(results); if (useCeilingAverage) results = GetFilesCollection(results, ceilingAverage); return results; } internal static IReadOnlyDictionary> GetFilesKeyValuePairs(List filesCollection) { Dictionary> results = new(); string fileName; List? collection; foreach (string[] files in filesCollection) { foreach (string file in files) { fileName = Path.GetFileName(file); if (!results.TryGetValue(fileName, out collection)) { results.Add(fileName, new()); if (!results.TryGetValue(fileName, out collection)) throw new Exception(); } collection.Add(file); } } return results; } internal static int LookForAbandoned(List jsonFilesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension) { string fileName; string fileNameWith; List? collection; string fileNameUpperExtension; int length = extension.Length; List renameCollection = new(); foreach (string[] files in jsonFilesCollection) { foreach (string file in files) { fileNameWith = Path.GetFileName(file); if (fileNameWith.Length < length || !fileNameWith.EndsWith(extension)) throw new Exception(); fileName = fileNameWith[..^length]; if (!fileNamesToFiles.TryGetValue(fileName, out collection)) { fileNameUpperExtension = string.Concat(Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName).ToUpper()); if (fileName == fileNameUpperExtension || !fileNamesToFiles.TryGetValue(fileNameUpperExtension, out collection)) renameCollection.Add(file); } } } if (renameCollection.Count > 0) IDirectory.MoveFiles(renameCollection, "{}", "{abd}"); return renameCollection.Count; } private static bool GetIsNotUniqueAndNeedsReview(string file, List collection) { bool result = false; FileInfo possibleFileInfo; FileInfo fileInfo = new(file); foreach (string possible in collection) { if (possible == file) continue; possibleFileInfo = new(possible); if (possibleFileInfo.LastWriteTime != fileInfo.LastWriteTime) File.SetLastWriteTime(file, new DateTime[] { possibleFileInfo.LastWriteTime, fileInfo.LastWriteTime }.Max()); if (possibleFileInfo.LastWriteTime == fileInfo.LastWriteTime && possibleFileInfo.Length == fileInfo.Length) continue; if (!result) result = true; } return result; } private static string? GetMatch(string file, List collection) { string? result = null; FileInfo possibleFileInfo; List lengths = new(); List matches = new(); FileInfo fileInfo = new(file); List creationTimes = new(); foreach (string possible in collection) { possibleFileInfo = new(possible); lengths.Add(possibleFileInfo.Length); creationTimes.Add(possibleFileInfo.CreationTime); if (possibleFileInfo.CreationTime != fileInfo.LastWriteTime) continue; matches.Add(possible); } if (matches.Count == 1 || (matches.Count > 0 && lengths.Distinct().Count() == 1 && creationTimes.Distinct().Count() == 1)) result = matches.First(); return result; } internal static List GetFiles(List filesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension, IReadOnlyDictionary> compareFileNamesToFiles) { List results = new(); string? match; string fileName; bool uniqueFileName; List? collection; bool? isNotUniqueAndNeedsReview; foreach (string[] files in filesCollection) { foreach (string file in files) { isNotUniqueAndNeedsReview = null; fileName = Path.GetFileName(file); if (!fileNamesToFiles.TryGetValue(fileName, out collection)) throw new Exception(); uniqueFileName = collection.Count == 1; if (!uniqueFileName) isNotUniqueAndNeedsReview = GetIsNotUniqueAndNeedsReview(file, collection); if (!compareFileNamesToFiles.TryGetValue(string.Concat(fileName, extension), out collection)) results.Add(new(file, uniqueFileName, isNotUniqueAndNeedsReview, new(), null)); else { if (collection.Count == 0) results.Add(new(file, uniqueFileName, isNotUniqueAndNeedsReview, collection, null)); else if (uniqueFileName && collection.Count == 1) results.Add(new(file, uniqueFileName, isNotUniqueAndNeedsReview, collection, collection.First())); else { match = GetMatch(file, collection); results.Add(new(file, uniqueFileName, isNotUniqueAndNeedsReview, collection, match)); } } } } return results; } private static void IsUniqueLoop(string resultAllInOne, string resultAllInOneDirectory, int resultAllInOneSubdirectoryLength, FilePair item, List<(string, string)> rename) { string fileName; string directoryName; foreach (string path in item.Collection) { if (path.Contains(resultAllInOne)) continue; fileName = Path.GetFileName(path); (directoryName, _) = IPath.GetDirectoryNameAndIndex(resultAllInOneSubdirectoryLength, fileName); rename.Add(new(path, Path.Combine(resultAllInOneDirectory, directoryName, fileName))); } } private static void IsNotUniqueLoop(string directory, string resultAllInOne, string jsonGroupDirectory, string extension, FilePair item, List<(string, string)> rename) { int length = directory.Length; foreach (string path in item.Collection) { if (!path.Contains(resultAllInOne)) continue; if (item.Match is null || path != item.Match) continue; rename.Add(new(path, string.Concat(jsonGroupDirectory, item.Path[length..], extension))); } } internal static int MaybeMove(string directory, string resultAllInOne, int resultAllInOneSubdirectoryLength, List filePairs, string jsonGroupDirectory, string extension) { FileInfo? toFileInfo; FileInfo fromFileInfo; string checkDirectory; List<(string, string)> rename = new(); string resultAllInOneDirectory = Path.Combine(jsonGroupDirectory, resultAllInOne); foreach (FilePair item in filePairs) { if (item.IsUnique) IsUniqueLoop(resultAllInOne, resultAllInOneDirectory, resultAllInOneSubdirectoryLength, item, rename); else IsNotUniqueLoop(directory, resultAllInOne, jsonGroupDirectory, extension, item, rename); } foreach ((string from, string to) in rename) { toFileInfo = null; checkDirectory = to; fromFileInfo = new(from); if (!fromFileInfo.Exists) continue; for (int i = 0; i < int.MaxValue; i++) { toFileInfo = new(checkDirectory); if (toFileInfo.Directory is null) continue; if (!toFileInfo.Directory.Exists) _ = Directory.CreateDirectory(toFileInfo.Directory.FullName); if (checkDirectory.Length > 199) throw new Exception(); if (!toFileInfo.Exists) break; else if (fromFileInfo.Length == toFileInfo.Length && fromFileInfo.LastWriteTime == toFileInfo.LastWriteTime) checkDirectory = string.Concat(checkDirectory, ".del"); else checkDirectory = string.Concat(checkDirectory, ".j"); } File.Move(from, checkDirectory); } return rename.Count; } internal static void MoveFiles(List files, string find, string replace) { string checkFile; string? checkDirectory; List directories = new(); foreach (string file in files) { checkDirectory = Path.GetDirectoryName(file.Replace(find, replace)); if (string.IsNullOrEmpty(checkDirectory) || directories.Contains(checkDirectory)) continue; directories.Add(checkDirectory); } foreach (string directory in directories) { if (Directory.Exists(directory)) continue; _ = Directory.CreateDirectory(directory); } foreach (string file in files) { if (!File.Exists(file)) continue; checkFile = file.Replace(find, replace); if (File.Exists(checkFile)) continue; File.Move(file, checkFile); } } internal static (bool, int?) GetId(short sortOrderOnlyLengthIndex, Models.FileHolder fileHolder) { int? id; short? multiplier; char negativeMarker; int absoluteValueOfId; bool nameWithoutExtensionIsIdFormat = IProperty.NameWithoutExtensionIsIdFormat(fileHolder); bool nameWithoutExtensionIsPaddedIdFormat = IDirectory.NameWithoutExtensionIsPaddedIdFormat(fileHolder, sortOrderOnlyLengthIndex); if (!nameWithoutExtensionIsIdFormat && !nameWithoutExtensionIsPaddedIdFormat) id = null; else if (nameWithoutExtensionIsIdFormat) { if (!int.TryParse(fileHolder.NameWithoutExtension, out absoluteValueOfId)) id = null; else id = absoluteValueOfId; } else { negativeMarker = fileHolder.NameWithoutExtension[sortOrderOnlyLengthIndex - 2]; if (negativeMarker == '7') multiplier = 1; else if (negativeMarker == '3') multiplier = -1; else multiplier = null; if (!int.TryParse(fileHolder.NameWithoutExtension[sortOrderOnlyLengthIndex..], out absoluteValueOfId)) id = null; else { id = absoluteValueOfId * multiplier; if (id is null || !fileHolder.NameWithoutExtension.EndsWith(id.Value.ToString()[1..])) id = null; } } return (nameWithoutExtensionIsIdFormat, id); } private static SortedRecord[] GetSortedRecords(int offset, List filesCollection) { List results = new(); int? id; Models.FileHolder fileHolder; bool nameWithoutExtensionIsIdFormat; short sortOrderOnlyLengthIndex = IDirectory.GetSortOrderOnlyLengthIndex(offset); foreach (string[] files in filesCollection) { foreach (string file in files) { fileHolder = new(file); (nameWithoutExtensionIsIdFormat, id) = GetId(sortOrderOnlyLengthIndex, fileHolder); results.Add(new(fileHolder, nameWithoutExtensionIsIdFormat, id)); } } return (from l in results orderby l.FileHolder.CreationTime, l.FileHolder.FullName.Length descending select l).ToArray(); } internal static (string[], List<(Models.FileHolder, string?, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, List filesCollection, string[] directories, Action? tick) { List<(Models.FileHolder, string?, string)> results = new(); string paddedId; string checkFile; string directory; FileInfo fileInfo; int directoryIndex; string paddedIdFile; bool wrapped = false; string directoryName; bool paddedCheck = false; string fileDirectoryName; SortedRecord sortedRecord; string? alternateCheckFile; string? alternateDirectory; Models.FileHolder fileHolder; List distinctIds = new(); List distinct = new(); List distinctDirectories = new(); int intMinValueLength = int.MinValue.ToString().Length; SortedRecord[] sortedRecords = GetSortedRecords(propertyConfiguration.Offset, filesCollection); string? alternateResultAllInOne = !propertyConfiguration.ResultAllInOne.Contains(' ') ? null : propertyConfiguration.ResultAllInOne.Replace(' ', '-'); for (int i = 0; i < sortedRecords.Length; i++) { tick?.Invoke(); sortedRecord = sortedRecords[i]; fileHolder = sortedRecord.FileHolder; if (fileHolder.Name.EndsWith("len") || fileHolder.ExtensionLowered == ".id" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null) continue; (directoryName, directoryIndex) = IPath.GetDirectoryNameAndIndex(propertyConfiguration.ResultAllInOneSubdirectoryLength, fileHolder.NameWithoutExtension); fileDirectoryName = Path.GetFileName(fileHolder.DirectoryName); if (fileDirectoryName.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength + 3 || !fileHolder.Name.StartsWith(fileDirectoryName)) { if (wrapped) continue; directory = directories[directoryIndex]; if (alternateResultAllInOne is null) alternateDirectory = null; else alternateDirectory = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(directory)) ?? directory, alternateResultAllInOne, directoryName); } else { if (!wrapped) wrapped = true; alternateDirectory = null; directory = Path.Combine(directories[directoryIndex], fileDirectoryName); } if (ifCanUseId && sortedRecord.NameWithoutExtensionIsIdFormat && sortedRecord.Id is not null && fileHolder.DirectoryName is not null) { paddedId = IDirectory.GetPaddedId(intMinValueLength, propertyConfiguration.Offset + i, sortedRecord.Id.Value); paddedIdFile = Path.Combine(fileHolder.DirectoryName, $"{paddedId}{fileHolder.ExtensionLowered}"); if (!File.Exists(paddedIdFile)) { File.Move(fileHolder.FullName, paddedIdFile); fileHolder = new(paddedIdFile); if (!paddedCheck) paddedCheck = true; } } if (ifCanUseId) checkFile = Path.Combine(directory, $"{sortedRecord.Id}{fileHolder.ExtensionLowered}"); else checkFile = Path.Combine(directory, $"{fileHolder.NameWithoutExtension}{fileHolder.ExtensionLowered}"); if ((sortedRecord.Id is not null && distinctIds.Contains(sortedRecord.Id.Value)) || distinct.Contains(checkFile)) { if (string.IsNullOrEmpty(fileHolder.DirectoryName)) continue; if (!copyDuplicates) continue; alternateDirectory = null; for (int j = 1; j < int.MaxValue; j++) { fileInfo = new(checkFile); if (!fileInfo.Exists || fileHolder.Length == fileInfo.Length && fileHolder.LastWriteTime == fileInfo.LastWriteTime) checkFile = Path.Combine(directory, $"{fileHolder.NameWithoutExtension}.{j}dup{fileHolder.ExtensionLowered}"); else checkFile = Path.Combine(directory, $"{fileHolder.NameWithoutExtension}.{j}why{fileHolder.ExtensionLowered}"); if (sortedRecord.Id is not null) { if (distinctIds.Contains(sortedRecord.Id.Value)) continue; distinctIds.Add(sortedRecord.Id.Value); } if (distinct.Contains(checkFile)) continue; alternateCheckFile = null; distinct.Add(checkFile); results.Add(new(fileHolder, alternateCheckFile, checkFile)); if (!distinctDirectories.Contains(directory)) distinctDirectories.Add(directory); break; } continue; } distinct.Add(checkFile); if (sortedRecord.Id is not null) distinctIds.Add(sortedRecord.Id.Value); if (string.IsNullOrEmpty(alternateDirectory)) alternateCheckFile = null; else { alternateCheckFile = Path.Combine(alternateDirectory, $"{sortedRecord.Id}{fileHolder.ExtensionLowered}.tsv"); if (!distinctDirectories.Contains(alternateDirectory)) distinctDirectories.Add(alternateDirectory); } results.Add(new(fileHolder, alternateCheckFile, checkFile)); if (!distinctDirectories.Contains(directory)) distinctDirectories.Add(directory); } if (paddedCheck) throw new Exception("Maybe need to restart application!"); return (distinctDirectories.ToArray(), results); } internal static List CopyOrMove(List<(Models.FileHolder, string?, string)> toDoCollection, bool move, bool moveBack, Action? tick) { List results = new(); FileInfo fileInfo; foreach ((Models.FileHolder fileHolder, string? alternateFile, string to) in toDoCollection) { tick?.Invoke(); fileInfo = new(to); if (!fileInfo.Exists && alternateFile is not null) _ = XPath.WriteAllText(alternateFile, fileHolder.FullName, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); if (fileInfo.Exists) { if (fileHolder.Length != fileInfo.Length || fileHolder.LastWriteTime != fileInfo.LastWriteTime) fileInfo.Delete(); else continue; } results.Add(fileHolder.NameWithoutExtension); try { if (move || moveBack) File.Move(fileHolder.FullName, to); else File.Copy(fileHolder.FullName, to); } catch (Exception) { } } return results; } }