/// <summary>
        /// Removes a <see cref="StorageFile"/> from this gallery.
        /// Note: Does not remove the file from the file system.
        /// </summary>
        /// <param name="mediaFilePath">The path of the file to remove.</param>
        public void RemoveFile(string mediaFilePath)
        {
            if (string.IsNullOrEmpty(mediaFilePath))
            {
                return;
            }

            var mediaDataToRemove = GetMediaDataFromPath(mediaFilePath, MediaDatas);

            if (mediaDataToRemove == null)
            {
                return;
            }

            var index = MediaDatas.IndexOf(mediaDataToRemove);

            if (index != -1)
            {
                MediaDatas.RemoveAt(index);
            }

            if (App.Files.Keys.Contains(mediaDataToRemove.Title))
            {
                App.Files.Remove(mediaDataToRemove.Title);
            }
            if (App.MediaDatas.Contains(mediaDataToRemove))
            {
                App.MediaDatas.Remove(mediaDataToRemove);
            }
        }
        public async Task EvaluateImagesOnline(IProgress <int> progress, CancellationToken cancellationToken)
        {
            // Sign into firebase.
            if (await FirebaseClient.Client.SignInPromptUserIfNecessary())
            {
                // Find all media datas without annotation data.
                var mediaDatasWithoutAnnotationData = MediaDatas.Where(data => data.Meta.AnnotationData == null);

                var evaluated = 0;
                using (new DisposableLogger(GalleryLog.EvaluateOnlineBegin, (sw) => GalleryLog.EvaluateOnlineEnd(sw, evaluated)))
                {
                    IsEvaluating = true;
                    await mediaDatasWithoutAnnotationData.ForEachAsyncConcurrent(async media =>
                    {
                        try
                        {
                            // Evaluate media thumbnail online.
                            await media.EvaluateOnlineAsync(FirebaseClient.accessToken);
                        }
                        catch (Exception e)
                        {
                            // Log Exception.
                            LifecycleLog.Exception(e);
                        }
                        finally
                        {
                            // Update and then report progress.
                            evaluated++;
                            progress.Report(evaluated);
                        }
                    }, cancellationToken, 5);

                    IsEvaluating = false;
                }

                // Save online results to database.
                await DatabaseUtils.SaveAllMetadatasAsync(MediaDatas);
            }
        }
        private async Task ProcessFileChange(StorageLibraryChange change)
        {
            // Temp variable used for instantiating StorageFiles for sorting if needed later
            //StorageFile newFile = null;
            var extension = Path.GetExtension(change.Path);

            switch (change.ChangeType)
            {
            // New File in the Library
            case StorageLibraryChangeType.Created:
            case StorageLibraryChangeType.MovedIntoLibrary:
                if (FileTypes.Extensions.Contains(extension, StringComparer.InvariantCultureIgnoreCase))
                {
                    try
                    {
                        // Don't add file to gallery if it already exists.
                        if (MediaDatas.Any(data => data.MediaFile.Path == change.Path))
                        {
                            return;
                        }

                        var mediaFile = (StorageFile)(await change.GetStorageItemAsync());

                        if (mediaFile == null)
                        {
                            return;
                        }

                        await AddFileAsync(ImageSize, mediaFile);
                    }
                    catch (Exception e)
                    {
                        // Log Exception.
                        LifecycleLog.Exception(e);
                    }
                }
                break;

            // Renamed file
            case StorageLibraryChangeType.MovedOrRenamed:
                if (FileTypes.Extensions.Contains(extension, StringComparer.InvariantCultureIgnoreCase))
                {
                    try
                    {
                        var mediaData = GetMediaDataFromPath(change.PreviousPath, MediaDatas);
                        var newName   = Path.GetFileName(change.Path);

                        // If no MediaData could be found in Gallery for this renamed item AND there is an entry for it in App.MediaDatas (lostMediaData)
                        // that probably means it has been renamed while the application was off, so update its path and add it to the gallery.
                        var lostMediaFile = GetMediaFileFromPath(change.Path, _lostMediaFiles);
                        if (mediaData == null && lostMediaFile != null)
                        {
                            var lostMetaData = GetMediaMetaDataFromPath(change.PreviousPath, App.DatabaseMetaDatas);
                            // Update path on metadata.
                            lostMetaData.MediaFilePath = change.Path;
                            // Get current MediaData associated with metadata.
                            var lostMediaData = await MediaData.CreateFromMediaMetadataAsync(lostMetaData);

                            // If file can still not be found then return.
                            if (lostMediaData == null)
                            {
                                return;
                            }
                            // Add file to gallery, including the lost media data.
                            await AddFileAsync(ImageSize, lostMediaFile, false, lostMediaData);
                        }
                        else if (mediaData == null)
                        {
                            return;
                        }

                        // Don't rename file in gallery if it is already renamed
                        if (MediaDatas.Any(data => data.Title == newName))
                        {
                            return;
                        }

                        var mediaFile = (StorageFile)(await change.GetStorageItemAsync());
                        // Run on UI thread.
                        await App.Current.NavigationService.Frame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                        {
                            // Update MediaFile of mediaData.
                            mediaData.MediaFile = mediaFile;
                        });

                        await mediaData.UpdateFileNameValuesAsync(newName);
                    }
                    catch (Exception e)
                    {
                        // Log Exception.
                        LifecycleLog.Exception(e);
                    }
                }
                break;

            // File Removed From Library
            case StorageLibraryChangeType.Deleted:
            case StorageLibraryChangeType.MovedOutOfLibrary:
                if (FileTypes.Extensions.Contains(extension, StringComparer.InvariantCultureIgnoreCase))
                {
                    try
                    {
                        // Only remove file from gallery if it exists in gallery.
                        if (MediaDatas.Any(data => data.MediaFile.Path == change.Path))
                        {
                            RemoveFile(change.Path);
                        }
                    }
                    catch (Exception e)
                    {
                        // Log Exception.
                        LifecycleLog.Exception(e);
                    }
                }
                break;

            // Modified Contents
            case StorageLibraryChangeType.ContentsChanged:
                if (FileTypes.Extensions.Contains(extension, StringComparer.InvariantCultureIgnoreCase))
                {
                    /*newFile = (StorageFile)(await change.GetStorageItemAsync());
                     * var imageProps = await newFile.Properties.GetImagePropertiesAsync();
                     * var dateTaken = imageProps.DateTaken;
                     * var dateModified = newFile.DateCreated;
                     * if (DateTimeOffset.Compare(dateTaken.AddSeconds(70), dateModified) > 0)
                     * {
                     *  // File was modified by the user
                     * }*/
                }
                break;

            // Ignored Cases
            case StorageLibraryChangeType.EncryptionChanged:
            case StorageLibraryChangeType.ContentsReplaced:
            case StorageLibraryChangeType.IndexingStatusChanged:
            default:
                // These are safe to ignore in this application
                break;
            }
        }
        /// <summary>
        /// Adds a new <see cref="StorageFile"/> to this gallery.
        /// </summary>
        /// <param name="imageSize">The size of the pregenerated thumbnail.</param>
        /// <param name="mediaFile">The file to add.</param>
        /// <param name="mediaData">Optional, the data of the file to add.</param>
        /// <returns></returns>
        public async Task AddFileAsync(int imageSize, StorageFile mediaFile, bool evaluate = true, MediaData data = null)
        {
            if (data == null)
            {
                data = MediaData.CreateFromStorageFile(mediaFile);
            }

            if (data is GifData)
            {
                GifCount++;
            }
            if (data is VideoData)
            {
                VideoCount++;
            }
            if (data is ImageData)
            {
                ImageCount++;
            }

            var imageProp = await mediaFile.Properties.GetImagePropertiesAsync();

            data.Meta.Height     = (int)imageProp.Height;
            data.Meta.Width      = (int)imageProp.Width;
            data.BasicProperties = await mediaFile.GetBasicPropertiesAsync();

            var properties = await mediaFile.Properties.RetrievePropertiesAsync
                             (
                new string[] { "System.DateModified" }
                             );

            var lastModified = properties["System.DateModified"];

            if (lastModified is DateTime lastModifiedDateTime)
            {
                data.Meta.LastModified = lastModifiedDateTime;
            }
            MediaDatas.Add(data);

            if (!App.Files.ContainsKey(mediaFile.Name))
            {
                App.Files.Add(mediaFile.Name, mediaFile);
            }

            await ApplyThumbnails(imageSize, mediaFile, data);

            if (evaluate)
            {
                // Make sure local model is in memory.
                await LoadLocalModel();

                // Evaluate locally.
                await data.EvaluateLocalAsync(_memeClassifier);

                // Scan for duplicates before evaluating online.
                var duplicateSets = new Dictionary <MediaData, List <MediaData> >();
                ScanForDuplicate(duplicateSets, data);
                // For each set of duplicates found...
                foreach (var duplicates in duplicateSets.Values)
                {
                    // ...have the user handle the duplicates.
                    DuplicateModal.DuplicateQueue.Enqueue(duplicates);
                }
                // Process enqeued duplicates.
                await DuplicateModal.ProcessQueueAsync();

                // If data or mediafile was removed during duplication handling, then don't try to evaluate online.
                var fileName = Path.GetFileName(data?.MediaFile?.Path);
                var exists   = await _folder.TryGetItemAsync(fileName);

                if (exists == null)
                {
                    return;
                }

                // Sign into firebase.
                if (await FirebaseClient.Client.SignInPromptUserIfNecessary())
                {
                    // Evaluate online.
                    await data.EvaluateOnlineAsync(FirebaseClient.accessToken);
                }
            }
        }