using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Text.Json;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Stateless.Methods;
using WindowsShortcutFactory;

namespace View_by_Distance.Map.Models.Stateless;

internal abstract class MapLogic
{

    internal record Duplicate(long PersonKey,
                              int Id,
                              FilePath FilePath,
                              float? Percent);

    internal record MappedFile(long PersonKey,
                               string PersonKeyFormatted,
                               string? PersonDisplayDirectoryName,
                               int? DirectoryNumber,
                               FilePath FilePath);

    internal record PersonKeyFormattedIdThenWholePercentages(string PersonKeyFormatted,
                                                             string? PersonDisplayDirectoryName,
                                                             bool? IsDefault,
                                                             int? LinksCount,
                                                             FilePath MappedFaceFilePath,
                                                             int Id,
                                                             int WholePercentages);

    private static ReadOnlyDictionary<int, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>> GetReadOnly(Dictionary<int, Dictionary<int, List<PersonContainer>>> idThenWholePercentagesToPersonContainerCollection)
    {
        Dictionary<int, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>> results = [];
        List<long> distinct = [];
        List<PersonContainer> personContainers;
        Dictionary<int, ReadOnlyCollection<PersonContainer>> keyValuePairs;
        foreach (KeyValuePair<int, Dictionary<int, List<PersonContainer>>> idTo in idThenWholePercentagesToPersonContainerCollection)
        {
            keyValuePairs = [];
            foreach (KeyValuePair<int, List<PersonContainer>> wholePercentagesTo in idThenWholePercentagesToPersonContainerCollection[idTo.Key])
            {
                distinct.Clear();
                personContainers = [];
                foreach (PersonContainer personContainer in wholePercentagesTo.Value)
                {
                    if (personContainer.Key is null)
                        throw new Exception();
                    if (distinct.Contains(personContainer.Key.Value))
                        continue;
                    personContainers.Add(personContainer);
                }
                keyValuePairs.Add(wholePercentagesTo.Key, new(personContainers));
            }
            results.Add(idTo.Key, new(keyValuePairs));
        }
        return results.AsReadOnly();
    }

    internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName)
    {
        string byValue;
        bool isByMapping;
        bool isBySorting;
        if (by is null)
        {
            isByMapping = false;
            isBySorting = !sortingContainersAny;
            byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Mapping)}Null";
        }
        else
        {
            isByMapping = by == Shared.Models.Stateless.IMapLogic.Mapping;
            isBySorting = by == Shared.Models.Stateless.IMapLogic.Sorting;
            bool isDefaultName = displayDirectoryName is not null && IPerson.IsDefaultName(displayDirectoryName);
            if (isBySorting && displayDirectoryName is null)
                byValue = saveIndividually ? nameof(Shared.Models.Stateless.IMapLogic.Individually) : $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)} Without Person{(distancePermyriad < 2000 ? "-A" : "-Z")}";
            else if (isBySorting && useFiltersCounter.HasValue)
                byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}";
            else
                byValue = Methods.IMapLogic.Get(saveIndividually, forceSingleImageHumanized, by.Value, isDefaultName);
        }
        return new(byValue, isByMapping, isBySorting);
    }

    internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, long? personKey, string? displayDirectoryName) =>
        Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, personKey is null ? null : Shared.Models.Stateless.IMapLogic.Mapping, displayDirectoryName);

    internal static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Item> items)
    {
        Mapping[] results;
        ReadOnlyCollection<Face> faces = GetFaces(items);
        results = GetSelectedMappingCollection(faces);
        return results;
    }

    internal static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Face> faces)
    {
        Mapping[] results;
        IEnumerable<Mapping> collection = from l in faces orderby l.Mapping?.MappingFromItem.Id select l.Mapping;
        results = (from l in collection where l is not null select l).ToArray();
        return results;
    }

    internal static ReadOnlyCollection<Face> GetFaces(ReadOnlyCollection<Item> items)
    {
        List<Face> results = [];
        foreach (Item item in items)
        {
            if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null)
                continue;
            foreach (Face face in item.Faces)
            {
                if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
                    continue;
                results.Add(face);
            }
        }
        return results.AsReadOnly();
    }

    internal static void SaveMappingShortcuts(string mappingDirectory)
    {
        string? shortcutFileName;
        string[] yearDirectories;
        string personKeyFormatted;
        string[] personNameDirectories;
        WindowsShortcut windowsShortcut;
        string personDisplayDirectoryName;
        (string, string)[] yearDirectoryNameCheck;
        List<(string, string)> yearDirectoryNames = [];
        string[] personKeyDirectories = Directory.GetDirectories(mappingDirectory, "*", SearchOption.TopDirectoryOnly);
        foreach (string personKeyDirectory in personKeyDirectories)
        {
            windowsShortcut = new();
            shortcutFileName = null;
            yearDirectoryNames.Clear();
            personKeyFormatted = Path.GetFileName(personKeyDirectory);
            yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
            yearDirectoryNames.AddRange(GetCollection(yearDirectories));
            yearDirectoryNameCheck = (from l in yearDirectoryNames where l.Item2.Contains('^') select l).OrderByDescending(l => l.Item2).ToArray();
            if (yearDirectoryNameCheck.Length == 0)
                yearDirectoryNameCheck = (from l in yearDirectoryNames where l.Item2.Contains('~') select l).OrderByDescending(l => l.Item2).ToArray();
            if (yearDirectoryNameCheck.Length == 0)
                yearDirectoryNameCheck = (from l in yearDirectoryNames where l.Item2.Contains('=') select l).OrderByDescending(l => l.Item2).ToArray();
            if (yearDirectoryNameCheck.Length == 0)
                yearDirectoryNameCheck = (from l in yearDirectoryNames select l).OrderByDescending(l => l).ToArray();
            if (yearDirectoryNameCheck.Length == 0)
                continue;
            foreach ((string yearDirectory, string yearDirectoryName) in yearDirectoryNameCheck)
            {
                personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly);
                foreach (string personNameDirectory in personNameDirectories)
                {
                    personDisplayDirectoryName = Path.GetFileName(personNameDirectory).Split('-')[0];
                    if (personDisplayDirectoryName is null)
                        continue;
                    windowsShortcut.Path = yearDirectory;
                    windowsShortcut.Description = yearDirectoryName;
                    shortcutFileName = Path.Combine(mappingDirectory, $"{personDisplayDirectoryName} [{windowsShortcut.Description}].lnk");
                    break;
                }
                if (shortcutFileName is not null)
                {
                    if (!File.Exists(shortcutFileName))
                        break;
                }
            }
            if (shortcutFileName is null || windowsShortcut.Path is null || windowsShortcut.Description is null)
                continue;
            try
            {
                windowsShortcut.Save(shortcutFileName);
                windowsShortcut.Dispose();
            }
            catch (Exception)
            { }
        }
    }

    private static IEnumerable<(string, string)> GetCollection(string[] yearDirectories)
    {
        foreach (string l in yearDirectories)
            yield return new(l, Path.GetFileName(l));
    }

    internal static ReadOnlyDictionary<int, List<int>> ConvertSkip(Dictionary<int, List<(string, int)>> skipCollection)
    {
        Dictionary<int, List<int>> results = [];
        List<int>? wholePercentagesCollection;
        foreach (KeyValuePair<int, List<(string, int)>> keyValuePair in skipCollection)
        {
            if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection))
            {
                results.Add(keyValuePair.Key, []);
                if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection))
                    throw new Exception();
            }
            foreach ((string _, int wholePercentage) in keyValuePair.Value)
                wholePercentagesCollection.Add(wholePercentage);
        }
        return results.AsReadOnly();
    }

    internal static ReadOnlyDictionary<int, List<long>> GetIdToPersonKeys(ReadOnlyDictionary<long, List<int>> personKeyToIds)
    {
        Dictionary<int, List<long>> results = [];
        List<long>? collection;
        foreach (KeyValuePair<long, List<int>> keyValuePair in personKeyToIds)
        {
            foreach (int id in keyValuePair.Value)
            {
                if (!results.TryGetValue(id, out collection))
                {
                    results.Add(id, []);
                    if (!results.TryGetValue(id, out collection))
                        throw new Exception();
                }
                if (collection.Contains(keyValuePair.Key))
                    continue;
                collection.Add(keyValuePair.Key);
            }
        }
        return results.AsReadOnly();
    }

    internal static SaveContainer GetDebugSaveContainer(SortingContainer sortingContainer, string directory, Mapping keyMapping)
    {
        SaveContainer result;
        string shortcutFile;
        if (sortingContainer?.Source.MappingFromLocation is null)
            throw new NullReferenceException(nameof(sortingContainer.Source.MappingFromLocation));
        FileHolder faceFileHolder = IFileHolder.Get($"C:/{sortingContainer.Sorting.Id}.{sortingContainer.Sorting.WholePercentages}");
        if (keyMapping.MappingFromPerson is not null && keyMapping.MappingFromLocation is not null)
            shortcutFile = Path.Combine(directory, $"{keyMapping.MappingFromLocation.DeterministicHashCodeKey}{keyMapping.MappingFromItem.FilePath.ExtensionLowered}.{sortingContainer.Sorting.DistancePermyriad}.lnk");
        else
            shortcutFile = Path.Combine(directory, $"{sortingContainer.Source.MappingFromLocation.DeterministicHashCodeKey}{sortingContainer.Source.MappingFromItem.FilePath.ExtensionLowered}.{sortingContainer.Sorting.DistancePermyriad}.lnk");
        result = new(directory, faceFileHolder, sortingContainer.Source.MappingFromItem.ResizedFileHolder, shortcutFile);
        return result;
    }

    internal static string GetMappingSegmentB(long ticks, long personKey, int? approximateYears, MappingFromItem mappingFromItem)
    {
        string result;
        DateTime dateTime = mappingFromItem.GetDateTimeOriginalThenMinimumDateTime();
        PersonBirthday personBirthday = IPersonBirthday.GetPersonBirthday(personKey);
        result = GetMappingSegmentB(ticks, personBirthday, approximateYears, dateTime, mappingFromItem.IsWrongYear);
        return result;
    }

    internal static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, MappingFromItem mappingFromItem)
    {
        string result;
        DateTime dateTime = mappingFromItem.GetDateTimeOriginalThenMinimumDateTime();
        result = GetMappingSegmentB(ticks, personBirthday, approximateYears, dateTime, mappingFromItem.IsWrongYear);
        return result;
    }

    private static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, DateTime dateTimeOriginalThenMinimumDateTime, bool? isWrongYear)
    {
        string result = GetMappingSegmentB(ticks, personBirthday, approximateYears, dateTimeOriginalThenMinimumDateTime.Ticks, isWrongYear);
        return result;
    }

    private static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, long dateTimeOriginalThenMinimumDateTimeTicks, bool? isWrongYear)
    {
        int years;
        string result;
        TimeSpan? timeSpan = IPersonBirthday.GetTimeSpan(dateTimeOriginalThenMinimumDateTimeTicks, isWrongYear, personBirthday);
        if (timeSpan.HasValue && timeSpan.Value.Ticks < 0)
            result = "!---";
        else if (timeSpan.HasValue)
        {
            (years, _) = IPersonBirthday.GetAge(dateTimeOriginalThenMinimumDateTimeTicks, personBirthday);
            result = $"^{years:000}";
        }
        else if (approximateYears.HasValue)
        {
            DateTime dateTime = new(ticks);
            (years, _) = IAge.GetAge(dateTimeOriginalThenMinimumDateTimeTicks, dateTime.AddYears(-approximateYears.Value));
            result = $"~{years:000}";
        }
        else
        {
            string isWrongYearFlag = IItem.GetWrongYearFlag(isWrongYear);
            result = $"{isWrongYearFlag}{new DateTime(dateTimeOriginalThenMinimumDateTimeTicks):yyyy}";
        }
        return result;
    }

    internal static ReadOnlyDictionary<int, List<int>> ConvertSkipNotSkip(Dictionary<int, List<(string, int)>> skipNotSkipCollection)
    {
        Dictionary<int, List<int>> results = [];
        List<int>? wholePercentagesCollection;
        foreach (KeyValuePair<int, List<(string, int)>> keyValuePair in skipNotSkipCollection)
        {
            if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection))
            {
                results.Add(keyValuePair.Key, []);
                if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection))
                    throw new Exception();
            }
            foreach ((string _, int wholePercentage) in keyValuePair.Value)
                wholePercentagesCollection.Add(wholePercentage);
        }
        return results.AsReadOnly();
    }

    internal static ReadOnlyDictionary<int, List<LocationContainer>> ConvertLocationContainers(List<LocationContainer> locationContainers)
    {
        Dictionary<int, List<LocationContainer>> results = [];
        foreach (LocationContainer locationContainer in locationContainers)
        {
            if (!results.ContainsKey(locationContainer.Id))
                results.Add(locationContainer.Id, []);
            results[locationContainer.Id].Add(locationContainer);
        }
        return results.AsReadOnly();
    }

    internal static void CheckCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string? rootDirectoryParent)
    {
        string json;
        string fullPath;
        List<KeyValuePair<int, int[]>>? collection;
        foreach (string propertyContentCollectionFile in propertyConfiguration.PropertyContentCollectionFiles)
        {
            fullPath = Path.GetFullPath(string.Concat(rootDirectoryParent, propertyContentCollectionFile));
            if (fullPath.Contains(propertyConfiguration.RootDirectory))
                continue;
            if (!File.Exists(fullPath))
                continue;
            json = File.ReadAllText(fullPath);
            collection = JsonSerializer.Deserialize<List<KeyValuePair<int, int[]>>>(json);
            if (collection is null)
                throw new NullReferenceException(nameof(collection));
        }
    }

    internal static string? GetFacePartsContentCollectionFile(string extension, string d2FacePartsContentCollectionDirectory, MappingFromItem mappingFromItem)
    {
        string? result;
        string? directoryName = Path.GetDirectoryName(mappingFromItem.RelativePath);
        if (directoryName is null)
            result = null;
        else
            result = Path.Combine($"{d2FacePartsContentCollectionDirectory}{directoryName}", $"{mappingFromItem.FilePath.Name}{extension}");
        return result;
    }

    internal static string GetResizeContentDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string cContentDirectory, FilePath filePath)
    {
        string result;
        CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath);
        result = Path.Combine(cContentDirectory, cei.Combined);
        return result;
    }

    internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> GetIdToWholePercentagesToFace(ReadOnlyCollection<Mapping> distinctValidImageMappingCollection)
    {
        Dictionary<int, Dictionary<int, Mapping>> results = [];
        Dictionary<int, Mapping>? keyValuePairs;
        foreach (Mapping mapping in distinctValidImageMappingCollection)
        {
            if (mapping.MappingFromLocation is null)
                continue;
            if (!results.TryGetValue(mapping.MappingFromItem.Id, out keyValuePairs))
            {
                results.Add(mapping.MappingFromItem.Id, []);
                if (!results.TryGetValue(mapping.MappingFromItem.Id, out keyValuePairs))
                    throw new Exception();
            }
            if (keyValuePairs.ContainsKey(mapping.MappingFromLocation.WholePercentages))
                continue;
            keyValuePairs.Add(mapping.MappingFromLocation.WholePercentages, mapping);
        }
        return GetReadOnly(results);
    }

    private static ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> GetReadOnly(Dictionary<int, Dictionary<int, Mapping>> keyValuePairs)
    {
        Dictionary<int, ReadOnlyDictionary<int, Mapping>> results = [];
        foreach (KeyValuePair<int, Dictionary<int, Mapping>> keyValuePair in keyValuePairs)
            results.Add(keyValuePair.Key, new(keyValuePair.Value));
        return results.AsReadOnly();
    }

    internal static int CopyManualFiles(Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string eDistanceContentTicksDirectory)
    {
        int result = 0;
        string checkFile;
        string dateDirectory;
        string directoryName;
        string checkDirectory;
        string personKeyFormatted;
        List<string> distinct = [];
        PersonBirthday personBirthday;
        DateTime dateTime = new(ticks);
        string by = nameof(Shared.Models.Stateless.IMapLogic.ManualCopy);
        foreach (PersonContainer personContainer in personContainers)
        {
            if (personContainer.Key is null)
                continue;
            for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--)
            {
                if (personContainer.DisplayDirectoryAllFilePaths[i].ExtensionLowered != configuration.FacesFileNameExtension)
                    continue;
                if (distinct.Contains(personContainer.DisplayDirectoryAllFilePaths[i].Name))
                    continue;
                distinct.Add(personContainer.DisplayDirectoryAllFilePaths[i].Name);
                directoryName = Path.GetFileName(personContainer.DisplayDirectoryAllFilePaths[i].DirectoryFullPath);
                if (directoryName != personContainer.DisplayDirectoryName)
                    continue;
                personBirthday = IPersonBirthday.GetPersonBirthday(personContainer.Key.Value);
                personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday);
                dateDirectory = Path.Combine(eDistanceContentTicksDirectory, by, personKeyFormatted, dateTime.ToString("yyyy"));
                checkDirectory = Path.Combine(dateDirectory, personContainer.DisplayDirectoryName);
                if (!Directory.Exists(checkDirectory))
                    _ = Directory.CreateDirectory(checkDirectory);
                checkFile = Path.Combine(dateDirectory, personContainer.DisplayDirectoryAllFilePaths[i].Name);
                if (File.Exists(checkFile))
                    continue;
                File.Move(personContainer.DisplayDirectoryAllFilePaths[i].FullName, checkFile);
                throw new NotImplementedException("readonly null?");
                // personContainer.DisplayDirectoryAllFilePaths[i] = null;
                // result++;
            }
        }
        return result;
    }

    internal static string GetFacePartsDirectoryX(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string d2FacePartsContentDirectory, FilePath filePath)
    {
        string result;
        CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath);
        result = Path.Combine(d2FacePartsContentDirectory, cei.Combined, filePath.NameWithoutExtension);
        MoveIf(filePath.NameWithoutExtension, cei, d2FacePartsContentDirectory, result);
        return result;
    }

    private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, string fullFileName)
    {
        string[] segments = directory.Split(cei.Combined);
        string? checkDirectory = segments.Length == 1 ?
            Path.Combine(segments[0], $"{cei.Combined[2..]}") :
                segments.Length == 2 ?
                    $"{segments[0]}{cei.Combined[2..]}{segments[1]}" :
                        null;
        if (checkDirectory is not null && Directory.Exists(checkDirectory))
        {
            string checkFile = Path.Combine(checkDirectory, fileName);
            if (File.Exists(checkFile))
                File.Move(checkFile, fullFileName);
        }
    }

    internal static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages)
    {
        bool? result;
        ReadOnlyCollection<PersonContainer>? personContainers;
        if (wholePercentagesToPersonContainers is null)
            result = null;
        else
        {
            if (!wholePercentagesToPersonContainers.TryGetValue(wholePercentages, out personContainers))
                result = null;
            else
            {
                result = false;
                foreach (PersonContainer personContainer in personContainers)
                {
                    if (!IPerson.IsDefaultName(personContainer) || personContainer.Key is null || !IPersonBirthday.IsCounterPersonYear(new DateTime(personContainer.Key.Value).Year) || jLinkResolvedPersonKeys.Contains(personContainer.Key.Value))
                        continue;
                    result = true;
                    break;
                }
            }
        }
        return result;
    }

    internal static ReadOnlyCollection<PersonKeyFormattedIdThenWholePercentages> GetPersonKeyFormattedIdThenWholePercentages(Configuration configuration, long ticks, List<Record> records)
    {
        List<PersonKeyFormattedIdThenWholePercentages> results = [];
        int? wholePercentages;
        List<int> wholePercentagesCollection;
        Dictionary<int, List<int>> idToWholePercentagesCollection = [];
        PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages;
        int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
        string message = $") {records.Count:000} join from ticks Director(ies) - C - {totalSeconds} total second(s)";
        ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
        using ProgressBar progressBar = new(records.Count, message, options);
        foreach (Record record in records)
        {
            progressBar.Tick();
            if (record.MappedFaceFilePath.Id is null)
                continue;
            wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, record.MappedFaceFilePath);
            if (wholePercentages is null)
                continue;
            if (!idToWholePercentagesCollection.ContainsKey(record.MappedFaceFilePath.Id.Value))
                idToWholePercentagesCollection.Add(record.MappedFaceFilePath.Id.Value, []);
            wholePercentagesCollection = idToWholePercentagesCollection[record.MappedFaceFilePath.Id.Value];
            wholePercentagesCollection.Add(wholePercentages.Value);
            idToWholePercentagesCollection[record.MappedFaceFilePath.Id.Value].Add(wholePercentages.Value);
            personKeyFormattedIdThenWholePercentages = new(PersonKeyFormatted: record.PersonKeyFormatted,
                                                           PersonDisplayDirectoryName: record.PersonDisplayDirectoryName,
                                                           IsDefault: record.IsDefault,
                                                           LinksCount: record.LinksCount,
                                                           MappedFaceFilePath: record.MappedFaceFilePath,
                                                           Id: record.MappedFaceFilePath.Id.Value,
                                                           WholePercentages: wholePercentages.Value);
            results.Add(personKeyFormattedIdThenWholePercentages);
        }
        return results.AsReadOnly();
    }

    internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, Mapping mapping) =>
        Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, mapping.By, mapping.MappingFromPerson?.DisplayDirectoryName);

    internal static string GetFacesDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string dFacesContentDirectory, FilePath filePath, MappingFromItem mappingFromItem)
    {
        string result;
        CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath);
        result = Path.Combine(dFacesContentDirectory, cei.Combined, mappingFromItem.FilePath.NameWithoutExtension);
        MoveIf(mappingFromItem.FilePath.NameWithoutExtension, cei, dFacesContentDirectory, result);
        return result;
    }

    internal static string GetFacePartsDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string d2FacePartsContentDirectory, FilePath filePath, MappingFromItem mappingFromItem)
    {
        string result;
        CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath);
        result = Path.Combine(d2FacePartsContentDirectory, cei.Combined, mappingFromItem.FilePath.NameWithoutExtension);
        MoveIf(mappingFromItem.FilePath.NameWithoutExtension, cei, d2FacePartsContentDirectory, result);
        return result;
    }

    internal static List<PersonContainer> GetNotMappedPersonContainers(Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, ReadOnlyDictionary<long, int> personKeyToCount)
    {
        List<PersonContainer> results = [];
        List<PersonContainer> notMappedAndNotNamedPersonKeys = [];
        List<PersonContainer> notMappedAndWithNamedPersonKeys = [];
        List<long> personKeys = IPersonContainer.GetPersonKeys(personContainers);
        foreach (PersonContainer personContainer in personContainers)
        {
            if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0)
                continue;
            if (personKeys.Contains(personContainer.Key.Value))
                continue;
            if (personKeyToCount.ContainsKey(personContainer.Key.Value))
                continue;
            if (string.IsNullOrEmpty(personContainer.DisplayDirectoryName) || personContainer.DisplayDirectoryName == configuration.MappingDefaultName)
                notMappedAndNotNamedPersonKeys.Add(personContainer);
            else
                notMappedAndWithNamedPersonKeys.Add(personContainer);
        }
        results.AddRange(notMappedAndNotNamedPersonKeys);
        if (results.Count == 0)
            results.AddRange(GetNonSpecificPeopleCollection(configuration, ticks, personKeys, personKeyToCount));
        return results;
    }

    private static List<PersonContainer> GetNonSpecificPeopleCollection(Configuration configuration, long ticks, List<long> personKeys, ReadOnlyDictionary<long, int> personKeyToCount)
    {
        List<PersonContainer> results = [];
        bool check;
        long personKey;
        int? approximateYears = null;
        PersonBirthday personBirthday;
        PersonContainer personContainer;
        List<FilePath> personDisplayDirectoryAllFilePaths = [];
        DateTime incrementDate = new(configuration.PersonBirthdayFirstYear, 1, 1);
        long oneHour = new DateTime(1, 1, 1, 1, 0, 0).Ticks - new DateTime(1, 1, 1).Ticks;
        for (int i = 0; i < int.MaxValue; i++)
        {
            check = false;
            personKey = incrementDate.Ticks;
            incrementDate = incrementDate.AddDays(1);
            if (incrementDate.Ticks > ticks)
                break;
            if (personKeys.Contains(personKey))
                continue;
            if (personKeyToCount.ContainsKey(personKey))
                continue;
            for (int j = 1; j < 24; j++)
            {
                if (personKeys.Contains(personKey + (oneHour * j)))
                {
                    check = true;
                    break;
                }
                if (personKeyToCount.ContainsKey(personKey + (oneHour * j)))
                {
                    check = true;
                    break;
                }
            }
            if (check)
                continue;
            personBirthday = IPersonBirthday.GetPersonBirthday(personKey + (oneHour * 2));
            personContainer = PersonContainer.Get(approximateYears, [personBirthday], new(personDisplayDirectoryAllFilePaths), configuration.MappingDefaultName, personKey);
            results.Add(personContainer);
            if (results.Count > 99)
                break;
        }
        return results;
    }

    internal static (SaveContainer, SaveContainer?) GetContainers(string facesFileNameExtension, string facePartsFileNameExtension, string directory, FileHolder faceFileHolder, FileHolder facePartsFileHolder, Mapping mapping)
    {
        string checkFile;
        SaveContainer? saveContainer;
        if (mapping.MappingFromLocation is null)
            throw new NullReferenceException(nameof(mapping.MappingFromLocation));
        checkFile = Path.Combine(directory, $"{mapping.MappingFromItem.FilePath.Name}{facePartsFileNameExtension}");
        saveContainer = !facePartsFileHolder.Exists ? null : new(checkFile, directory, facePartsFileHolder);
        checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}");
        return (new(checkFile, directory, faceFileHolder), saveContainer);
    }

    internal static List<MappedFile> GetMappedFiles(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyCollection<PersonContainer> personContainers, List<Record> records)
    {
        List<MappedFile> results = [];
        long personKey;
        string checkFile;
        FilePath filePath;
        FileHolder fileHolder;
        MappedFile mappedFile;
        List<string> distinct = [];
        PersonBirthday? personBirthday;
        PersonContainer? personContainer;
        string? personDisplayDirectoryName;
        ReadOnlyDictionary<long, PersonContainer> keyValuePairs = PersonContainer.Extract(personContainers);
        results.AddRange(GetDisplayDirectoryAllFiles(configuration.FacesFileNameExtension, configuration.PersonBirthdayFormat, personContainers));
        foreach (Record record in records)
        {
            personBirthday = IPersonBirthday.GetPersonBirthday(configuration.PersonBirthdayFormat, record.PersonKeyFormatted);
            if (personBirthday is null)
                continue;
            if (distinct.Contains(record.MappedFaceFilePath.Name))
                continue;
            personKey = personBirthday.Value.Ticks;
            distinct.Add(record.MappedFaceFilePath.Name);
            if (!keyValuePairs.TryGetValue(personKey, out personContainer))
                personDisplayDirectoryName = record.PersonDisplayDirectoryName;
            else
                personDisplayDirectoryName = personContainer.DisplayDirectoryName;
            mappedFile = new(PersonKey: personKey,
                             PersonKeyFormatted: record.PersonKeyFormatted,
                             PersonDisplayDirectoryName: personDisplayDirectoryName,
                             DirectoryNumber: record.DirectoryNumber,
                             FilePath: record.MappedFaceFilePath);
            results.Add(mappedFile);
        }
        for (int i = results.Count - 1; i > -1; i--)
        {
            filePath = results[i].FilePath;
            if (filePath.Name.EndsWith(".old"))
            {
                results.RemoveAt(i);
                continue;
            }
            if (!filePath.Name.EndsWith(".abd") && !filePath.Name.EndsWith(".brt") && !filePath.Name.EndsWith(".dup") && !filePath.Name.EndsWith(".unk"))
                continue;
            if (!File.Exists(filePath.FullName))
                continue;
            checkFile = filePath.FullName[..^4];
            if (File.Exists(checkFile))
                continue;
            File.Move(filePath.FullName, checkFile);
            fileHolder = IFileHolder.Get(checkFile);
            filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
            results[i] = new(PersonKey: results[i].PersonKey,
                             PersonKeyFormatted: results[i].PersonKeyFormatted,
                             PersonDisplayDirectoryName: results[i].PersonDisplayDirectoryName,
                             DirectoryNumber: results[i].DirectoryNumber,
                             FilePath: filePath);
        }
        return results;
    }

    private static List<MappedFile> GetDisplayDirectoryAllFiles(string fileNameExtension, string personBirthdayFormat, ReadOnlyCollection<PersonContainer> personContainers)
    {
        List<MappedFile> results = [];
        MappedFile mappedFile;
        string personKeyFormatted;
        List<string> distinct = [];
        foreach (PersonContainer personContainer in personContainers)
        {
            if (personContainer.Key is null)
                continue;
            for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--)
            {
                if (personContainer.DisplayDirectoryAllFilePaths[i].ExtensionLowered != fileNameExtension)
                    continue;
                if (distinct.Contains(personContainer.DisplayDirectoryAllFilePaths[i].Name))
                    continue;
                distinct.Add(personContainer.DisplayDirectoryAllFilePaths[i].Name);
                personKeyFormatted = IPersonBirthday.GetFormatted(personBirthdayFormat, personContainer.Key.Value);
                mappedFile = new(PersonKey: personContainer.Key.Value,
                                 PersonKeyFormatted: personKeyFormatted,
                                 PersonDisplayDirectoryName: personContainer.DisplayDirectoryName,
                                 DirectoryNumber: null,
                                 FilePath: personContainer.DisplayDirectoryAllFilePaths[i]);
                results.Add(mappedFile);
            }
        }
        return results;
    }

    internal static List<(string, long)> GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory)
    {
        List<(string, long)> results;
        string[] files;
        string? foundPath;
        int totalFiles = 0;
        string checkDirectory;
        WindowsShortcut windowsShortcut;
        List<string> resolvedDirectories = [];
        if (string.IsNullOrEmpty(genealogicalDataCommunicationFile))
            results = GetDirectoryAndTicksCollection(jLinks, personBirthdayFormat, a2PeopleContentDirectory);
        else
            results = GetGenealogicalDataCommunicationDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat);
        if (results.Count == 0 || results.Count < jLinks.Length)
        {
            List<(string, string, string)> a2PeopleSingletonDirectories = [];
            foreach (string directory in Directory.GetDirectories(a2PeopleSingletonDirectory, "*", SearchOption.AllDirectories))
                a2PeopleSingletonDirectories.Add((directory, Path.GetFileName(directory), Path.GetFileName(directory).Split(personCharacters).First()));
            foreach (string directoryName in jLinks)
            {
                checkDirectory = Path.Combine(a2PeopleContentDirectory, directoryName);
                if (!Directory.Exists(checkDirectory))
                    continue;
                files = Directory.GetFiles(checkDirectory, "*.lnk", SearchOption.TopDirectoryOnly);
                totalFiles += files.Length;
                foreach (string file in files)
                {
                    windowsShortcut = WindowsShortcut.Load(file);
                    if (windowsShortcut.Path is null)
                        continue;
                    if (Directory.Exists(windowsShortcut.Path))
                        resolvedDirectories.Add(windowsShortcut.Path);
                    else
                    {
                        foundPath = TryToFind(personCharacters, a2PeopleSingletonDirectory, a2PeopleSingletonDirectories, file, windowsShortcut.Path);
                        if (string.IsNullOrEmpty(foundPath))
                            continue;
                        resolvedDirectories.Add(foundPath);
                    }
                }
            }
            if (totalFiles == resolvedDirectories.Count)
                results = GetJLinkResolvedDirectories(personBirthdayFormat, resolvedDirectories);
            else
            {
                resolvedDirectories.Clear();
                results = [];
            }
        }
        return results;
    }

    private static List<(string, long)> GetDirectoryAndTicksCollection(string[] jLinks, string personBirthdayFormat, string? rootDirectory)
    {
        List<(string, long)> results = [];
        string directory;
        DateTime dateTime;
        string[] personKeyDirectories;
        string[] personDisplayDirectoryNames;
        string personKeyFormattedDirectoryName;
        foreach (string jLink in jLinks)
        {
            if (rootDirectory is null)
                continue;
            directory = Path.Combine(rootDirectory, jLink);
            if (!Directory.Exists(directory))
                continue;
            personDisplayDirectoryNames = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly);
            foreach (string personDisplayDirectoryName in personDisplayDirectoryNames)
            {
                personKeyDirectories = Directory.GetDirectories(personDisplayDirectoryName, "*", SearchOption.TopDirectoryOnly);
                foreach (string personKeyFormattedDirectory in personKeyDirectories)
                {
                    personKeyFormattedDirectoryName = Path.GetFileName(personKeyFormattedDirectory);
                    if (personKeyFormattedDirectoryName.Length != personBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormattedDirectoryName, personBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
                        continue;
                    results.Add((directory, dateTime.Ticks));
                }
            }
        }
        return results;
    }

    private static List<(string, long)> GetGenealogicalDataCommunicationDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat)
    {
        List<(string, long)> results;
        string? genealogicalDataCommunicationDirectory = Path.GetDirectoryName(genealogicalDataCommunicationFile);
        results = GetDirectoryAndTicksCollection(jLinks, personBirthdayFormat, genealogicalDataCommunicationDirectory);
        return results;
    }

    private static string? TryToFind(char[] personCharacters, string a2PeopleSingletonDirectory, List<(string Directory, string DirectoryName, string DirectoryNameSplitFirst)> a2PeopleSingletonDirectories, string file, string path)
    {
        string? result;
        string? pathName = Path.GetFileName(path);
        string? group = Path.GetDirectoryName(path);
        string? groupName = Path.GetFileName(group);
        if (pathName is null || group is null || groupName is null)
            result = null;
        else
        {
            string[] matches;
            matches = (from l in a2PeopleSingletonDirectories where l.DirectoryName == pathName select l.Directory).ToArray();
            if (matches.Length == 1)
                result = matches.First();
            else
            {
                string pathNameSplitFirst = pathName.Split(personCharacters).First();
                matches = (from l in a2PeopleSingletonDirectories where l.DirectoryNameSplitFirst == pathNameSplitFirst select l.Directory).ToArray();
                if (matches.Length == 1)
                    result = matches.First();
                else
                {
                    string checkDirectory = Path.Combine(a2PeopleSingletonDirectory, groupName, pathName);
                    if (!Directory.Exists(checkDirectory))
                        result = null;
                    else
                        result = checkDirectory;
                }
            }
            if (!string.IsNullOrEmpty(result))
            {
                try
                {
                    WindowsShortcut windowsShortcut;
                    windowsShortcut = new() { Path = result };
                    windowsShortcut.Save(file);
                    windowsShortcut.Dispose();
                }
                catch (Exception)
                { }
            }
        }
        return result;
    }

    private static List<(string, long)> GetJLinkResolvedDirectories(string personBirthdayFormat, List<string> resolvedDirectories)
    {
        List<(string, long)> results = [];
        DateTime dateTime;
        string directoryName;
        string[] directories;
        foreach (string resolvedDirectory in resolvedDirectories)
        {
            directories = Directory.GetDirectories(resolvedDirectory, "*", SearchOption.TopDirectoryOnly);
            foreach (string directory in directories)
            {
                directoryName = Path.GetFileName(directory);
                if (directoryName.Length != personBirthdayFormat.Length || !DateTime.TryParseExact(directoryName, personBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
                    continue;
                results.Add((resolvedDirectory, dateTime.Ticks));
            }
        }
        if (results.Count != resolvedDirectories.Count)
            results.Clear();
        return results;
    }

    internal static void SetPersonCollectionsAfterSetSkipCollections(Configuration configuration, ReadOnlyCollection<PersonContainer> personContainers, Dictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted, List<string> personKeyFormattedCollection)
    {
        string personKeyFormatted;
        string newestPersonKeyFormatted;
        foreach (PersonContainer personContainer in personContainers)
        {
            if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0)
                continue;
            foreach (PersonBirthday personBirthday in personContainer.Birthdays)
            {
                personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday);
                personKeyFormattedCollection.Add(personKeyFormatted);
                if (personContainer.Birthdays.Length < 1)
                    continue;
                newestPersonKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personContainer.Key.Value);
                _ = personKeyFormattedToNewestPersonKeyFormatted.TryAdd(personKeyFormatted, newestPersonKeyFormatted);
            }
        }
    }

    internal static void SetSkipCollections(Configuration configuration, ReadOnlyCollection<PersonContainer> personContainers, string? a2PeopleSingletonDirectory, Dictionary<int, List<(string, int)>> skipCollection, Dictionary<int, List<(string, int)>> skipNotSkipCollection)
    {
        string checkFile;
        int? wholePercentages;
        List<FilePath> distinct = [];
        List<string> distinctFiles = [];
        List<string> distinctFileName = [];
        bool skipNotSkipDirectoriesAny = configuration.SkipNotSkipDirectories.Length > 0;
        string[] checkDirectories = (from l in configuration.SkipNotSkipDirectories select Path.GetFullPath($"{a2PeopleSingletonDirectory}{l}")).ToArray();
        foreach (PersonContainer personContainer in personContainers)
        {
            foreach (FilePath personDisplayDirectoryAllFilePath in personContainer.DisplayDirectoryAllFilePaths)
            {
                if (personDisplayDirectoryAllFilePath.ExtensionLowered != configuration.FacesFileNameExtension)
                    continue;
                if (distinctFiles.Contains(personDisplayDirectoryAllFilePath.FullName))
                    continue;
                distinctFiles.Add(personDisplayDirectoryAllFilePath.FullName);
                distinct.Add(personDisplayDirectoryAllFilePath);
            }
        }
        foreach (FilePath filePath in distinct)
        {
            if (distinctFileName.Contains(filePath.Name))
            {
                checkFile = $"{filePath.FullName}.dup";
                if (File.Exists(checkFile))
                    continue;
                File.Move(filePath.FullName, checkFile);
                continue;
            }
            if (filePath.Id is null)
                continue;
            wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath);
            if (wholePercentages is null)
                continue;
            if (!skipNotSkipDirectoriesAny || !checkDirectories.Any(filePath.FullName.StartsWith))
            {
                if (!skipCollection.ContainsKey(filePath.Id.Value))
                    skipCollection.Add(filePath.Id.Value, []);
                skipCollection[filePath.Id.Value].Add((filePath.FullName, wholePercentages.Value));
            }
            else
            {
                if (!skipNotSkipCollection.ContainsKey(filePath.Id.Value))
                    skipNotSkipCollection.Add(filePath.Id.Value, []);
                skipNotSkipCollection[filePath.Id.Value].Add((filePath.FullName, wholePercentages.Value));
            }
        }
    }

    internal static (SaveContainer?, SaveContainer?) GetContainers(string facesFileNameExtension, string facePartsFileNameExtension, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string dFacesContentDirectory, string d2FacePartsContentCollectionDirectory, string directory, Mapping keyMapping)
    {
        SaveContainer? result;
        SaveContainer? saveContainer;
        if (keyMapping.MappingFromLocation is null)
            (result, saveContainer) = (null, null);
        else
        {
            string? facePartsContentCollectionFile = GetFacePartsContentCollectionFile(facePartsFileNameExtension, d2FacePartsContentCollectionDirectory, keyMapping.MappingFromItem);
            if (facePartsContentCollectionFile is null || !File.Exists(facePartsContentCollectionFile))
                result = null;
            else
            {
                string checkFile = Path.Combine(directory, $"{keyMapping.MappingFromItem.FilePath.Name}{facePartsFileNameExtension}");
                result = new(checkFile, directory, IFileHolder.Get(facePartsContentCollectionFile));
            }
            string facesDirectory = GetFacesDirectory(propertyConfiguration, dFacesContentDirectory, keyMapping.FilePath, keyMapping.MappingFromItem);
            FileHolder faceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{keyMapping.MappingFromLocation.DeterministicHashCodeKey}{keyMapping.MappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}"));
            if (!faceFileHolder.Exists)
                saveContainer = null;
            else
            {
                string checkFile = Path.Combine(directory, $"{keyMapping.MappingFromLocation.DeterministicHashCodeKey}{keyMapping.MappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}");
                saveContainer = new(checkFile, directory, faceFileHolder);
            }
        }
        return (result, saveContainer);
    }

    internal static void PossiblyRebuildPersonContainers(Configuration configuration, long ticks, string? a2PeopleSingletonDirectory, ReadOnlyDictionary<long, int> readOnlyPersonKeyToCount, ReadOnlyCollection<(PersonKeyFormattedIdThenWholePercentages, PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer)
    {
        int count;
        long personKey;
        bool[] matches;
        string fileName;
        string checkFile;
        const int zero = 0;
        string personKeyFormatted;
        List<string> distinct = [];
        FilePath[] deleteCollection;
        PersonBirthday personBirthday;
        string personDisplayDirectory;
        DateTime dateTime = new(ticks);
        string personKeyFormattedDirectory;
        foreach ((PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages, PersonContainer personContainer) in possiblyNewPersonDisplayDirectoryNamesAndPersonContainer)
        {
            if (distinct.Contains(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted))
                continue;
            if (a2PeopleSingletonDirectory is null || personContainer.Key is null || personContainer.Birthdays is null || personContainer.PersonDirectory is null || personContainer.Birthdays.Length == 0)
                continue;
            fileName = $"{Path.GetFileName(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName)}{configuration.FacesHiddenFileNameExtension}";
            personBirthday = personContainer.Birthdays[zero];
            personKey = personBirthday.Value.Ticks;
            personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday);
            personDisplayDirectory = Path.Combine(a2PeopleSingletonDirectory, personContainer.PersonDirectory.Char.ToString(), personContainer.PersonDirectory.Group, personContainer.DisplayDirectoryName);
            personKeyFormattedDirectory = Path.GetFullPath(Path.Combine(personDisplayDirectory, personKeyFormatted));
            deleteCollection = (from l in personContainer.DisplayDirectoryAllFilePaths where l.FullName.StartsWith(personKeyFormattedDirectory) select l).ToArray();
            if (personContainer.DisplayDirectoryAllFilePaths.Count != 0 && deleteCollection.Length == 0)
                throw new NotSupportedException();
            if (!Directory.Exists(personKeyFormattedDirectory))
                _ = Directory.CreateDirectory(personKeyFormattedDirectory);
            _ = readOnlyPersonKeyToCount.TryGetValue(personKey, out count);
            _ = Directory.CreateDirectory(Path.Combine(personDisplayDirectory, count.ToString("0000")));
            Directory.SetLastWriteTime(personDisplayDirectory, dateTime.AddMinutes(count));
            matches = (from l in personContainer.DisplayDirectoryAllFilePaths where l.FullName.EndsWith(fileName) select true).ToArray();
            if (matches.Length > 0)
                continue;
            matches = (from l in personContainer.DisplayDirectoryAllFilePaths where l.FullName.EndsWith(configuration.FacesHiddenFileNameExtension) select true).ToArray();
            if (matches.Length > 0)
                continue;
            if (!File.Exists(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName))
                continue;
            checkFile = Path.Combine(personKeyFormattedDirectory, $"{Path.GetFileName(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName)}{configuration.FacesHiddenFileNameExtension}");
            if (File.Exists(checkFile))
                continue;
            File.Copy(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName, checkFile);
            foreach (FilePath delete in deleteCollection)
            {
                if (delete.ExtensionLowered == ".lnk")
                    continue;
                if (!File.Exists(delete.FullName))
                    continue;
                File.Delete(delete.FullName);
            }
            Directory.SetLastWriteTime(personDisplayDirectory, DateTime.Now);
            distinct.Add(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted);
        }
    }

    internal static void SetPersonKeyToPersonContainer(Configuration configuration, ReadOnlyCollection<PersonContainer> personContainers, ReadOnlyDictionary<long, int> personKeyToCount, Dictionary<long, PersonContainer> personKeyToPersonContainer, ReadOnlyDictionary<long, List<PersonContainer>> personKeyToPersonContainerCollection)
    {
        string? displayDirectoryName;
        foreach (PersonContainer personContainer in personContainers)
        {
            if (personContainer.Key is null || !personKeyToCount.ContainsKey(personContainer.Key.Value))
                continue;
            displayDirectoryName = GetDisplayDirectoryName(personKeyToPersonContainer, personContainer.Key.Value);
            if (displayDirectoryName is not null && (displayDirectoryName == personContainer.DisplayDirectoryName || (displayDirectoryName[0] == personContainer.DisplayDirectoryName[0] && (displayDirectoryName.Length == 1 || personContainer.DisplayDirectoryName.Length == 1))))
                continue;
            personKeyToPersonContainer.Add(personContainer.Key.Value, personContainer);
        }
        if (personKeyToCount.Count > 0)
        {
            const int zero = 0;
            int? approximateYears = null;
            PersonBirthday? personBirthday;
            PersonContainer personContainer;
            List<PersonContainer>? collection;
            displayDirectoryName = configuration.MappingDefaultName;
            foreach (KeyValuePair<long, int> keyValuePair in personKeyToCount)
            {
                if (personKeyToPersonContainer.ContainsKey(keyValuePair.Key))
                    continue;
                personBirthday = IPersonBirthday.GetPersonBirthday(keyValuePair.Key);
                if (!personKeyToPersonContainerCollection.TryGetValue(keyValuePair.Key, out collection))
                    personContainer = PersonContainer.Get(approximateYears, personBirthday, displayDirectoryName, keyValuePair.Key);
                else
                    personContainer = PersonContainer.Get(approximateYears, personBirthday, collection[zero].PersonDirectory, displayDirectoryName, keyValuePair.Key);
                personKeyToPersonContainer.Add(keyValuePair.Key, personContainer);
            }
        }
    }

    private static string? GetDisplayDirectoryName(Dictionary<long, PersonContainer> personKeyToPersonContainer, long key)
    {
        string? result = null;
        if (personKeyToPersonContainer.TryGetValue(key, out PersonContainer? personContainer))
        {
            result = personContainer.DisplayDirectoryName;
            if (string.IsNullOrEmpty(result))
                throw new NotSupportedException();
        }
        return result;
    }

    internal static List<LocationContainer> GetLocationContainers(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, ReadOnlyDictionary<int, List<(string, int)>> skipCollection, List<Record> records)
    {
        List<LocationContainer> results = [];
        List<MappedFile> mappedFiles = GetMappedFiles(propertyConfiguration, configuration, personContainers, records);
        if (mappedFiles.Count > 0 && (configuration.DistanceMoveUnableToMatch || configuration.DistanceRenameToMatch))
        {
            int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
            string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)";
            ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
            ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
            using ProgressBar progressBar = new(mappedFiles.Count, message, options);
            _ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
            {
                progressBar.Tick();
                ParallelFor(propertyConfiguration, configuration, skipCollection, results, mappedFiles[i]);
            });
        }
        if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory))
        {
            ReadOnlyCollection<LocationContainer> locationContainers = new(results.OrderBy(l => l.DirectoryNumber).ToArray());
            LookForPossibleDuplicates(configuration, locationContainers);
        }
        return results;
    }

    private static void ParallelFor(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyDictionary<int, List<(string, int)>> skipCollection, List<LocationContainer> locationContainers, MappedFile mappedFile)
    {
        int? id;
        string checkFile;
        DateOnly dateOnly;
        FilePath filePath;
        string[] fileMatches;
        FileHolder fileHolder;
        int? wholePercentages;
        const string lnk = ".lnk";
        ExifDirectory? exifDirectory;
        string personDisplayDirectoryName;
        const bool fromDistanceContent = true;
        List<(string File, int WholePercentages)>? wholePercentagesCollection;
        if (!mappedFile.FilePath.Name.EndsWith(lnk))
        {
            if (mappedFile.FilePath.Id is null)
                return;
            id = mappedFile.FilePath.Id;
            wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, mappedFile.FilePath);
        }
        else
        {
            fileHolder = IFileHolder.Get(mappedFile.FilePath.FullName[..^4]);
            filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
            if (filePath.Id is null)
                return;
            id = filePath.Id;
            wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath);
        }
        if (wholePercentages is null)
            return;
        if (configuration.LinkedAlpha is null && string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory) && skipCollection.TryGetValue(id.Value, out wholePercentagesCollection))
        {
            fileMatches = (from l in wholePercentagesCollection where l.WholePercentages == wholePercentages select l.File).ToArray();
            foreach (string fileMatch in fileMatches)
            {
                if (string.IsNullOrEmpty(fileMatch) || !File.Exists(fileMatch))
                    continue;
                checkFile = $"{fileMatch}.dup";
                if (File.Exists(checkFile))
                    continue;
                File.Move(fileMatch, checkFile);
                continue;
            }
        }
        dateOnly = DateOnly.FromDateTime(new DateTime(mappedFile.FilePath.CreationTicks));
        if (mappedFile.FilePath.Name.EndsWith(lnk) || (!configuration.DistanceMoveUnableToMatch && !configuration.DistanceRenameToMatch) || !File.Exists(mappedFile.FilePath.FullName))
            exifDirectory = null;
        else
            exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(mappedFile.FilePath);
        RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value);
        personDisplayDirectoryName = mappedFile.PersonDisplayDirectoryName is null ? configuration.MappingDefaultName : mappedFile.PersonDisplayDirectoryName;
        LocationContainer locationContainer = new(dateOnly,
                                                  exifDirectory,
                                                  mappedFile.DirectoryNumber,
                                                  personDisplayDirectoryName,
                                                  null,
                                                  null,
                                                  mappedFile.FilePath,
                                                  fromDistanceContent,
                                                  id.Value,
                                                  null,
                                                  null,
                                                  mappedFile.PersonKey,
                                                  rectangle,
                                                  wholePercentages.Value);
        lock (locationContainers)
            locationContainers.Add(locationContainer);
    }

    private static void LookForPossibleDuplicates(Configuration configuration, ReadOnlyCollection<LocationContainer> locationContainers)
    {
        string key;
        float? percent;
        float itemPercentagesArea;
        List<FilePath> delete = [];
        List<Duplicate> duplicates = [];
        RectangleF? itemPercentagesRectangle;
        (FilePath FilePath, int WholePercentages) item;
        Dictionary<string, (FilePath, int)> distinct = [];
        foreach (LocationContainer locationContainer in locationContainers)
        {
            if (locationContainer.PersonKey is null)
                continue;
            key = string.Concat(locationContainer.PersonKey, locationContainer.Id);
            if (distinct.TryGetValue(key, out item))
            {
                if (item.WholePercentages == locationContainer.WholePercentages)
                    continue;
                itemPercentagesRectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, item.WholePercentages);
                if (itemPercentagesRectangle is null || locationContainer.Rectangle is null)
                    percent = null;
                else
                {
                    itemPercentagesArea = itemPercentagesRectangle.Value.Width * itemPercentagesRectangle.Value.Height;
                    percent = ILocation.GetIntersectPercent(itemPercentagesRectangle.Value, itemPercentagesArea, locationContainer.Rectangle.Value);
                }
                delete.Add(item.FilePath);
                delete.Add(locationContainer.FilePath);
                duplicates.Add(new(locationContainer.PersonKey.Value, locationContainer.Id, locationContainer.FilePath, percent));
                continue;
            }
            distinct.Add(key, new(locationContainer.FilePath, locationContainer.WholePercentages));
        }
        if (!configuration.DeletePossibleDuplicates && duplicates.Count > 0)
            OpenPossibleDuplicates(configuration, duplicates);
        else
        {
            if (delete.Count > 8)
                throw new Exception("Something maybe wrong!");
            foreach (FilePath filePath in delete)
            {
                if (File.Exists(filePath.FullName))
                    File.Delete(filePath.FullName);
            }
        }
    }

    private static void OpenPossibleDuplicates(Configuration configuration, List<Duplicate> duplicates)
    {
        string personKeyFormatted;
        foreach (Duplicate duplicate in duplicates)
        {
            if (duplicate.Percent is null)
                continue;
            _ = Process.Start("explorer.exe", string.Concat("\"", duplicate.FilePath.DirectoryFullPath, "\""));
            personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, duplicate.PersonKey);
        }
        foreach ((long personKey, int id, FilePath filePath, float? percent) in duplicates)
        {
            if (percent is not null && percent.Value == 0)
                continue;
            _ = Process.Start("explorer.exe", string.Concat("\"", filePath.DirectoryFullPath, "\""));
            personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey);
        }
        foreach ((long personKey, int id, FilePath filePath, float? percent) in duplicates)
        {
            if (percent is not null && percent.Value > 0)
                continue;
            _ = Process.Start("explorer.exe", string.Concat("\"", filePath.DirectoryFullPath, "\""));
            personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey);
        }
    }

    internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>> GetIdThenWholePercentagesToPersonContainers(Configuration configuration, Dictionary<int, List<(string, int)>> skipCollection, ReadOnlyDictionary<string, PersonContainer> personKeyFormattedToPersonContainer, ReadOnlyCollection<PersonKeyFormattedIdThenWholePercentages> personKeyFormattedIdThenWholePercentagesCollection)
    {
        ReadOnlyDictionary<int, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>> results;
        Dictionary<int, Dictionary<int, List<PersonContainer>>> idThenWholePercentagesToPersonContainerCollection;
        idThenWholePercentagesToPersonContainerCollection = GetAll(configuration, skipCollection, personKeyFormattedToPersonContainer, personKeyFormattedIdThenWholePercentagesCollection);
        results = GetReadOnly(idThenWholePercentagesToPersonContainerCollection);
        return results;
    }

    private static Dictionary<int, Dictionary<int, List<PersonContainer>>> GetAll(Configuration configuration, Dictionary<int, List<(string, int)>> skipCollection, ReadOnlyDictionary<string, PersonContainer> personKeyFormattedToPersonContainer, ReadOnlyCollection<PersonKeyFormattedIdThenWholePercentages> personKeyFormattedIdThenWholePercentagesCollection)
    {
        Dictionary<int, Dictionary<int, List<PersonContainer>>> results = [];
        PersonBirthday? personBirthday;
        PersonContainer? personContainer;
        List<PersonContainer>? personContainers;
        Dictionary<int, List<PersonContainer>>? idTo;
        int? linkedAlphaCheck = string.IsNullOrEmpty(configuration.LinkedAlpha) ? null : configuration.LinkedAlpha[0] - 65;
        if (personKeyFormattedIdThenWholePercentagesCollection.Count > 0)
        {
            foreach (PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages in personKeyFormattedIdThenWholePercentagesCollection)
            {
                personBirthday = IPersonBirthday.GetPersonBirthday(configuration.PersonBirthdayFormat, personKeyFormattedIdThenWholePercentages.PersonKeyFormatted);
                if (personBirthday is null)
                    throw new Exception();
                if (linkedAlphaCheck is not null && personKeyFormattedIdThenWholePercentages.LinksCount is not null && personKeyFormattedIdThenWholePercentages.LinksCount.Value < linkedAlphaCheck.Value)
                {
                    if (!skipCollection.ContainsKey(personKeyFormattedIdThenWholePercentages.Id))
                        skipCollection.Add(personKeyFormattedIdThenWholePercentages.Id, []);
                    skipCollection[personKeyFormattedIdThenWholePercentages.Id].Add((personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName, personKeyFormattedIdThenWholePercentages.WholePercentages));
                    continue;
                }
                if (!personKeyFormattedToPersonContainer.TryGetValue(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted, out personContainer))
                    continue;
                if (!results.TryGetValue(personKeyFormattedIdThenWholePercentages.Id, out idTo))
                {
                    results.Add(personKeyFormattedIdThenWholePercentages.Id, []);
                    if (!results.TryGetValue(personKeyFormattedIdThenWholePercentages.Id, out idTo))
                        throw new Exception();
                }
                if (!idTo.TryGetValue(personKeyFormattedIdThenWholePercentages.WholePercentages, out personContainers))
                {
                    idTo.Add(personKeyFormattedIdThenWholePercentages.WholePercentages, []);
                    if (!idTo.TryGetValue(personKeyFormattedIdThenWholePercentages.WholePercentages, out personContainers))
                        throw new Exception();
                }
                personContainers.Add(personContainer);
            }
        }
        return results;
    }

    internal static void SetKeyValuePairsAndAddToCollections(Configuration configuration, ReadOnlyCollection<PersonContainer> personContainers, Dictionary<long, PersonContainer> personKeyToPersonContainer, ReadOnlyCollection<PersonKeyFormattedIdThenWholePercentages> personKeyFormattedIdThenWholePercentagesCollection, Dictionary<long, int> personKeyToCount, Dictionary<string, PersonContainer> personKeyFormattedToPersonContainer, Dictionary<long, List<PersonContainer>> personKeyToPersonContainerCollection, List<(PersonKeyFormattedIdThenWholePercentages, PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer)
    {
        PersonBirthday? personBirthday;
        Dictionary<int, Dictionary<int, List<PersonContainer>>> idThenWholePercentagesToPersonContainers = [];
        (long, PersonContainer)[] collection = GetDistinctCollection(configuration, personContainers, personKeyToPersonContainerCollection, personKeyFormattedToPersonContainer);
        foreach ((long personKey, PersonContainer personContainer) in collection)
            personKeyToPersonContainer.Add(personKey, personContainer);
        if (personKeyFormattedIdThenWholePercentagesCollection.Count > 0)
        {
            string group;
            char[] matches;
            char status, sex, first;
            PersonDirectory personDirectory;
            PersonContainer? personContainer;
            string personDisplayDirectoryName;
            foreach (PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages in personKeyFormattedIdThenWholePercentagesCollection)
            {
                personBirthday = IPersonBirthday.GetPersonBirthday(configuration.PersonBirthdayFormat, personKeyFormattedIdThenWholePercentages.PersonKeyFormatted);
                if (personBirthday is null)
                    throw new Exception();
                if (!personKeyFormattedToPersonContainer.TryGetValue(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted, out personContainer))
                {
                    personDisplayDirectoryName = personKeyFormattedIdThenWholePercentages.PersonDisplayDirectoryName is null ? configuration.MappingDefaultName : personKeyFormattedIdThenWholePercentages.PersonDisplayDirectoryName;
                    matches = configuration.PersonCharacters.Where(l => personDisplayDirectoryName.Contains(l)).ToArray();
                    if (matches.Length == 0)
                        continue;
                    group = IPerson.GetHourGroup(personKeyFormattedIdThenWholePercentages.PersonDisplayDirectoryName, personBirthday.Value.Hour);
                    (status, sex, first) = IPerson.GetPersonHour(personKeyFormattedIdThenWholePercentages.PersonDisplayDirectoryName, personBirthday.Value.Hour);
                    personDirectory = new(matches.First(), group, status, sex, first);
                    personContainer = PersonContainer.Get(configuration.PersonCharacters.ToArray(), personBirthday, personDisplayDirectoryName, personDirectory);
                    personKeyFormattedToPersonContainer.Add(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted, personContainer);
                }
                if (personContainer.Key is null)
                    throw new Exception();
                _ = personKeyToCount.TryAdd(personContainer.Key.Value, 0);
                personKeyToCount[personContainer.Key.Value]++;
                possiblyNewPersonDisplayDirectoryNamesAndPersonContainer.Add(new(personKeyFormattedIdThenWholePercentages, personContainer));
            }
        }
    }

    private static (long, PersonContainer)[] GetDistinctCollection(Configuration configuration, IEnumerable<PersonContainer> personContainers, Dictionary<long, List<PersonContainer>> personKeyToPersonContainerCollection, Dictionary<string, PersonContainer> personKeyFormattedToPersonContainer)
    {
        (long, PersonContainer)[] results;
        const int zero = 0;
        List<string> errors = [];
        string newestPersonKeyFormatted;
        List<(long PersonKey, PersonContainer PersonContainer)> collection = [];
        foreach (PersonContainer personContainer in personContainers)
        {
            if (personContainer.Key is null)
                continue;
            if (!personKeyToPersonContainerCollection.ContainsKey(personContainer.Key.Value))
                personKeyToPersonContainerCollection.Add(personContainer.Key.Value, []);
            personKeyToPersonContainerCollection[personContainer.Key.Value].Add(personContainer);
            newestPersonKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personContainer.Key.Value);
            _ = personKeyFormattedToPersonContainer.TryAdd(newestPersonKeyFormatted, personContainer);
        }
        foreach (KeyValuePair<long, List<PersonContainer>> keyValuePair in personKeyToPersonContainerCollection)
        {
            if (keyValuePair.Value.Count != 1 && (from l in keyValuePair.Value select l.DisplayDirectoryName).Distinct().Count() != 1)
                errors.Add(keyValuePair.Value[zero].DisplayDirectoryName);
            collection.Add(new(keyValuePair.Key, keyValuePair.Value[zero]));
        }
        if (errors.Count > 0)
            throw new Exception(string.Join(Environment.NewLine, errors));
        results = (from l in collection orderby l.PersonKey descending select (l.PersonKey, l.PersonContainer)).ToArray();
        return results;
    }

}