/// <summary>
        /// Performa backup using the supplied configuration.
        /// </summary>
        /// <returns>BackupResult object describing the outcome.</returns>
        public BackupResult DoBackup()
        {
            this.OnError       += this.OnEvent_Error;
            this.OnInformation += this.OnEvent_Information;

            var result = new BackupResult();

            result.StartTime = DateTime.Now;

            try
            {
                Directory.CreateDirectory(config.BackupTarget.Path);
            }
            catch (Exception ex)
            {
                OnError?.Invoke(this,
                                new BackupEventArgs("Failed to create backup directory at {0}", config.BackupTarget.Path));
                result.Success   = false;
                result.Exception = ex;
                result.EndTime   = DateTime.Now;
                return(result);
            }

            if ((config.Options != null) && config.Options.CreateLogFile)
            {
                // Create log file.
                result.LogFilePath = Path.Combine(config.BackupTarget.Path, "backup.log");
                logFileWtr         = new StreamWriter(result.LogFilePath, false);
                OnInformation?.Invoke(this, new BackupEventArgs("Starting at {0}", result.StartTime));
            }

            try
            {
                foreach (var source in config.BackupSource)
                {
                    var excludeFileTypes = Combine(source.GetExcludeFileTypes(), config.Options.GetExcludeFileTypes());
                    var excludeDirs      = Combine(source.GetExcludeDirs(), config.Options.GetExcludeDirs());

                    // Create a top-level directory for each backup root in the configuration.
                    // We need to make sure these are unique.
                    var targetPath = BuildTargetPath(new DirectoryInfo(source.Path).Name,
                                                     config.BackupTarget.Path);
                    DoBackup(source.Path, targetPath, config.Options.Verify, excludeFileTypes, excludeDirs, result);
                }
                result.Success = true;
            }
            catch (Exception ex)
            {
                result.Success   = false;
                result.Exception = ex;
                if (logFileWtr != null)
                {
                    logFileWtr.WriteLine("{0} : Error: {1}", DateTime.Now, ex.Message);
                }
            }
            finally
            {
                result.EndTime = DateTime.Now;
                if (logFileWtr != null)
                {
                    logFileWtr.WriteLine("Finished at {0}", result.EndTime);
                    logFileWtr.Close();
                    logFileWtr = null;
                }
                this.OnError       -= this.OnEvent_Error;
                this.OnInformation -= this.OnEvent_Information;
            }

            return(result);
        }
        /// <summary>
        /// backup a directory.
        /// </summary>
        /// <param name="sourceDirPath">Source directory.</param>
        /// <param name="targetDirPath">Target directory.</param>
        /// <param name="verify">Verify each file copy?</param>
        /// <param name="excludeFileTypes">File types to exclude</param>
        /// <param name="excludeDirs">Directory names to exclude</param>
        /// <param name="progress">Backup result to be updated.</param>
        private void DoBackup(
            string sourceDirPath,
            string targetDirPath,
            bool verify,
            string[] excludeFileTypes,
            string[] excludeDirs,
            BackupResult progress)
        {
            Directory.CreateDirectory(targetDirPath);

            // Backup files.
            IEnumerable <string> filesEnum = null;

            try
            {
                filesEnum = EnumerateFiles(sourceDirPath, excludeFileTypes);
            }
            catch (System.UnauthorizedAccessException ex)
            {
                progress.DirectoriesSkipped += 1;
                OnError?.Invoke(this, new BackupEventArgs("{0} Directory skipped.", ex.Message));

                return;
            }

            foreach (var sourceFilePath in filesEnum)
            {
                var targetFilePath = Path.Combine(targetDirPath, sourceFilePath.Substring(sourceFilePath.LastIndexOf('\\') + 1));
                var sourceFileLen  = new FileInfo(sourceFilePath).Length;
                OnInformation?.Invoke(this, new BackupEventArgs("{0} => {1}", sourceFilePath, targetFilePath));
                string sourceHash, targetHash;
                try
                {
                    CopyFile(sourceFilePath, targetFilePath, copyBuff, verify, out sourceHash, out targetHash);
                }
                catch (Exception ex)
                {
                    if (ex is UnauthorizedAccessException || ex is IOException)
                    {
                        progress.FilesSkipped += 1;
                        OnError?.Invoke(this, new BackupEventArgs("{0} File skipped.", ex.Message));
                        continue;
                    }
                    else
                    {
                        throw;
                    }
                }
                if (sourceHash != targetHash)
                {
                    throw new IOException(string.Format("Backup verify failed for {0}", sourceFilePath));
                }
                progress.FilesCopied += 1;
                progress.BytesCopied += sourceFileLen;
                OnInformation?.Invoke(this,
                                      new BackupEventArgs("{0}, {1} => {2}, {3} bytes {4}",
                                                          DateTime.Now, sourceFilePath, targetFilePath, sourceFileLen,
                                                          verify ? sourceHash : ""));
            }

            // Recurse subdirectories.
            foreach (var sourceSubdirPath in EnumerateDirectories(sourceDirPath, excludeDirs))
            {
                var targetSubdirPath = Path.Combine(targetDirPath, sourceSubdirPath.Substring(sourceSubdirPath.LastIndexOf('\\') + 1));
                DoBackup(sourceSubdirPath, targetSubdirPath, verify, excludeFileTypes, excludeDirs, progress);
                progress.DirectoriesCopied += 1;
            }
        }