private void MirrorGroup(FileGroup group)
        {
            using (_engine = new MirrorMaker(_progress))
            {
                try
                {
                    _engine.StartingDirectory += OnStartingDirectory;
                    _engine.StartingFile      += OnStartingFile;
                    _engine.ItemHandlingError += OnItemHandlingError;

                    string destinationSubFolder = group.GetDestinationSubFolder(DestinationRootForThisUser);

                    _progress.WriteVerbose("[{0}] Source={1}", group.Name, group.RootFolder);
                    _progress.WriteVerbose("[{0}] Destination={1}", group.Name, destinationSubFolder);

                    //_engine.PreviewMode = false;
                    _engine.Run(group.RootFolder, destinationSubFolder);
                    if (_engine.WasCancelled)
                    {
                        _currentGroup.Disposition = FileGroup.DispositionChoice.Cancelled;
                    }
                    else
                    {
                        group.Disposition = FileGroup.DispositionChoice.WasBackedUp;
                    }
                }
                catch (IOException error)
                {
                    _gotIOExceptionProbablyDiskFull = true;
                    //enhance: we could clarify that it was partially backed up
                    _currentGroup.Disposition = FileGroup.DispositionChoice.NotEnoughRoom;
                    _progress.WriteWarning(error.Message);
                }

                if (GroupProgress != null)
                {
                    GroupProgress.Invoke();
                }
            }
        }
        public void GatherPreview()
        {
            /* enhance: we could try to deal with the situation where a lower-priority group
             * is hogging space from a newly enlarged higher-priority group. We'd have to
             * scan ahead, perhaps first collecting the current on-backup sizes of each.
             *
             * Then, as we go through the groups, we could keep going so long as deleting
             * some lower-priority group would allow us to keep going.
             */
            _alreadyAccountedFor = new HashSet <string>();
            _cancelRequested     = false;
            _files             = 0;
            ApprovedChangeInKB = 0;
            //PredictedChangeInKiloBytes = 0;
            _totalFilesThatWillBeBackedUp = 0;
            var limitHasBeenReached = false;

            foreach (var group in _groups)
            {
                if (_cancelRequested)
                {
                    break;
                }
                _currentGroup = group;                 //used by callbacks
                group.ClearStatistics();

                if (group.Disposition == FileGroup.DispositionChoice.Hide)
                {
                    _progress.WriteMessage("Skipping preview of group {0}", group.Name);
                    continue;
                }
                _progress.WriteMessage("Beginning preview of group {0}", group.Name);

                Debug.Assert(!string.IsNullOrEmpty(group.RootFolder), "should have been weeded out already");
                Debug.Assert(SafeIO.Directory.Exists(group.RootFolder), "should have been weeded out already");


                if (limitHasBeenReached)
                {
                    //don't even investigate.
                    //NB: there might actually be enough room, if this group is smaller
                    //than the first one which was too big. Or algorithm doesn't try
                    //to fit it in.
                    group.Disposition = FileGroup.DispositionChoice.NotEnoughRoom;

                    InvokeGroupProgress();
                    continue;
                }
                group.Disposition = FileGroup.DispositionChoice.Calculating;
                InvokeGroupProgress();

                string destinationSubFolder = group.GetDestinationSubFolder(DestinationRootForThisUser);
                using (_engine = new MirrorMaker(_progress))
                {
                    _engine.StartingDirectory += OnStartingDirectory;
                    _engine.StartingFile      += OnStartingFile;
                    _engine.Run(group.RootFolder, destinationSubFolder);

                    var groupsChangeInKB = (long)Math.Ceiling(group.NetChangeInBytes / 1024.0);
                    //is there room to fit in this whole group?
                    if (ApprovedChangeInKB + groupsChangeInKB < _totalAvailableOnDeviceInKilobytes)
                    {
                        ApprovedChangeInKB             = groupsChangeInKB;
                        _totalFilesThatWillBeBackedUp += group.UpdateFileCount + group.NewFileCount;
                        group.Disposition              = FileGroup.DispositionChoice.WillBeBackedUp;
                    }
                    else
                    {
                        limitHasBeenReached = true;                         //nb: remove if/when we go to the system below of deleting
                        group.Disposition   = FileGroup.DispositionChoice.NotEnoughRoom;
                    }

                    InvokeGroupProgress();
                    _files = 0;
                }
                _engine = null;
            }
        }
        public void Run()
        {
            _cancelRequested     = false;
            _alreadyAccountedFor = new HashSet <string>();

            try
            {
                foreach (var group in _groups)
                {
                    if (_cancelRequested)
                    {
                        break;
                    }
                    _currentGroup = group; //used by callbacks
                    try
                    {
                        if (group.Disposition == FileGroup.DispositionChoice.Hide)
                        {
                            _progress.WriteMessage("Skipping group {0}", group.Name);
                            continue;
                        }
                        if (_gotIOExceptionProbablyDiskFull)
                        {
                            group.Disposition = FileGroup.DispositionChoice.NotEnoughRoom;
                            continue;
                        }

                        _progress.WriteMessage("Beginning group {0}", group.Name);
                        if (group.Disposition == FileGroup.DispositionChoice.NotEnoughRoom)
                        {
                            continue;
                        }

                        group.ClearStatistics();
                        group.Disposition = FileGroup.DispositionChoice.Synchronizing;
                        InvokeGroupProgress();
                        MirrorGroup(group);
                        InvokeGroupProgress();
                    }
                    catch (Exception error)
                    {
                        var message = "Exception processing group: " + group.Name;
                        _progress.WriteError(message);
                        _progress.WriteException(error);
                        UsageReporter.ReportException(false, "backup", error, message);
                    }
                    _engine = null;
                    _progress.WriteMessage("Controller finished normally.");

                    //we don't want to send, say, 100 errors just because the drive is full. Let's just send one.
                    if (_firstErrorMessage != null)
                    {
                        UsageReporter.SendEvent("Backup", "file error(just the 1st one)", "file error(just the 1st one)", _firstErrorMessage, 0);
                    }
                }
//                Program.ReportToGoogleAnalytics("FinishedBackup", "filesBackedUp", _totalFilesThatWillBeBackedUp);

                /*                    Program.ReportToGoogleAnalytics("finishedUpdated","updated", _engine.UpdatedCount);
                 *  Program.ReportToGoogleAnalytics("finishedCreated", "created", _engine.CreatedCount);
                 *  Program.ReportToGoogleAnalytics("finishedDeleted", "deleted", _engine.DeletedCount);
                 */
            }
            finally
            {
                CleanupTempFiles();
            }
        }