public static ProcessStatus SyncArchive()
        {
            var status = new ProcessStatus();

            var newRecords = new List<Photo>();

            foreach (var photo in Repository.Photos())
            {
                if (!File.Exists(photo.ArchiveFullName))
                {
                    photo.DateDeleted = DateTime.Now;
                    photo.Status = PhotoStatus.Deleted;
                    status.PhotoRemoved();
                }
                else
                {
                    status.PhotoExists();
                }
            }

            var archivePath = Configuration.ArchivePath.TrimEnd('\\');
            var archive = new DirectoryInfo(archivePath);

            var extensions = Configuration.Extensions;

            var fileList = new List<FileInfo>();

            foreach (var extension in extensions)
            {
                var mask = string.Format("*.{0}", extension);

                var files = archive.GetFiles(mask, SearchOption.AllDirectories)
                    .Where(x => x.DirectoryName != null)
                    .ToList();

                fileList.AddRange(files);
            }

            Parallel.ForEach(fileList, file =>
                                    {
                                        var shelf = file.Directory.FullName.Replace(archivePath, string.Empty).TrimStart('\\');
                                        var filename = file.Name;

                                        if (!Repository.Photos().Any(x => x.Shelf == shelf && x.Filename == filename))
                                        {
                                            newRecords.Add(Photo.New(file, shelf));
                                            status.PhotoAdded();
                                        }
                                    });

            newRecords.Save();

            var dups = DeDupe();

            status.PhotoRemoved(dups);

            return status;
        }
        public static ProcessStatus Cleanup()
        {
            var status = new ProcessStatus();

            var photos = Repository.Photos()
                .Where(x => x.Status == PhotoStatus.Deleted || x.Status == PhotoStatus.Duplicated)
                .ToList();

            foreach (var photo in photos)
            {
                try
                {
                    if (File.Exists(photo.ArchiveFullName))
                    {
                        File.Delete(photo.ArchiveFullName);
                        status.PhotoRemoved();
                    }
                }
                catch (Exception)
                {
                    // swallow
                }

                var dir = Path.GetDirectoryName(photo.ArchiveFullName);

                if (string.IsNullOrEmpty(dir))
                {
                    continue;
                }

                var di = new DirectoryInfo(dir);

                if (di.Exists && di.GetFiles("*.*", SearchOption.AllDirectories).Length == 0)
                {
                    di.Delete(true);
                }
            }

            photos.Delete();

            return status;
        }
        public static ProcessStatus ExtractFromCamera(bool cleanCamera = false)
        {
            var status = new ProcessStatus();

            var cameraPath = Configuration.CameraPath.TrimEnd('\\');
            var archive = new DirectoryInfo(cameraPath);

            var extensions = Configuration.Extensions;

            var fileList = new List<FileInfo>();

            foreach (var extension in extensions)
            {
                var mask = string.Format("*.{0}", extension);

                var files = archive.GetFiles(mask, SearchOption.AllDirectories)
                    .Where(x => x.DirectoryName != null)
                    .ToList();

                fileList.AddRange(files);
            }

            if (fileList.Count == 0)
            {
                return status;
            }

            var shelfDate = DateTime.Now;

            var shelf = string.Format("{0:yyyyMMdd_hhmmss}", shelfDate);

            var shelfPath = Path.Combine(Configuration.ArchivePath, shelf);

            var newPhotos = new List<Photo>();

            foreach (var file in fileList)
            {
                var photo = Photo.New(file, shelf);

                if (Repository.Photos().Any(x => x.HashValue == photo.HashValue))
                {
                    status.PhotoExists();
                    continue;
                }

                if (!Directory.Exists(shelfPath))
                {
                    Directory.CreateDirectory(shelfPath);
                }

                bool success;

                try
                {
                    File.Copy(file.FullName, photo.ArchiveFullName);
                    success = true;

                }
                catch (Exception)
                {
                    success = false;
                }

                if (success)
                {
                    newPhotos.Add(photo);
                    status.PhotoAdded();

                    if (cleanCamera)
                    {
                        try
                        {
                            File.Delete(file.FullName);
                            status.PhotoRemoved();
                        }
                        catch (Exception)
                        {
                            // swallow
                        }
                    }
                }
            }

            newPhotos.Save();

            return status;
        }
        public static ProcessStatus UpdateFrame()
        {
            var status = new ProcessStatus();

            var size = new Size(Configuration.FrameWidth, Configuration.FrameHeight);

            var frame = new DirectoryInfo(Configuration.FramePath);

            var files = frame.GetFiles("*.*", SearchOption.AllDirectories)
                    .ToList();

            var filesToDelete = new List<FileInfo>();

            var photos = Repository.Photos()
                .Where(x => x.Status == PhotoStatus.Included)
                .ToList();

            foreach (var file in files)
            {
                if (photos.All(x => x.FrameFullName != file.FullName))
                {
                    filesToDelete.Add(file);
                    status.PhotoRemoved();
                }
            }

            filesToDelete.ForEach(x => File.Delete(x.FullName));

            var added = new List<Photo>();

            foreach (var photo in photos)
            {
                if (File.Exists(photo.FrameFullName))
                {
                    status.PhotoExists();
                    continue;
                }

                status.PhotoAdded();

                Image frameImage;

                using (var image = Image.FromFile(photo.ArchiveFullName))
                {
                    frameImage = ResizeImage(image, size);
                }

                frameImage.Save(photo.FrameFullName);
            }

            added.Save(false);

            var states = photos.Select(x => new PhotoFrameState
                                                {
                                                    PhotoID = x.ID,
                                                    DateAdded = DateTime.Now
                                                });

            states.Save();

            return status;
        }