using Microsoft.Extensions.Logging; using System.Collections.ObjectModel; using System.Text; namespace File_Folder_Helper.Day.Q42023; internal static partial class Helper20231222 { private record Record(string File, string DestinationDirectory, string DestinationFile); private record IntelligentIdRecord(int Key, ReadOnlyCollection ResultAllInOneSubdirectoryChars, string Reverse); private record FilePath(long CreationTicks, string DirectoryName, string ExtensionLowered, string FileNameFirstSegment, string FullName, int? Id, bool IsIntelligentIdFormat, long LastWriteTicks, long Length, string Name, string NameWithoutExtension, int? SortOrder); public record MetadataConfiguration(int ResultAllInOneSubdirectoryLength, int Offset, int IntMinValueLength); private static short GetSortOrderOnlyLengthIndex(MetadataConfiguration metadataConfiguration) => (short)metadataConfiguration.Offset.ToString().Length; private static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataConfiguration metadataConfiguration, string fileNameFirstSegment) => fileNameFirstSegment.Length - 1 == metadataConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber); private static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataConfiguration metadataConfiguration, short sortOrderOnlyLengthIndex, string fileNameFirstSegment) => fileNameFirstSegment.Length == metadataConfiguration.IntMinValueLength + sortOrderOnlyLengthIndex + 1 && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber); private static bool NameWithoutExtensionIsIdFormat(MetadataConfiguration metadataConfiguration, string fileNameFirstSegment) { bool result; if (fileNameFirstSegment.Length < 5 || fileNameFirstSegment.Length > metadataConfiguration.IntMinValueLength) result = false; else { bool skipOneAllAreNumbers = fileNameFirstSegment[1..].All(char.IsNumber); result = (skipOneAllAreNumbers && fileNameFirstSegment[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileNameFirstSegment[0])); } return result; } private static FilePath GetFilePath(MetadataConfiguration metadataConfiguration, FileInfo fileInfo, int? index) { FilePath result; int? id; int? sortOder; string fileNameFirstSegment = fileInfo.Name.Split('.')[0]; short sortOrderOnlyLengthIndex = GetSortOrderOnlyLengthIndex(metadataConfiguration); string fileDirectoryName = fileInfo.DirectoryName ?? throw new NullReferenceException(); bool fileNameFirstSegmentIsIntelligentIdFormat = NameWithoutExtensionIsIntelligentIdFormat(metadataConfiguration, fileNameFirstSegment); bool fileNameFirstSegmentIsPaddedIntelligentIdFormat = NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment); bool fileNameFirstSegmentIsIdFormat = !fileNameFirstSegmentIsPaddedIntelligentIdFormat && !fileNameFirstSegmentIsIntelligentIdFormat && NameWithoutExtensionIsIdFormat(metadataConfiguration, fileNameFirstSegment); if (fileNameFirstSegmentIsIdFormat) { if (index is null) throw new NullReferenceException(nameof(index)); if (!int.TryParse(fileNameFirstSegment, out int valueOfFileNameFirstSegment)) throw new NotSupportedException(); (id, sortOder) = (valueOfFileNameFirstSegment, metadataConfiguration.Offset + index); } else if (!fileNameFirstSegmentIsIntelligentIdFormat && !fileNameFirstSegmentIsPaddedIntelligentIdFormat) (id, sortOder) = (null, null); else if (fileNameFirstSegmentIsIntelligentIdFormat) (id, sortOder) = (GetId(metadataConfiguration, fileNameFirstSegment), null); else if (fileNameFirstSegmentIsPaddedIntelligentIdFormat) { if (!int.TryParse(fileNameFirstSegment[..sortOrderOnlyLengthIndex], out int absoluteValueOfSortOrder)) (id, sortOder) = (null, null); else (id, sortOder) = (GetId(metadataConfiguration, fileNameFirstSegment[sortOrderOnlyLengthIndex..]), absoluteValueOfSortOrder); } else throw new NotSupportedException(); result = new(fileInfo.CreationTime.Ticks, fileDirectoryName, fileInfo.Extension.ToLower(), fileNameFirstSegment, fileInfo.FullName, id, fileNameFirstSegmentIsIntelligentIdFormat, fileInfo.LastWriteTime.Ticks, fileInfo.Length, fileInfo.Name, Path.GetFileNameWithoutExtension(fileInfo.Name), sortOder); return result; } private static IntelligentIdRecord GetIntelligentIdRecord(MetadataConfiguration metadataConfiguration, long id, bool ignore) { IntelligentIdRecord result; StringBuilder stringBuilder = new(); if (metadataConfiguration.IntMinValueLength < (metadataConfiguration.ResultAllInOneSubdirectoryLength + 2)) throw new NotSupportedException(); int key; string value; List chars = []; if (id > -1) { key = ignore ? 8 : 9; value = id.ToString().PadLeft(metadataConfiguration.IntMinValueLength, '0'); } else { key = ignore ? 2 : 1; value = id.ToString()[1..].PadLeft(metadataConfiguration.IntMinValueLength, '0'); } for (int i = value.Length - metadataConfiguration.ResultAllInOneSubdirectoryLength - 1; i > -1; i--) _ = stringBuilder.Append(value[i]); for (int i = value.Length - metadataConfiguration.ResultAllInOneSubdirectoryLength; i < value.Length; i++) chars.Add(value[i]); result = new(key, new(chars), stringBuilder.ToString()); return result; } private static string GetIntelligentId(IntelligentIdRecord intelligentId) => $"{intelligentId.Reverse}{string.Join(string.Empty, intelligentId.ResultAllInOneSubdirectoryChars)}{intelligentId.Key}"; private static int GetId(MetadataConfiguration metadataConfiguration, string intelligentId) { int result; StringBuilder results = new(); if (metadataConfiguration.IntMinValueLength < (metadataConfiguration.ResultAllInOneSubdirectoryLength + 2)) throw new NotSupportedException(); for (int i = intelligentId.Length - (metadataConfiguration.ResultAllInOneSubdirectoryLength + 2); i > -1; i--) _ = results.Append(intelligentId[i]); _ = results.Append(intelligentId[^3]).Append(intelligentId[^2]); result = int.Parse(results.ToString()); if (intelligentId[^1] is '1' or '2') result *= -1; else if (intelligentId[^1] is not '9' and not '8') throw new NotSupportedException(); return result; } private static ReadOnlyCollection GetRecords(MetadataConfiguration metadataConfiguration, string sourceDirectory, string searchPattern) { List results = []; int check; int index = -1; FileInfo fileInfo; FilePath filePath; string? directory; string[] segments; bool ignore = false; string directoryName; string intelligentId; string? parentDirectory; IntelligentIdRecord intelligentIdRecord; string sourceParentDirectory = Path.GetDirectoryName(sourceDirectory) ?? throw new NotSupportedException(); string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); foreach (string file in files) { index += 1; directory = Path.GetDirectoryName(file); if (directory is null) continue; parentDirectory = Path.GetDirectoryName(directory); if (parentDirectory is null) continue; fileInfo = new(file); directoryName = Path.GetFileName(directory); filePath = GetFilePath(metadataConfiguration, fileInfo, index); if (filePath.Id is null) continue; intelligentIdRecord = GetIntelligentIdRecord(metadataConfiguration, filePath.Id.Value, ignore); intelligentId = GetIntelligentId(intelligentIdRecord); check = GetId(metadataConfiguration, intelligentId); if (check != filePath.Id.Value) throw new NotSupportedException(); segments = Path.GetFileName(file).Split('.'); if (segments.Length == 2) { if (filePath.SortOrder is not null) results.Add(new(file, directory, $"{filePath.SortOrder.Value}{intelligentId}.{segments[1]}")); else results.Add(new(file, Path.Combine(sourceParentDirectory, intelligentIdRecord.Key.ToString(), string.Join(string.Empty, intelligentIdRecord.ResultAllInOneSubdirectoryChars)), $"{intelligentId}.{segments[1]}")); } else if (segments.Length == 3) results.Add(new(file, Path.Combine(sourceParentDirectory, intelligentIdRecord.Key.ToString(), string.Join(string.Empty, intelligentIdRecord.ResultAllInOneSubdirectoryChars)), $"{intelligentId}.{segments[1]}.{segments[2]}")); else if (segments.Length == 4) { if (directoryName != segments[0]) results.Add(new(file, directory, $"{intelligentId}.{segments[1]}.{segments[2]}.{segments[3]}")); else results.Add(new(file, Path.Combine(sourceParentDirectory, intelligentIdRecord.Key.ToString(), string.Join(string.Empty, intelligentIdRecord.ResultAllInOneSubdirectoryChars), intelligentId), $"{intelligentId}.{segments[1]}.{segments[2]}.{segments[3]}")); } else if (segments.Length == 5) results.Add(new(file, directory, $"{intelligentId}.{segments[1]}.{segments[2]}.{segments[3]}.{segments[4]}")); else continue; } return new(results); } internal static void ConvertId(ILogger logger, List args) { List distinct = []; string searchPattern = args[2]; string sourceDirectory = args[0]; logger.LogInformation("{sourceDirectory}", sourceDirectory); MetadataConfiguration metadataConfiguration = new(2, 1000000, int.MinValue.ToString().Length); ReadOnlyCollection records = GetRecords(metadataConfiguration, sourceDirectory, searchPattern); foreach (Record record in records) { if (distinct.Contains(record.DestinationDirectory)) continue; distinct.Add(record.DestinationDirectory); if (!Directory.Exists(record.DestinationDirectory)) _ = Directory.CreateDirectory(record.DestinationDirectory); } foreach (Record record in records) File.Move(record.File, Path.Combine(record.DestinationDirectory, record.DestinationFile)); } }