using Microsoft.Extensions.Configuration;
using Phares.Shared;
using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Drawing.Imaging;
using System.Text.Json;
using System.Text.RegularExpressions;
using View_by_Distance.Distance.Models;
using View_by_Distance.Face.Models;
using View_by_Distance.FaceParts.Models;
using View_by_Distance.Instance.Models;
using View_by_Distance.Map.Models;
using View_by_Distance.Metadata.Models;
using View_by_Distance.PhotoPrism.Models;
using View_by_Distance.Property.Models;
using View_by_Distance.Resize.Models;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Methods;
using View_by_Distance.Shared.Models.Stateless.Methods;
using WindowsShortcutFactory;

namespace View_by_Distance.Instance;

public partial class DlibDotNet
{

    private IBlurHasher? _BlurHasher;

    private readonly D_Face _Faces;
    private readonly C_Resize _Resize;
    private readonly F_Random _Random;
    private readonly IConsole _Console;
    private readonly E_Distance _Distance;
    private readonly Serilog.ILogger? _Log;
    private readonly D2_FaceParts _FaceParts;
    private readonly AppSettings _AppSettings;
    private readonly List<string> _Exceptions;
    private readonly IsEnvironment _IsEnvironment;
    private readonly bool _PropertyRootExistedBefore;
    private readonly Models.Configuration _Configuration;
    private readonly bool _ArgZeroIsConfigurationRootDirectory;
    private readonly Map.Models.Configuration _MapConfiguration;

    public DlibDotNet(
        List<string> args,
        IsEnvironment isEnvironment,
        IConfigurationRoot configurationRoot,
        AppSettings appSettings,
        string workingDirectory,
        bool isSilent,
        IConsole console)
    {
        string message;
        _BlurHasher = null;
        _Console = console;
        _AppSettings = appSettings;
        _IsEnvironment = isEnvironment;
        long ticks = DateTime.Now.Ticks;
        _Exceptions = new List<string>();
        _Log = Serilog.Log.ForContext<DlibDotNet>();
        ReadOnlyCollection<PersonContainer> personContainers;
        Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot);
        Models.Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration);
        _Log.Information(propertyConfiguration.RootDirectory);
        Property.Models.Configuration.Verify(propertyConfiguration, requireExist: false);
        Verify(configuration);
        VerifyExtra(args, propertyConfiguration, configuration);
        _Configuration = configuration;
        _Random = new(configuration);
        if (configuration.IgnoreExtensions is null)
            throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
        string propertyRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(propertyConfiguration, nameof(A_Property), create: false);
        _PropertyRootExistedBefore = !Directory.Exists(propertyRoot);
        string argZero = args.Count > 0 ? Path.GetFullPath(args[0]) : Path.GetFullPath(propertyConfiguration.RootDirectory);
        _ArgZeroIsConfigurationRootDirectory = propertyConfiguration.RootDirectory == argZero;
        if (!Directory.Exists(argZero))
            _ = Directory.CreateDirectory(argZero);
        Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(argZero, ticks);
        if (!Directory.Exists(argZero))
            _ = Directory.CreateDirectory(argZero);
        _Log.Information(configuration.ModelDirectory);
        {
            (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetPngLowQuality();
            (ImageCodecInfo hiddenImageCodecInfo, EncoderParameters hiddenEncoderParameters, string hiddenFileNameExtension) = C_Resize.GetGifLowQuality();
            _Faces = new D_Face(
                argZero,
                configuration.PropertyConfiguration,
                configuration.CheckDFaceAndUpWriteDates,
                encoderParameters,
                configuration.FaceDistanceHiddenImageFactor,
                filenameExtension,
                configuration.ForceFaceLastWriteTimeToCreationTime,
                hiddenEncoderParameters,
                hiddenFileNameExtension,
                hiddenImageCodecInfo,
                imageCodecInfo,
                configuration.LoadPhotoPrismLocations,
                configuration.ModelDirectory,
                configuration.ModelName,
                configuration.OverrideForFaceImages,
                configuration.PredictorModelName,
                configuration.PropertiesChangedForFaces,
                configuration.RectangleIntersectMinimums);
        }
        {
            (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetGifLowQuality();
            _FaceParts = new D2_FaceParts(_Configuration.PropertyConfiguration, imageCodecInfo, encoderParameters, filenameExtension, configuration.CheckDFaceAndUpWriteDates, configuration.OverrideForFaceLandmarkImages);
        }
        _MapConfiguration = Get(configuration, _Faces.FileNameExtension, _Faces.HiddenFileNameExtension, _FaceParts.FileNameExtension);
        _Distance = new(configuration.DistanceMoveUnableToMatch, configuration.DistanceRenameToMatch, _Configuration.FaceConfidencePercent, configuration.RangeDistanceTolerance, configuration.RangeFaceConfidence, configuration.RectangleIntersectMinimums);
        if (_PropertyRootExistedBefore || !_ArgZeroIsConfigurationRootDirectory)
            personContainers = new(new List<PersonContainer>());
        else
        {
            int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
            message = $") Building People Collection - {totalSeconds} total second(s)";
            ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
            using ProgressBar progressBar = new(1, message, options);
            progressBar.Tick();
            string rootDirectory = propertyConfiguration.RootDirectory;
            string peopleRootDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(propertyConfiguration, nameof(A2_People));
            string? rootResultsDirectory = Path.GetDirectoryName(Path.GetDirectoryName(peopleRootDirectory)) ?? throw new Exception();
            Storage storage = new(rootDirectory, rootResultsDirectory, peopleRootDirectory);
            _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(peopleRootDirectory, propertyConfiguration.ResultSingleton));
            personContainers = new(IPersonContainer.GetPersonContainers(storage, configuration.MappingDefaultName, configuration.PersonBirthdayFormat, configuration.PersonCharacters.ToArray(), _Faces.FileNameExtension));
            GenealogicalDataCommunicationCollections genealogicalDataCommunicationCollections = IGenealogicalDataCommunication.GetIndividuals(configuration.GenealogicalDataCommunicationFile, requireNickName: true);
            if (!string.IsNullOrEmpty(configuration.GenealogicalDataCommunicationFile) && genealogicalDataCommunicationCollections.HeaderLines.Any() && genealogicalDataCommunicationCollections.Individuals.Any() && genealogicalDataCommunicationCollections.FamilyGroupLines.Any() && genealogicalDataCommunicationCollections.FooterLines.Any())
            {
                string a2PeopleContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration.PropertyConfiguration, nameof(A2_People), "([])");
                TimeSpan a2LastWriteTimeTimeSpan = new(ticks - new DirectoryInfo(a2PeopleContentDirectory).LastWriteTime.Ticks);
                if (a2LastWriteTimeTimeSpan.TotalDays > 1)
                {
                    ReadOnlyDictionary<int, List<GenealogicalDataCommunicationRelation>> familyIndexToCollection = IGenealogicalDataCommunication.GetFamilyIndexToCollection(configuration.PersonBirthdayFormat, personContainers, genealogicalDataCommunicationCollections);
                    if (familyIndexToCollection.Count != 0)
                        IGenealogicalDataCommunication.MaybeWriteMarkDownFiles(configuration.MappingDefaultName, configuration.PersonBirthdayFormat, ticks, personContainers, genealogicalDataCommunicationCollections.Individuals, familyIndexToCollection, a2PeopleContentDirectory);
                    Directory.SetLastWriteTime(a2PeopleContentDirectory, new(ticks));
                }
            }
        }
        {
            (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(
                configuration.OutputExtension,
                configuration.OutputQuality);
            _Resize = new C_Resize(
                configuration.PropertyConfiguration,
                configuration.ForceResizeLastWriteTimeToCreationTime,
                configuration.OverrideForResizeImages,
                configuration.PropertiesChangedForResize,
                configuration.ValidResolutions,
                imageCodecInfo,
                encoderParameters,
                filenameExtension);
        }
        if (!configuration.SkipSearch)
            Search(ticks, personContainers, argZero, propertyRoot);
        if (!_PropertyRootExistedBefore && !_IsEnvironment.Development && _Exceptions.Count == 0 && _ArgZeroIsConfigurationRootDirectory)
        {
            string d2FacePartsRootDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(propertyConfiguration, nameof(D2_FaceParts));
            _Log.Information(string.Concat("Cleaning <", d2FacePartsRootDirectory, ">"));
            Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(d2FacePartsRootDirectory, ticks);
        }
        message = $"There were {_Exceptions.Count} exception(s) thrown! {Environment.NewLine}{string.Join(Environment.NewLine, _Exceptions)}";
        _Log.Information(message);
        if (_Exceptions.Count != 0)
            throw new Exception(message);
        if (_PropertyRootExistedBefore)
            _Log.Information("First run completed. Run again if wanted");
    }

    private long LogDelta(long ticks, string? methodName)
    {
        long result;
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        double delta = new TimeSpan(DateTime.Now.Ticks - ticks).TotalMilliseconds;
        _Log.Debug($"{methodName} took {Math.Floor(delta)} millisecond(s)");
        result = DateTime.Now.Ticks;
        return result;
    }

    private long LogDeltaInSeconds(long ticks, string methodName)
    {
        long result;
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        double delta = new TimeSpan(DateTime.Now.Ticks - ticks).Seconds;
        _Log.Debug($"{methodName} took {Math.Floor(delta)} seconds(s)");
        result = DateTime.Now.Ticks;
        return result;
    }

    private long LogDeltaInMinutes(long ticks, string methodName)
    {
        long result;
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        double delta = new TimeSpan(DateTime.Now.Ticks - ticks).Minutes;
        _Log.Debug($"{methodName} took {Math.Floor(delta)} minutes(s)");
        result = DateTime.Now.Ticks;
        return result;
    }

    private static void Verify(Models.Configuration configuration)
    {
        if (!configuration.OutputResolutions.Any() || string.IsNullOrEmpty(configuration.OutputResolutions[0]) || !configuration.ValidResolutions.Contains(configuration.OutputResolutions[0]))
            throw new NullReferenceException($"{nameof(configuration.OutputResolutions)} must be fileNameToCollection valid outputResolution!");
        if ((from l in configuration.OutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any())
            throw new Exception($"One or more {nameof(configuration.OutputResolutions)} are not in the ValidResolutions list!");
        if ((from l in configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any())
            throw new Exception($"One or more {nameof(configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions)} are not in the ValidResolutions list!");
        if ((from l in configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any())
            throw new Exception($"One or more {nameof(configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions)} are not in the ValidResolutions list!");
        if ((from l in configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any())
            throw new Exception($"One or more {nameof(configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions)} are not in the ValidResolutions list!");
        if ((from l in configuration.SaveShortcutsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any())
            throw new Exception($"One or more {nameof(configuration.SaveShortcutsForOutputResolutions)} are not in the ValidResolutions list!");
        if ((from l in configuration.SaveFaceLandmarkForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any())
            throw new Exception($"One or more {nameof(configuration.SaveFaceLandmarkForOutputResolutions)} are not in the ValidResolutions list!");
        if (string.IsNullOrEmpty(configuration.ModelName))
            throw new NullReferenceException(nameof(configuration.ModelName));
        if (string.IsNullOrEmpty(configuration.OutputExtension))
            throw new NullReferenceException(nameof(configuration.OutputExtension));
        if (string.IsNullOrEmpty(configuration.PredictorModelName))
            throw new NullReferenceException(nameof(configuration.PredictorModelName));
        if (string.IsNullOrEmpty(configuration.ModelDirectory) || !Directory.Exists(configuration.ModelDirectory))
            throw new NullReferenceException(nameof(configuration.ModelDirectory));
    }

    private void VerifyExtra(List<string> args, Property.Models.Configuration propertyConfiguration, Models.Configuration configuration)
    {
        string[] sourceDirectoryNames;
        if (!args.Any())
            sourceDirectoryNames = Array.Empty<string>();
        else
        {
            string? century;
            string argZero = Path.GetFullPath(args[0]);
            sourceDirectoryNames = argZero.Split(Path.DirectorySeparatorChar);
            if (!argZero.StartsWith(propertyConfiguration.RootDirectory))
                throw new Exception($"Source directory must be inside root directory! <{argZero}> <{propertyConfiguration.RootDirectory}>");
            if (_ArgZeroIsConfigurationRootDirectory && propertyConfiguration.RootDirectory != argZero)
            {
                if (!configuration.MixedYearRelativePaths.Contains(sourceDirectoryNames[0]))
                {
                    string[] segments = sourceDirectoryNames[0].Split(' ');
                    century = segments[^1].Length == 4 ? segments[^1][..2] : null;
                    if (segments.Length < 2 || century is null || (century != "18" && century != "19" && century != "20"))
                        throw new Exception("root subdirectory must have fileNameToCollection year at the end or directory name needs to be added to the exclude list!");
                }
            }
        }
        string[] resizeMatch = (from l in sourceDirectoryNames where configuration.ValidResolutions.Contains(l) select l).ToArray();
        if (resizeMatch.Any())
            throw new Exception("Input directory should be the source and not fileNameToCollection resized directory!");
        if (configuration.LocationDigits != Shared.Models.Stateless.ILocation.Digits)
            throw new Exception("Configuration has to match interface!");
        if (configuration.LocationFactor != Shared.Models.Stateless.ILocation.Factor)
            throw new Exception("Configuration has to match interface!");
    }

    private static Map.Models.Configuration Get(Models.Configuration configuration, string facesFileNameExtension, string facesHiddenFileNameExtension, string facePartsFileNameExtension)
    {
        Map.Models.Configuration result = new(
            configuration.DeletePossibleDuplicates,
            configuration.DistanceMoveUnableToMatch,
            configuration.DistanceRenameToMatch,
            configuration.FaceConfidencePercent,
            configuration.FaceDistancePermyriad,
            configuration.LocationDigits,
            configuration.MappingDefaultName,
            configuration.PersonBirthdayFirstYear,
            configuration.PersonBirthdayFormat,
            configuration.PersonCharacters.ToArray(),
            configuration.RangeDaysDeltaTolerance,
            configuration.RangeDistanceTolerance,
            configuration.SaveSortingWithoutPerson,
            configuration.SkipNotSkipDirectories,
            configuration.SortingMaximumPerKey,
            configuration.SortingMinimumToUseSigma,
            facesFileNameExtension,
            facesHiddenFileNameExtension,
            facePartsFileNameExtension);
        return result;
    }

    private bool? GetIsFocusModel(Shared.Models.Property? property)
    {
        bool? result;
        if (string.IsNullOrEmpty(_Configuration.FocusModel))
            result = null;
        else if (property is null || string.IsNullOrEmpty(property.Model))
            result = null;
        else
            result = property.Model.Contains(_Configuration.FocusModel);
        return result;
    }

    private void SetMapping(MapLogic mapLogic, Item item, bool? isFocusRelativePath, bool? isIgnoreRelativePath, MappingFromItem mappingFromItem, List<MappingFromPhotoPrism>? mappingFromPhotoPrismCollection, List<Shared.Models.Face> faces)
    {
        double? α;
        int? eyeα;
        bool? eyeReview;
        Mapping mapping;
        int faceAreaPermyriad;
        int confidencePercent;
        bool? inSkipCollection;
        int wholePercentRectangle;
        string deterministicHashCodeKey;
        MappingFromFilter mappingFromFilter;
        MappingFromLocation? mappingFromLocation;
        bool? isFocusModel = GetIsFocusModel(item.Property);
        foreach (Shared.Models.Face face in faces)
        {
            if (item.Property?.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
            {
                inSkipCollection = null;
                mappingFromLocation = null;
                mappingFromFilter = new(isFocusModel, isFocusRelativePath, isIgnoreRelativePath, inSkipCollection);
            }
            else
            {
                if (face.FaceParts is null)
                    (eyeα, eyeReview) = (null, null);
                else
                {
                    (eyeReview, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts);
                    eyeα = α is null ? null : (int)Math.Round(Math.Abs(α.Value));
                }
                confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_Configuration.FaceConfidencePercent, _Configuration.RangeFaceConfidence, face.Location.Confidence);
                faceAreaPermyriad = IMapping.GetAreaPermyriad(_Configuration.FaceAreaPermyriad, face.Location, face.OutputResolution);
                wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
                deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.Property.Id.Value, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
                mappingFromLocation = new(faceAreaPermyriad, confidencePercent, deterministicHashCodeKey, eyeα, eyeReview, wholePercentRectangle);
                inSkipCollection = mapLogic.InSkipCollection(item.Property.Id.Value, mappingFromLocation);
                mappingFromFilter = new(isFocusModel, isFocusRelativePath, isIgnoreRelativePath, inSkipCollection);
            }
            mapping = new(mappingFromItem, mappingFromFilter, mappingFromLocation, mappingFromPhotoPrismCollection);
            _ = mapLogic.UpdateMappingFromPerson(mapping);
            face.SetMapping(mapping);
        }
    }

    private Mapping GetMapping(MapLogic mapLogic, Item item, bool? isFocusRelativePath, bool? isIgnoreRelativePath, MappingFromItem mappingFromItem)
    {
        Mapping result;
        int? eyeα = null;
        bool? eyeReview = null;
        bool? inSkipCollection;
        int wholePercentRectangle;
        int faceAreaPermyriad = 0;
        int confidencePercent = 0;
        string deterministicHashCodeKey;
        MappingFromFilter mappingFromFilter;
        MappingFromLocation? mappingFromLocation;
        bool? isFocusModel = GetIsFocusModel(item.Property);
        if (item.Property?.Id is null)
        {
            inSkipCollection = null;
            mappingFromLocation = null;
            mappingFromFilter = new(isFocusModel, isFocusRelativePath, isIgnoreRelativePath, inSkipCollection);
        }
        else
        {
            wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(Shared.Models.Stateless.ILocation.Digits);
            deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.Property.Id.Value, Shared.Models.Stateless.ILocation.Digits);
            mappingFromLocation = new(faceAreaPermyriad, confidencePercent, deterministicHashCodeKey, eyeα, eyeReview, wholePercentRectangle);
            inSkipCollection = mapLogic.InSkipCollection(item.Property.Id.Value, mappingFromLocation);
            mappingFromFilter = new(isFocusModel, isFocusRelativePath, isIgnoreRelativePath, inSkipCollection);
        }
        result = new(mappingFromItem, mappingFromFilter, mappingFromLocation, mappingFromPhotoPrismCollection: null);
        return result;
    }

    private void LogItemPropertyIsNull(Item item)
    {
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        if (!item.SourceDirectoryFileHolder.Exists)
            _Log.Information(string.Concat("NoJson <", item.ImageFileHolder.FullName, '>'));
        else if (item.FileSizeChanged.HasValue && item.FileSizeChanged.Value)
            _Log.Information(string.Concat("FileSizeChanged <", item.ImageFileHolder.FullName, '>'));
        else if (item.LastWriteTimeChanged.HasValue && item.LastWriteTimeChanged.Value)
            _Log.Information(string.Concat("LastWriteTimeChanged <", item.ImageFileHolder.FullName, '>'));
        else if (item.Moved.HasValue && item.Moved.Value)
            _Log.Information(string.Concat("Moved <", item.ImageFileHolder.FullName, '>'));
    }

    private void LogNameWithoutExtensionIsIdFormatBut(Item item)
    {
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        _Log.Information($"Name without extension is Id format but doesn't match id <{item.ImageFileHolder.FullName}>");
        File.Move(item.ImageFileHolder.FullName, $"{item.ImageFileHolder.FullName}.rename");
    }

    private void FullParallelForWork(A_Property propertyLogic,
                                     B_Metadata metadata,
                                     ReadOnlyDictionary<int, List<LocationContainer<MetadataExtractor.Directory>>> idToLocationContainers,
                                     MapLogic mapLogic,
                                     string outputResolution,
                                     bool outputResolutionHasNumber,
                                     string cResultsFullGroupDirectory,
                                     string dResultsFullGroupDirectory,
                                     string d2ResultsFullGroupDirectory,
                                     List<Tuple<string, DateTime>> sourceDirectoryChanges,
                                     Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection,
                                     Container container,
                                     int index,
                                     Item item,
                                     DateTime[] containerDateTimes,
                                     bool? isFocusRelativePath,
                                     bool? isIgnoreRelativePath,
                                     string facePartsCollectionDirectory)
    {
        List<Shared.Models.Face> faces;
        long ticks = DateTime.Now.Ticks;
        DateTime dateTime = DateTime.Now;
        Shared.Models.Property? property;
        List<string> parseExceptions = new();
        List<Tuple<string, DateTime>> subFileTuples = new();
        List<KeyValuePair<string, string>> metadataCollection;
        string[] changesFrom = new string[] { nameof(A_Property) };
        FileHolder resizedFileHolder = _Resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber);
        if (item.Property is null || item.Property.Id is null || item.Any())
        {
            LogItemPropertyIsNull(item);
            int? propertyHashCode = item.Property?.GetHashCode();
            property = propertyLogic.GetProperty(metadata, item, subFileTuples, parseExceptions);
            item.Update(property);
            if (propertyHashCode is null)
            {
                lock (sourceDirectoryChanges)
                    sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
            }
            else if (propertyHashCode.Value != property.GetHashCode())
            {
                lock (sourceDirectoryChanges)
                    sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
            }
        }
        else
        {
            property = item.Property;
            if (item.SourceDirectoryFileHolder.LastWriteTime is not null)
                subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), item.SourceDirectoryFileHolder.LastWriteTime.Value));
            else
                subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), new FileInfo(item.SourceDirectoryFileHolder.FullName).LastWriteTime));
            int sortOrderOnlyLengthIndex = IDirectory.GetSortOrderOnlyLengthIndex();
            bool nameWithoutExtensionIsIdFormat = Shared.Models.Stateless.Methods.IProperty.NameWithoutExtensionIsIdFormat(item.ImageFileHolder);
            bool nameWithoutExtensionIsPaddedIdFormat = IDirectory.NameWithoutExtensionIsPaddedIdFormat(item.ImageFileHolder, sortOrderOnlyLengthIndex);
            if (nameWithoutExtensionIsIdFormat && item.ImageFileHolder.NameWithoutExtension != item.Property.Id.ToString())
                LogNameWithoutExtensionIsIdFormatBut(item);
            if (nameWithoutExtensionIsPaddedIdFormat && item.ImageFileHolder.NameWithoutExtension.EndsWith(item.Property.Id.Value.ToString()[1..]))
                LogNameWithoutExtensionIsIdFormatBut(item);
            if (_BlurHasher is not null && resizedFileHolder.Exists && item.Property.Width is not null && item.Property.Width.Value > 4 && _Configuration.SaveBlurHashForOutputResolutions.Contains(outputResolution))
            {
                string? file = _BlurHasher.GetFile(resizedFileHolder);
                if (file is not null && !File.Exists(file))
                    _ = _BlurHasher.EncodeAndSave(resizedFileHolder);
            }
        }
        if (property is null || item.Property is null)
            throw new NullReferenceException(nameof(property));
        item.SetResizedFileHolder(_Resize.FileNameExtension, resizedFileHolder);
        MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
        (int metadataGroups, metadataCollection) = metadata.GetMetadataCollection(subFileTuples, parseExceptions, changesFrom, mappingFromItem);
        if (_AppSettings.MaxDegreeOfParallelism < 2)
            ticks = LogDelta(ticks, nameof(B_Metadata.GetMetadataCollection));
        Dictionary<string, int[]> outputResolutionToResize = _Resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, subFileTuples, parseExceptions, metadataCollection, item.Property, mappingFromItem);
        if (_AppSettings.MaxDegreeOfParallelism < 2)
            ticks = LogDelta(ticks, nameof(C_Resize.GetResizeKeyValuePairs));
        if (_Configuration.SaveResizedSubfiles)
        {
            _Resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize);
            if (_AppSettings.MaxDegreeOfParallelism < 2)
                ticks = LogDelta(ticks, nameof(C_Resize.SaveResizedSubfile));
        }
        if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution))
            faces = new();
        else if (!mappingFromItem.ResizedFileHolder.Exists && !File.Exists(mappingFromItem.ResizedFileHolder.FullName))
            faces = new();
        else
        {
            List<MappingFromPhotoPrism>? mappingFromPhotoPrismCollection;
            List<LocationContainer<MetadataExtractor.Directory>>? locationContainers;
            if (item.Property?.Id is null)
                locationContainers = null;
            else
                _ = idToLocationContainers.TryGetValue(item.Property.Id.Value, out locationContainers);
            if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection))
                mappingFromPhotoPrismCollection = null;
            faces = _Faces.GetFaces(outputResolution, dResultsFullGroupDirectory, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionToResize, locationContainers, mappingFromPhotoPrismCollection);
            if (_AppSettings.MaxDegreeOfParallelism < 2)
                ticks = LogDelta(ticks, nameof(D_Face.GetFaces));
            SetMapping(mapLogic, item, isFocusRelativePath, isIgnoreRelativePath, mappingFromItem, mappingFromPhotoPrismCollection, faces);
            List<(Shared.Models.Face, FileInfo?, string, bool Saved)> faceCollection = _Faces.SaveFaces(_FaceParts.FileNameExtension, dResultsFullGroupDirectory, subFileTuples, parseExceptions, mappingFromItem, faces);
            if (_Configuration.CopyFacesAndSaveFaceLandmarkForOutputResolutions.Contains(outputResolution))
                _FaceParts.CopyFacesAndSaveFaceLandmarkImage(facePartsCollectionDirectory, mappingFromItem, faceCollection);
            if (_AppSettings.MaxDegreeOfParallelism < 2)
                ticks = LogDelta(ticks, nameof(D_Face.SaveFaces));
            if ((_Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch)
                && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)
                && locationContainers is not null && faceCollection.All(l => !l.Saved))
                _Distance.LookForMatchFacesAndPossiblyRename(_Faces.FileNameExtension, mappingFromItem, faces, locationContainers);
            (bool review, int[] eyesCollection) = Shared.Models.Stateless.Methods.IFace.GetEyeCollection(_Configuration.EyeThreshold, faces);
            if (review || _Configuration.SaveFaceLandmarkForOutputResolutions.Contains(outputResolution))
            {
                bool saveRotated = false;
                string sourceDirectorySegment = Property.Models.Stateless.IResult.GetRelativePath(_Configuration.PropertyConfiguration, container.SourceDirectory);
                _FaceParts.SaveFaceLandmarkImages(_Configuration.PropertyConfiguration, subFileTuples, parseExceptions, mappingFromItem, faces, saveRotated);
                if (_AppSettings.MaxDegreeOfParallelism < 2)
                    ticks = LogDelta(ticks, nameof(D2_FaceParts.SaveFaceLandmarkImages));
            }
        }
        lock (sourceDirectoryChanges)
        {
            item.Faces.AddRange(faces);
            sourceDirectoryChanges.AddRange(from l in subFileTuples where l.Item2 > dateTime select l);
        }
    }

    private int FullParallelWork(int maxDegreeOfParallelism,
                                 A_Property propertyLogic,
                                 B_Metadata metadata,
                                 ReadOnlyDictionary<int, List<LocationContainer<MetadataExtractor.Directory>>> idToLocationContainers,
                                 MapLogic mapLogic,
                                 string outputResolution,
                                 bool outputResolutionHasNumber,
                                 string cResultsFullGroupDirectory,
                                 string dResultsFullGroupDirectory,
                                 string d2ResultsFullGroupDirectory,
                                 List<Tuple<string, DateTime>> sourceDirectoryChanges,
                                 Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection,
                                 Container container,
                                 Item[] filteredItems,
                                 string message)
    {
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        int result = 0;
        ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
        DateTime[] containerDateTimes = Shared.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems);
        ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
        string focusRelativePath = Path.GetFullPath(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, _Configuration.FocusDirectory));
        bool? isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath);
        string facePartsCollectionDirectory = _Configuration.CopyFacesAndSaveFaceLandmarkForOutputResolutions.Contains(outputResolution) ? _FaceParts.GetFacePartsDirectory(_Configuration.PropertyConfiguration, d2ResultsFullGroupDirectory, item: filteredItems.First(), includeNameWithoutExtension: false) : string.Empty;
        bool? isIgnoreRelativePath = !_Configuration.IgnoreRelativePaths.Any() ? null : _Configuration.IgnoreRelativePaths.Any(l => container.SourceDirectory.Contains(l)) && Shared.Models.Stateless.Methods.IContainer.IsIgnoreRelativePath(_Configuration.PropertyConfiguration, _Configuration.IgnoreRelativePaths, container.SourceDirectory);
        using ProgressBar progressBar = new(filteredItems.Length, message, options);
        _ = Parallel.For(0, filteredItems.Length, parallelOptions, (i, state) =>
           {
               try
               {
                   FullParallelForWork(propertyLogic,
                                       metadata,
                                       idToLocationContainers,
                                       mapLogic,
                                       outputResolution,
                                       outputResolutionHasNumber,
                                       cResultsFullGroupDirectory,
                                       dResultsFullGroupDirectory,
                                       d2ResultsFullGroupDirectory,
                                       sourceDirectoryChanges,
                                       fileNameToCollection,
                                       container,
                                       index: i,
                                       filteredItems[i],
                                       containerDateTimes,
                                       isFocusRelativePath,
                                       isIgnoreRelativePath,
                                       facePartsCollectionDirectory);
                   if (i == 0 || sourceDirectoryChanges.Any())
                       progressBar.Tick();
               }
               catch (Exception ex)
               {
                   result += 1;
                   _Log.Error(string.Concat(container.SourceDirectory, Environment.NewLine, ex.Message, Environment.NewLine, ex.StackTrace), ex);
                   if (result == filteredItems.Length)
                       throw new Exception(string.Concat("All in [", container.SourceDirectory, "] failed!"));
               }
           });
        return result;
    }

    private static void WriteTab(string checkDirectory, List<(string Id, string Line)> metadataIdLines, string fileName)
    {
        string text;
        FileInfo fileInfo;
        List<string> duplicates = new();
        List<string> metadataIds = new();
        fileInfo = new(Path.Combine(checkDirectory, "[()]", Path.ChangeExtension(fileName, "tsv")));
        if (fileInfo?.Directory is null)
            throw new Exception();
        if (!fileInfo.Directory.Exists)
            fileInfo.Directory.Create();
        foreach ((string Id, string Line) metadataIdLine in metadataIdLines)
        {
            if (metadataIds.Contains(metadataIdLine.Id))
                duplicates.Add(metadataIdLine.Id);
            else
                metadataIds.Add(metadataIdLine.Id);
        }
        for (int i = metadataIdLines.Count - 1; i > -1; i--)
        {
            if (duplicates.Contains(metadataIdLines[i].Id))
                metadataIdLines.RemoveAt(i);
        }
        if (metadataIdLines.Any())
        {
            text = string.Join(Environment.NewLine, from l in metadataIdLines orderby l.Id select l.Line);
            _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, text, updateDateWhenMatches: true, compareBeforeWrite: true);
        }
        else
        {
            if (fileInfo.Exists)
                File.Delete(fileInfo.FullName);
        }
    }

    private (string, string) GetResultsFullGroupDirectories()
    {
        string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
            _Configuration.PropertyConfiguration,
            nameof(A_Property),
            string.Empty,
            includeResizeGroup: false,
            includeModel: false,
            includePredictorModel: false);
        string bResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
            _Configuration.PropertyConfiguration,
            nameof(B_Metadata),
            string.Empty,
            includeResizeGroup: false,
            includeModel: false,
            includePredictorModel: false);
        return new(aResultsFullGroupDirectory, bResultsFullGroupDirectory);
    }

    private (string, string, string, string) GetResultsFullGroupDirectories(string outputResolution)
    {
        string cResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
            _Configuration.PropertyConfiguration,
            nameof(C_Resize),
            outputResolution,
            includeResizeGroup: true,
            includeModel: false,
            includePredictorModel: false);
        string c2ResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
            _Configuration.PropertyConfiguration,
            nameof(BlurHash.Models.C2_BlurHasher),
            outputResolution,
            includeResizeGroup: true,
            includeModel: false,
            includePredictorModel: false);
        string dResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
            _Configuration.PropertyConfiguration,
            nameof(D_Face),
            outputResolution,
            includeResizeGroup: true,
            includeModel: true,
            includePredictorModel: true);
        string d2ResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
            _Configuration.PropertyConfiguration,
            nameof(D2_FaceParts),
            outputResolution,
            includeResizeGroup: true,
            includeModel: true,
            includePredictorModel: true);
        return new(cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory);
    }

    private void FullDoWork(string argZero, string propertyRoot, long ticks, string aResultsFullGroupDirectory, string bResultsFullGroupDirectory, int t, Container[] containers, A_Property propertyLogic, B_Metadata metadata, Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection, ReadOnlyDictionary<int, List<LocationContainer<MetadataExtractor.Directory>>> idToLocationContainers, MapLogic mapLogic)
    {
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        int total;
        string message;
        int totalSeconds;
        int exceptionCount;
        Container container;
        Item[] filteredItems;
        bool outputResolutionHasNumber;
        bool anyNullOrNoIsUniqueFileName;
        string cResultsFullGroupDirectory;
        string dResultsFullGroupDirectory;
        string c2ResultsFullGroupDirectory;
        string d2ResultsFullGroupDirectory;
        int containersLength = containers.Length;
        List<Tuple<string, DateTime>> sourceDirectoryChanges = new();
        int maxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism;
        string dResultsDateGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(D_Face));
        foreach (string outputResolution in _Configuration.OutputResolutions)
        {
            total = 0;
            outputResolutionHasNumber = outputResolution.Any(l => char.IsNumber(l));
            (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution);
            _Faces.Update(dResultsFullGroupDirectory);
            _Resize.Update(cResultsFullGroupDirectory);
            _FaceParts.Update(d2ResultsFullGroupDirectory);
            _BlurHasher = new BlurHash.Models.C2_BlurHasher(_Configuration.PropertyConfiguration, c2ResultsFullGroupDirectory);
            for (int i = 0; i < containers.Length; i++)
            {
                container = containers[i];
                if (!container.Items.Any())
                    continue;
                if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero))
                    continue;
                filteredItems = Shared.Models.Stateless.Methods.IContainer.GetFilterItems(_Configuration.PropertyConfiguration, container);
                if (!filteredItems.Any())
                    continue;
                sourceDirectoryChanges.Clear();
                anyNullOrNoIsUniqueFileName = filteredItems.Any(l => !l.IsUniqueFileName);
                totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
                message = $"{i + 1:000} [{filteredItems.Length:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}";
                propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, container.SourceDirectory, anyNullOrNoIsUniqueFileName);
                if (outputResolutionHasNumber)
                    _Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory);
                exceptionCount = FullParallelWork(maxDegreeOfParallelism,
                                                  propertyLogic,
                                                  metadata,
                                                  idToLocationContainers,
                                                  mapLogic,
                                                  outputResolution,
                                                  outputResolutionHasNumber,
                                                  cResultsFullGroupDirectory,
                                                  dResultsFullGroupDirectory,
                                                  d2ResultsFullGroupDirectory,
                                                  sourceDirectoryChanges,
                                                  fileNameToCollection,
                                                  container,
                                                  filteredItems,
                                                  message);
                if (exceptionCount != 0)
                {
                    _Exceptions.Add(container.SourceDirectory);
                    continue;
                }
                if (Directory.GetFiles(propertyRoot, "*.txt", SearchOption.TopDirectoryOnly).Any())
                {
                    for (int y = 0; y < int.MaxValue; y++)
                    {
                        _Log.Information("Press \"Y\" key when ready to continue or close console");
                        if (_Console.ReadKey() == ConsoleKey.Y)
                            break;
                    }
                    _Log.Information(". . .");
                }
                total += container.Items.Count;
            }
        }
    }

    private void SaveFaceDistances(long ticks, MapLogic mapLogic, Mapping[] mappingCollection, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, Dictionary<int, Dictionary<int, Mapping>> idToWholePercentagesToMapping, List<FaceDistance> faceDistanceEncodings, FaceDistanceContainer[] faceDistanceContainers)
    {
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        DistanceLimits distanceLimits;
        int? useFiltersCounter = null;
        SortingContainer[] sortingContainers;
        FaceDistanceContainer[] filteredFaceDistanceContainers;
        long? skipOlderThan = _Configuration.SkipOlderThanDays is null ? null : new DateTime(ticks).AddDays(-_Configuration.SkipOlderThanDays.Value).Ticks;
        distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh);
        filteredFaceDistanceContainers = E_Distance.FilteredFaceDistanceContainers(mapLogic, faceDistanceContainers, skipOlderThan, distanceLimits);
        if (!filteredFaceDistanceContainers.Any())
            _Log.Information("All images have been filtered!");
        else
        {
            sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers);
            if (!sortingContainers.Any())
            {
                for (useFiltersCounter = 1; useFiltersCounter < _Configuration.UseFilterTries; useFiltersCounter++)
                {
                    distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh, useFiltersCounter);
                    filteredFaceDistanceContainers = E_Distance.FilteredFaceDistanceContainers(mapLogic, faceDistanceContainers, skipOlderThan, distanceLimits);
                    if (filteredFaceDistanceContainers.Any())
                        _Log.Information("All images have been filtered!");
                    else
                    {
                        sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers);
                        if (sortingContainers.Any())
                            break;
                    }
                }
            }
            E_Distance.SaveFaceDistances(_Configuration.PropertyConfiguration, sortingContainers);
            if (filteredFaceDistanceContainers.Length > 0)
            {
                int updated = mapLogic.UpdateFromSortingContainers(_Configuration.SaveIndividually, distanceLimits, sortingContainers);
                List<SaveContainer> saveContainers = mapLogic.GetSaveContainers(_Configuration.SaveIndividually, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, mappingCollection, idToWholePercentagesToMapping, useFiltersCounter, sortingContainers.Any());
                mapLogic.SaveContainers(_Configuration.SaveIndividually, filteredFaceDistanceContainers.Length, updated, saveContainers);
            }
        }
    }

    private void SaveFaceDistances(long ticks, MapLogic mapLogic, List<Shared.Models.Face> distinctFilteredFaces, Mapping[] mappingCollection, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, string dFacesCollectionDirectory, Dictionary<int, Dictionary<int, Mapping>> idToWholePercentagesToMapping)
    {
        E_Distance.SetFaceDistances(_AppSettings.MaxDegreeOfParallelism, ticks, distinctFilteredFaces);
        FaceDistanceContainer[] faceDistanceContainers = E_Distance.GetFaceDistanceContainers(distinctFilteredFaces);
        Dictionary<int, Dictionary<int, PersonContainer[]>> missingIdThenWholePercentagesToPersonContainers = mapLogic.GetMissing(idToWholePercentagesToMapping);
        List<FaceDistanceContainer> missingFaceDistanceContainers = _Distance.GetMissingFaceDistanceContainer(_AppSettings.MaxDegreeOfParallelism, ticks, dFacesCollectionDirectory, missingIdThenWholePercentagesToPersonContainers);
        List<FaceDistance> faceDistanceEncodings = E_Distance.GetFaceDistanceEncodings(faceDistanceContainers, missingFaceDistanceContainers);
        if (faceDistanceContainers.Any())
            SaveFaceDistances(ticks, mapLogic, mappingCollection, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, faceDistanceEncodings, faceDistanceContainers);
    }

    private void MapLogic(long ticks, Container[] containers, string a2PeopleSingletonDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, Dictionary<long, List<int>> personKeyToIds, List<Shared.Models.Face> distinctFilteredFaces, Mapping[] distinctFilteredMappingCollection, int totalNotMapped)
    {
        string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent);
        string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent);
        string d2FacePartsContentCollectionDirectory = Path.Combine(d2ResultsFullGroupDirectory, "[()]");
        string dFacesCollectionDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultCollection, _Configuration.PropertyConfiguration.ResultAllInOne);
        if (distinctFilteredMappingCollection.Any())
        {
            Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(d2FacePartsContentDirectory, ticks);
            if (Directory.Exists(d2FacePartsContentCollectionDirectory))
                Shared.Models.Stateless.Methods.IPath.MakeHiddenIfAllItemsAreHidden(d2FacePartsContentCollectionDirectory);
        }
        Dictionary<int, Dictionary<int, Mapping>> idToWholePercentagesToMapping = Map.Models.Stateless.Methods.IMapLogic.GetIdToWholePercentagesToFace(distinctFilteredMappingCollection);
        if (Directory.Exists(fPhotoPrismContentDirectory))
            F_PhotoPrism.WriteMatches(fPhotoPrismContentDirectory, _Configuration.MappingDefaultName, _Configuration.PersonBirthdayFormat, _Configuration.RectangleIntersectMinimums, ticks, distinctFilteredFaces, mapLogic);
        if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution))
            mapLogic.SaveShortcutsForOutputResolutionsDuringMapLogic(containers, personKeyToIds, dFacesContentDirectory, distinctFilteredMappingCollection);
        if (!string.IsNullOrEmpty(_Configuration.PersonCharacters))
        {
            if (_Configuration.PersonCharactersCopyCount > 0)
                mapLogic.CopyAtLeastOneMappedFiles(_Configuration.PersonCharactersCopyCount, dFacesContentDirectory, a2PeopleSingletonDirectory, distinctFilteredMappingCollection);
            mapLogic.SavePersonKeyToCount(dFacesContentDirectory, a2PeopleSingletonDirectory, distinctFilteredMappingCollection);
        }
        mapLogic.CopyManualFiles(dFacesContentDirectory, idToWholePercentagesToMapping);
        if (_Configuration.SaveMappedForOutputResolutions.Contains(outputResolution))
            mapLogic.SaveMapped(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, personKeyToIds, distinctFilteredMappingCollection, idToWholePercentagesToMapping, totalNotMapped);
        if (_Configuration.SaveFaceDistancesForOutputResolutions.Contains(outputResolution))
            SaveFaceDistances(ticks, mapLogic, distinctFilteredFaces, distinctFilteredMappingCollection, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, dFacesCollectionDirectory, idToWholePercentagesToMapping);
    }

    private string SaveUrlAndGetNewRootDirectory(string[] files)
    {
        string result;
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        if (!files.Any())
            throw new NotSupportedException();
        string? sourceDirectory = Path.GetDirectoryName(files.First());
        if (string.IsNullOrEmpty(sourceDirectory))
            throw new NotSupportedException();
        Uri uri;
        string? line;
        string fileName;
        Task<byte[]> task;
        string relativePath;
        FileHolder fileHolder;
        string extensionLowered;
        string sourceDirectoryFile;
        HttpClient httpClient = new();
        bool isValidImageFormatExtension;
        result = Path.GetDirectoryName(Path.GetDirectoryName(sourceDirectory)) ?? throw new NotSupportedException();
        int length = result.Length;
        for (int y = 0; y < int.MaxValue; y++)
        {
            _Log.Information("Enter fileNameToCollection url for fileNameToCollection image");
            line = _Console.ReadLine();
            if (string.IsNullOrEmpty(line))
                break;
            uri = new(line);
            if (uri.HostNameType != UriHostNameType.Dns)
                continue;
            task = httpClient.GetByteArrayAsync(uri);
            fileName = Path.GetFileName(uri.LocalPath);
            sourceDirectoryFile = Path.Combine(sourceDirectory, fileName);
            File.WriteAllBytes(sourceDirectoryFile, task.Result);
            extensionLowered = Path.GetExtension(uri.LocalPath);
            relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(sourceDirectoryFile, length, forceExtensionToLower: true);
            isValidImageFormatExtension = _Configuration.PropertyConfiguration.ValidImageFormatExtensions.Contains(extensionLowered);
            fileHolder = new(sourceDirectoryFile);
            _ = new Item(fileHolder, relativePath, isValidImageFormatExtension);
            // container.Items.Add(item);
        }
        _Log.Information(". . .");
        return result;
    }

    private static void LookForAbandoned(List<int> distinctFilteredIds, string directory, string directoryName)
    {
        string fileNameWithoutExtension;
        bool nameWithoutExtensionIsIdFormat;
        List<string> renameCollection = new();
        bool nameWithoutExtensionIsPaddedIdFormat;
        int sortOrderOnlyLengthIndex = IDirectory.GetSortOrderOnlyLengthIndex();
        string[] distinctFilteredIdsValues = distinctFilteredIds.Select(l => l.ToString()).ToArray();
        string[] files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
        foreach (string file in files)
        {
            fileNameWithoutExtension = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file)));
            nameWithoutExtensionIsIdFormat = Shared.Models.Stateless.Methods.IProperty.NameWithoutExtensionIsIdFormat(fileNameWithoutExtension);
            nameWithoutExtensionIsPaddedIdFormat = IDirectory.NameWithoutExtensionIsPaddedIdFormat(fileNameWithoutExtension, sortOrderOnlyLengthIndex);
            if (!nameWithoutExtensionIsIdFormat && !nameWithoutExtensionIsPaddedIdFormat)
                continue;
            if (distinctFilteredIdsValues.Contains(fileNameWithoutExtension))
                continue;
            renameCollection.Add(file);
        }
        if (renameCollection.Any())
        {
            if (directoryName.Length == 2)
                IDirectory.MoveFiles(renameCollection, directoryName, $"{directoryName[0]}abd{directoryName[^1]}");
            else if (directoryName.Length == 4)
                IDirectory.MoveFiles(renameCollection, directoryName, $"{directoryName[..2]}abd{directoryName[^2..]}");
            else
                throw new NotSupportedException();
        }
    }

    private static void LookForAbandoned(string bResultsFullGroupDirectory, List<int> distinctFilteredIds)
    {
        string[] directories = Directory.GetDirectories(bResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly);
        foreach (string directory in directories)
        {
            string? directoryName = Path.GetFileName(directory);
            if (string.IsNullOrEmpty(directoryName) || (directoryName.Length != 2 && directoryName.Length != 4))
                continue;
            LookForAbandoned(distinctFilteredIds, directory, directoryName);
        }
    }

    private void LookForAbandoned(ReadOnlyDictionary<int, List<LocationContainer<MetadataExtractor.Directory>>> idToLocationContainers, List<int> distinctFilteredIds)
    {
        List<string> renameCollection = new();
        foreach (KeyValuePair<int, List<LocationContainer<MetadataExtractor.Directory>>> keyValuePair in idToLocationContainers)
        {
            if (distinctFilteredIds.Contains(keyValuePair.Key))
                continue;
            foreach (LocationContainer<MetadataExtractor.Directory> locationContainer in keyValuePair.Value)
            {
                if (locationContainer.File.Contains('!'))
                    continue;
                renameCollection.Add(locationContainer.File);
            }
        }
        if (renameCollection.Any())
            IDirectory.MoveFiles(renameCollection, _Configuration.PropertyConfiguration.ResultContent, "(abd)");
    }

    private void LookForAbandoned(string bResultsFullGroupDirectory, Container[] containers, ReadOnlyDictionary<int, List<LocationContainer<MetadataExtractor.Directory>>> idToLocationContainers)
    {
        string[] directories;
        string? directoryName;
        string cResultsFullGroupDirectory;
        string dResultsFullGroupDirectory;
        string c2ResultsFullGroupDirectory;
        string d2ResultsFullGroupDirectory;
        List<int> distinctFilteredIds = Shared.Models.Stateless.Methods.IContainer.GetFilteredDistinctIds(_Configuration.PropertyConfiguration, containers);
        LookForAbandoned(idToLocationContainers, distinctFilteredIds);
        LookForAbandoned(bResultsFullGroupDirectory, distinctFilteredIds);
        foreach (string outputResolution in _Configuration.OutputResolutions)
        {
            (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution);
            directories = Directory.GetDirectories(cResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly);
            foreach (string directory in directories)
            {
                directoryName = Path.GetFileName(directory);
                if (string.IsNullOrEmpty(directoryName) || (directoryName.Length != 2 && directoryName.Length != 4))
                    continue;
                LookForAbandoned(distinctFilteredIds, directory, directoryName);
            }
            directories = Directory.GetDirectories(dResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly);
            foreach (string directory in directories)
            {
                directoryName = Path.GetFileName(directory);
                if (string.IsNullOrEmpty(directoryName) || (directoryName.Length != 2 && directoryName.Length != 4))
                    continue;
                LookForAbandoned(distinctFilteredIds, directory, directoryName);
            }
            directories = Directory.GetDirectories(d2ResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly);
            foreach (string directory in directories)
            {
                directoryName = Path.GetFileName(directory);
                if (string.IsNullOrEmpty(directoryName) || (directoryName.Length != 2 && directoryName.Length != 4))
                    continue;
                LookForAbandoned(distinctFilteredIds, directory, directoryName);
            }
        }
    }

    private Mapping[] GetMappings(Property.Models.Configuration propertyConfiguration, Container[] containers, MapLogic mapLogic, bool distinctItems)
    {
        Mapping[] results;
        int count = 0;
        Mapping mapping;
        bool anyValidFaces;
        string focusRelativePath;
        bool? isFocusRelativePath;
        bool? isIgnoreRelativePath;
        List<int> distinct = new();
        DateTime[] containerDateTimes;
        IEnumerable<Item> filteredItems;
        MappingFromItem mappingFromItem;
        List<Mapping> mappingCollection = new();
        foreach (Container container in containers)
        {
            if (!container.Items.Any())
                continue;
            filteredItems = Shared.Models.Stateless.Methods.IContainer.GetFilterItems(propertyConfiguration, container);
            if (!filteredItems.Any())
                continue;
            containerDateTimes = Shared.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems);
            focusRelativePath = Path.GetFullPath(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, _Configuration.FocusDirectory));
            isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath);
            isIgnoreRelativePath = !_Configuration.IgnoreRelativePaths.Any() ? null : _Configuration.IgnoreRelativePaths.Any(l => container.SourceDirectory.Contains(l)) && Shared.Models.Stateless.Methods.IContainer.IsIgnoreRelativePath(_Configuration.PropertyConfiguration, _Configuration.IgnoreRelativePaths, container.SourceDirectory);
            foreach (Item item in filteredItems)
            {
                if (item.Property?.Id is null || item.ResizedFileHolder is null)
                    continue;
                mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, item.ResizedFileHolder);
                if (distinctItems)
                {
                    if (distinct.Contains(item.Property.Id.Value))
                        continue;
                    distinct.Add(item.Property.Id.Value);
                }
                count++;
                anyValidFaces = false;
                foreach (Shared.Models.Face face in item.Faces)
                {
                    if (face.Mapping is null)
                        continue;
                    anyValidFaces = true;
                    mappingCollection.Add(face.Mapping);
                }
                if (!anyValidFaces)
                {
                    mapping = GetMapping(mapLogic, item, isFocusRelativePath, isIgnoreRelativePath, mappingFromItem);
                    mappingCollection.Add(mapping);
                }
            }
        }
        results = (from l in mappingCollection orderby l.MappingFromItem.Id select l).ToArray();
        return results;
    }

    private static void Verify(string eDistanceContentDirectory, List<Item> distinctFilteredItems)
    {
#if VerifyItem
        bool found;
        List<Item> notFound = new();
        foreach (Item item in distinctFilteredItems)
        {
            found = false;
            if (item.Property?.Id is null)
                continue;
            foreach (Mapping mapping in distinctFilteredMappingCollection)
            {
                if (mapping.MappingFromItem.Id != item.Property.Id.Value)
                    continue;
                found = true;
                break;
            }
            if (!found)
                notFound.Add(item);
        }
        if (notFound.Any())
            throw new NotSupportedException();
#endif
        string model;
        string fileName;
        string directory;
        bool? isWrongYear;
        List<DateTime> dateTimes;
        List<string> distinct = new();
        WindowsShortcut windowsShortcut;
        List<(string, string, string)> collection = new();
        foreach (Item item in distinctFilteredItems)
        {
            if (item.Property?.Id is null || item.ImageFileHolder.LastWriteTime is null)
                continue;
            if (item.IsNotUniqueAndNeedsReview is null || !item.IsNotUniqueAndNeedsReview.Value)
                continue;
            directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(item.IsNotUniqueAndNeedsReview)})", item.ImageFileHolder.NameWithoutExtension);
            fileName = Path.Combine(directory, $"{item.ImageFileHolder.Length} {item.ImageFileHolder.LastWriteTime.Value.Ticks}.lnk");
            collection.Add((item.ImageFileHolder.FullName, directory, fileName));
            if (distinct.Contains(directory))
                continue;
            distinct.Add(directory);
        }
        foreach (Item item in distinctFilteredItems)
        {
            if (item.Property?.Id is null || item.Property.DateTimeOriginal is null)
                continue;
            dateTimes = item.Property.GetDateTimes();
            (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.ImageFileHolder, item.Property.DateTimeOriginal, dateTimes);
            if (isWrongYear is null || !isWrongYear.Value)
                continue;
            // Remove-Item -LiteralPath "\\?\D:\Tmp\a\EX-Z70     "
            model = string.IsNullOrEmpty(item.Property.Model) ? "Unknown" : Regex.Replace(item.Property.Model.Trim(), @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]", "_");
            directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Item)})", item.Property.DateTimeOriginal.Value.Year.ToString(), model);
            fileName = item.IsNotUniqueAndNeedsReview is not null && item.IsNotUniqueAndNeedsReview.Value ? Path.Combine(directory, $"{item.ImageFileHolder.Name} {item.ImageFileHolder.Length}.lnk") : Path.Combine(directory, $"{item.ImageFileHolder.Name}.lnk");
            collection.Add((item.ImageFileHolder.FullName, directory, fileName));
            if (distinct.Contains(directory))
                continue;
            distinct.Add(directory);
        }
#if Mapping
        foreach (Mapping mapping in distinctFilteredMappingCollection)
        {
            if (mapping.MappingFromItem.IsWrongYear is null || !mapping.MappingFromItem.IsWrongYear.Value)
                continue;
            directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Mapping)})", mapping.MappingFromItem.MinimumDateTime.Year.ToString());
            fileName = Path.Combine(directory, $"{mapping.MappingFromItem.ResizedFileHolder.Name}.lnk");
            collection.Add((mapping.MappingFromItem.ResizedFileHolder.FullName, directory, fileName));
            if (distinct.Contains(directory))
                continue;
            distinct.Add(directory);
        }
#endif
        foreach (string distinctDirectory in distinct)
        {
            if (!Directory.Exists(distinctDirectory))
                _ = Directory.CreateDirectory(distinctDirectory);
        }
        foreach ((string path, string checkDirectory, string checkFile) in collection)
        {
            if (File.Exists(checkFile))
                continue;
            windowsShortcut = new() { Path = path };
            windowsShortcut.Save(checkFile);
            windowsShortcut.Dispose();
        }
    }

    private void Search(long ticks, ReadOnlyCollection<PersonContainer> personContainers, string argZero, string propertyRoot)
    {
        int t;
        int count;
        string message;
        MapLogic? mapLogic;
        Container[] containers;
        A_Property propertyLogic;
        string eDistanceContentDirectory;
        string? a2PeopleContentDirectory;
        string aResultsFullGroupDirectory;
        string bResultsFullGroupDirectory;
        string cResultsFullGroupDirectory;
        string dResultsFullGroupDirectory;
        string c2ResultsFullGroupDirectory;
        string d2ResultsFullGroupDirectory;
        string fPhotoPrismContentDirectory;
        const string fileSearchFilter = "*";
        string fPhotoPrismSingletonDirectory;
        bool filesCollectionCountIsOne = false;
        List<string[]>? filesCollection = null;
        const string directorySearchFilter = "*";
        Dictionary<long, List<int>> personKeyToIds;
        bool configurationOutputResolutionsHas = false;
        Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection;
        (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = GetResultsFullGroupDirectories();
        ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
        a2PeopleContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), "([])");
        eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent);
        string a2PeopleSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), _Configuration.PropertyConfiguration.ResultSingleton);
        TimeSpan eLastWriteTimeTimeSpan = new(ticks - new DirectoryInfo(eDistanceContentDirectory).LastWriteTime.Ticks);
        if (eLastWriteTimeTimeSpan.TotalDays > 1)
            mapLogic = null;
        else
            mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, personContainers, ticks, a2PeopleSingletonDirectory, eDistanceContentDirectory);
        foreach (string outputResolution in _Configuration.OutputResolutions)
        {
            if (outputResolution.Any(l => char.IsNumber(l)))
                continue;
            configurationOutputResolutionsHas = true;
            if (eLastWriteTimeTimeSpan.TotalDays < 1)
                break;
            ProgressBar progressBar;
            (cResultsFullGroupDirectory, _, _, _) = GetResultsFullGroupDirectories(outputResolution);
            IReadOnlyDictionary<string, string[]> fileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, new string[] { _Configuration.PropertyConfiguration.ResultContent });
            filesCollection = IDirectory.GetFilesCollection(_Configuration.PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter);
            message = $") Selecting for ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
            count = filesCollection.Select(l => l.Length).Sum();
            filesCollectionCountIsOne = filesCollection.Count == 1;
            progressBar = new(count, message, options);
            (string[] distinctDirectories, List<(FileHolder, string)> toDoCollection) = IDirectory.GetToDoCollection(_Configuration.PropertyConfiguration, filesCollection, fileGroups[_Configuration.PropertyConfiguration.ResultContent], () => progressBar.Tick());
            progressBar.Dispose();
            message = $") Copying to ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
            progressBar = new(count, message, options);
            _ = IDirectory.CopyOrMove(toDoCollection, move: false, moveBack: false, () => progressBar.Tick());
            progressBar.Dispose();
            break;
        }
        fPhotoPrismContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultContent);
        fPhotoPrismSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultSingleton);
        propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FileNameExtension, _Configuration.Reverse, aResultsFullGroupDirectory);
        if (filesCollectionCountIsOne)
        {
            if (filesCollection is null)
                throw new NullReferenceException(nameof(filesCollection));
            string resultsGroupDirectory;
            a2PeopleContentDirectory = null;
            eDistanceContentDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultContent);
            fPhotoPrismContentDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultContent);
            fPhotoPrismSingletonDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultSingleton);
            for (int i = 1; i < 10; i++)
            {
                resultsGroupDirectory = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, string.Empty, create: true);
                _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(resultsGroupDirectory);
            }
            argZero = SaveUrlAndGetNewRootDirectory(filesCollection.First());
            _Configuration.PropertyConfiguration.ChangeRootDirectory(argZero);
            (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = GetResultsFullGroupDirectories();
            propertyRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), create: false);
            propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FileNameExtension, _Configuration.Reverse, aResultsFullGroupDirectory);
        }
        if (configurationOutputResolutionsHas)
        {
            foreach (string outputResolution in _Configuration.OutputResolutions)
            {
                if (outputResolution.Any(l => char.IsNumber(l)))
                    continue;
                (cResultsFullGroupDirectory, _, _, _) = GetResultsFullGroupDirectories(outputResolution);
                filesCollection = IDirectory.GetFilesCollection(Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent, _Configuration.PropertyConfiguration.ResultAllInOne), directorySearchFilter, fileSearchFilter);
                count = filesCollection.Select(l => l.Length).Sum();
                break;
            }
        }
        if (filesCollection is null)
            throw new NullReferenceException(nameof(filesCollection));
        message = $") Building Container(s) - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
        using (ProgressBar progressBar = new(2, message, options))
        {
            progressBar.Tick();
            string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton);
            if (!Directory.Exists(aPropertySingletonDirectory))
                _ = Directory.CreateDirectory(aPropertySingletonDirectory);
            (t, containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory, filesCollection);
            progressBar.Tick();
        }
        fileNameToCollection = !Directory.Exists(fPhotoPrismSingletonDirectory) ? fileNameToCollection = new() : F_PhotoPrism.GetFileNameToCollection(fPhotoPrismSingletonDirectory);
        B_Metadata metadata = new(_Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory);
        mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, personContainers, ticks, a2PeopleSingletonDirectory, eDistanceContentDirectory);
        containers = Shared.Models.Stateless.Methods.IContainer.SortContainers(_Configuration.PropertyConfiguration, _Configuration.IgnoreRelativePaths, _ArgZeroIsConfigurationRootDirectory, argZero, containers);
        ReadOnlyDictionary<int, List<LocationContainer<MetadataExtractor.Directory>>> idToLocationContainers = mapLogic.GetIdToLocationContainers();
        FullDoWork(argZero, propertyRoot, ticks, aResultsFullGroupDirectory, bResultsFullGroupDirectory, t, containers, propertyLogic, metadata, fileNameToCollection, idToLocationContainers, mapLogic);
        if (_Configuration.LookForAbandoned)
            LookForAbandoned(bResultsFullGroupDirectory, containers, idToLocationContainers);
        _Distance.Clear();
        List<Item> distinctFilteredItems = Shared.Models.Stateless.Methods.IContainer.GetItems(_Configuration.PropertyConfiguration, containers, distinctItems: true, filterItems: true);
        Verify(eDistanceContentDirectory, distinctFilteredItems);
        List<Shared.Models.Face> distinctFilteredFaces = Map.Models.Stateless.Methods.IMapLogic.GetFaces(distinctFilteredItems);
        Mapping[] distinctFilteredMappingCollection = GetMappings(_Configuration.PropertyConfiguration, containers, mapLogic, distinctItems: true);
        int totalNotMapped = mapLogic.UpdateMappingFromPerson(distinctFilteredMappingCollection);
        string json = JsonSerializer.Serialize(distinctFilteredMappingCollection);
        File.WriteAllText(Path.Combine(eDistanceContentDirectory, $"{ticks}.json"), json);
        for (int i = 1; i < 5; i++)
            _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(eDistanceContentDirectory);
        foreach (string outputResolution in _Configuration.OutputResolutions)
        {
            if (_PropertyRootExistedBefore)
                break;
            personKeyToIds = mapLogic.GetPersonKeyToIds();
            if (!string.IsNullOrEmpty(a2PeopleContentDirectory) && _Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution))
                mapLogic.SaveShortcutsForOutputResolutionsPreMapLogic(eDistanceContentDirectory, personKeyToIds, distinctFilteredMappingCollection);
            if (!string.IsNullOrEmpty(a2PeopleContentDirectory) && _Configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions.Contains(outputResolution))
                mapLogic.SaveFilteredOriginalImagesFromJLinks(_Configuration.JLinks, personContainers, a2PeopleContentDirectory, personKeyToIds, distinctFilteredMappingCollection, totalNotMapped);
            (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution);
            if (_ArgZeroIsConfigurationRootDirectory
                && _Configuration.SaveResizedSubfiles
                && outputResolution == _Configuration.OutputResolutions[0]
                && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)
                && _Exceptions.Count == 0)
                MapLogic(ticks, containers, a2PeopleSingletonDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, fPhotoPrismContentDirectory, mapLogic, outputResolution, personKeyToIds, distinctFilteredFaces, distinctFilteredMappingCollection, totalNotMapped);
            if (_Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Any() && distinctFilteredMappingCollection.Any())
                _Random.Random(_Configuration.PropertyConfiguration, outputResolution, personKeyToIds, distinctFilteredMappingCollection);
            if (_IsEnvironment.Development)
                continue;
            if (!_IsEnvironment.Development)
            {
                _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(aResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton));
                _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(bResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton));
                _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton));
                if (_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution))
                    _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultCollection));
            }
        }
    }

}