/// <summary>
        /// This method is called to clear the contents of an <see cref="SPBackupCatalog"/> to remove
        /// all backup sets it contains. This operation ensures that an spbrtoc.xml file remains with
        /// any remnants of restore and non-backup operations, but backup set references (and folders
        /// containing the sets) are deleted.
        /// <para>"Removal" of a backup set involves two different operations. The first operation is
        /// the actual updating of the table of contents file to reflect that the backup set is no
        /// longer present. The second part of the removal is the actual deletion of the backup
        /// subfolders housing the backup data being trimmed out.</para>
        /// </summary>
        /// <seealso cref="SPBackupCatalog"/>
        /// <seealso cref="SPCmdletRemoveSPBackupCatalog"/>
        /// <param name="workingCatalog">An <see cref="SPBackupCatalog"/> object that represents the
        /// backup set to be cleared.</param>
        /// <remarks>If an exception occurs, it is assumed that the calling method will trap it and
        /// handle it accordingly (including a possible re-throw).</remarks>
        public static void EmptyBackupCatalog(SPBackupCatalog workingCatalog)
        {
            // Use the XML for the backup catalog's TOC to grab all of the full backup SPHistoryObject
            // nodes and reverse sort them by directory number.
            XElement tocFile            = workingCatalog.TableOfContents;
            String   backupRoot         = workingCatalog.CatalogPath;
            var      fullHistoryObjects = tocFile.Elements().Where(ho =>
                                                                   (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") &&
                                                                   ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" &&
                                                                   ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE").OrderByDescending(fho =>
                                                                                                                                                Convert.ToInt32(fho.Element("SPDirectoryNumber").Value));

            // We need each of the folders we'll be deleting as part of the operation. This has to be
            // dumped to a list because subsequent deferred evaluation (when deleting folders) wouldn't
            // work properly following PHASE 1 (below).
            var backupSetsToDelete = tocFile.Elements().Where(ho =>
                                                              (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE")).Select(dho =>
                                                                                                                                  new { ID = dho.Element("SPId").Value, Folder = Path.Combine(backupRoot, dho.Element("SPDirectoryName").Value) }).ToList();

            // CLEANUP PHASE 1: clean-up the SPHistoryObject elements in the backup TOC file and save off the file
            tocFile.Elements().Where(ho =>
                                     (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE")).Remove();
            workingCatalog.TableOfContents = tocFile;
            workingCatalog.SaveTableOfContents();

            // CLEANUP PHASE 2: Iterate through the backup folders to delete and clear them out.
            foreach (var backupSet in backupSetsToDelete)
            {
                Directory.Delete(backupSet.Folder, true);
            }
        }
        /// <summary>
        /// This method is called when the backup set pointers contained within <paramref name="workingCatalog"/>
        /// need to be validated and corrected to make them consistent with the current path held by
        /// the SPBackupCatalog. This type of correction becomes necessary when a backup catalog that
        /// was created at one path (for example, "\\BackupServer\Backups") is moved to another path
        /// (for example, "\\BackupServer\Restores") and referenced.
        /// <para>In this scenario, folder pointers that are internal to the backup catalog will still
        /// point to "\\BackupServer\Backups." Use of this method will correct those pointers to reference
        /// the path currently specified by the <paramref name="newCatalogPath"/>.</para>
        /// </summary>
        /// <seealso cref="SPBackupCatalog"/>
        /// <seealso cref="SPCmdletSetSPBackupCatalog"/>
        /// <param name="workingCatalog">An <see cref="SPBackupCatalog"/> object that represents the
        /// backup set to be parsed and path-corrected.</param>
        /// <param name="newCatalogPath">A <c>String</c> containing the new root path that should be used
        /// for modifying the backup directory paths in the <paramref name="workingCatalog"/>.</param>
        /// <remarks>If an exception occurs, it is assumed that the calling method will trap it and
        /// handle it accordingly (including a possible re-throw).</remarks>
        public static void UpdateBackupCatalogPaths(SPBackupCatalog workingCatalog, String newCatalogPath)
        {
            // We need the XML structure for the table of contents file.
            XElement tocFile = workingCatalog.TableOfContents;

            // Perform the modifications
            XElement workingToc = UpdateBackupCatalogPaths(tocFile, newCatalogPath);

            // Assign the updated TOC structure back to the catalog and save it off.
            workingCatalog.TableOfContents = workingToc;
            workingCatalog.SaveTableOfContents();
        }
        /// <summary>
        /// This method is called to trim the contents of an <see cref="SPBackupCatalog"/> to retain
        /// as many backups as possible that allow the backup catalog to remain below the retention
        /// size specified by the <paramref name="retainSize"/> parameter. Older full backups (and any
        /// differential backups that depend on those full backups) are removed from the backup set
        /// from oldest to newest, and the overall size of the backup set is checked with each removal.
        /// If the backup catalog is too big after a removal or set of removals, the process is repeated.
        /// <para>"Removal" of a backup set involves two different operations. The first operation is
        /// the actual updating of the table of contents file to reflect that the backup set is no
        /// longer present. The second part of the removal is the actual deletion of the backup
        /// subfolders housing the backup data being trimmed out. As indicated, this process is
        /// carried out recursively until the size constraints are met.</para>
        /// </summary>
        /// <seealso cref="SPBackupCatalog"/>
        /// <seealso cref="SPCmdletRemoveSPBackupCatalog"/>
        /// <param name="workingCatalog">An <see cref="SPBackupCatalog"/> object that represents the
        /// backup set to be trimmed.</param>
        /// <param name="retainSize">An <c>Int64</c> that contains the total maximum size of all backups
        /// to retain in the <paramref name="workingCatalog"/>.</param>
        /// <param name="ignoreErrors">TRUE if backup set errors should be ignored for purposes of
        /// determining what to delete, FALSE if backup sets with errors should not count against the
        /// number of backups retained.</param>
        /// <remarks>If an exception occurs, it is assumed that the calling method will trap it and
        /// handle it accordingly (including a possible re-throw).</remarks>
        public static void TrimBackupCatalogSize(SPBackupCatalog workingCatalog, Int64 retainSize, Boolean ignoreErrors)
        {
            // Use the XML for the backup catalog's TOC to grab all of the full backup SPHistoryObject
            // nodes and reverse sort them by directory number.
            var backupRoot = workingCatalog.CatalogPath;
            var tocFile    = workingCatalog.TableOfContents;
            IOrderedEnumerable <XElement> fullHistoryObjects;

            // If we aren't ignoring errors, then we'll need to limit (potentially) the fullHistoryObjects
            // needed to just those backup sets that don't contain errors.
            if (ignoreErrors)
            {
                // All SPHistoryObjects are valid
                fullHistoryObjects = tocFile.Elements().Where(ho =>
                                                              (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") &&
                                                              ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" &&
                                                              ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE").OrderByDescending(fho =>
                                                                                                                                           Convert.ToInt32(fho.Element("SPDirectoryNumber").Value));
            }
            else
            {
                // Only SPHistoryObjects with a zero error count are valid
                fullHistoryObjects = tocFile.Elements().Where(ho =>
                                                              (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") &&
                                                              ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" &&
                                                              ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" &&
                                                              Convert.ToInt32(ho.Element("SPErrorCount").Value) == 0).OrderByDescending(fho =>
                                                                                                                                        Convert.ToInt32(fho.Element("SPDirectoryNumber").Value));
            }

            // We need to enter into a repeating delete cycle until one of two conditions are met:
            // 1. We have only one full backup set (plus any number of differentials) left
            // 2. The backup catalog is equal to our below the retention size supplied
            while (fullHistoryObjects.Count() > 1 && workingCatalog.CatalogSize > retainSize)
            {
                // Get the ID of the next-to-the-last full backup set; i.e., the last full backup
                // set that will remain in the catalog after the delete. Everything below it in the
                // directory number sequence will be wiped.
                var lastDirNumber = Convert.ToInt32(fullHistoryObjects.ElementAt(fullHistoryObjects.Count() - 2).Element("SPDirectoryNumber").Value);

                // We need each of the folders we'll be deleting as part of the operation. This has to be
                // dumped to a list because subsequent deferred evaluation (when deleting folders) wouldn't
                // work properly following PHASE 1 (below).
                var backupSetsToDelete = tocFile.Elements().Where(ho =>
                                                                  (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" &&
                                                                   Convert.ToInt32(ho.Element("SPDirectoryNumber").Value) < lastDirNumber)).Select(dho =>
                                                                                                                                                   new { ID = dho.Element("SPId").Value, Folder = Path.Combine(backupRoot, dho.Element("SPDirectoryName").Value) }).ToList();

                // CLEANUP PHASE 1: clean-up the SPHistoryObject elements in the backup TOC file and save off the file
                tocFile.Elements().Where(ho =>
                                         (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" &&
                                          Convert.ToInt32(ho.Element("SPDirectoryNumber").Value) < lastDirNumber)).Remove();
                workingCatalog.TableOfContents = tocFile;
                workingCatalog.SaveTableOfContents();

                // CLEANUP PHASE 2: Iterate through the backup folders to delete and clear them out.
                foreach (var backupSet in backupSetsToDelete)
                {
                    Directory.Delete(backupSet.Folder, true);
                }

                // Refresh the working catalog and re-query for the needed objects.
                workingCatalog.Refresh();
                if (ignoreErrors)
                {
                    // All SPHistoryObjects are valid
                    fullHistoryObjects = tocFile.Elements().Where(ho =>
                                                                  (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") &&
                                                                  ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" &&
                                                                  ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE").OrderByDescending(fho =>
                                                                                                                                               Convert.ToInt32(fho.Element("SPDirectoryNumber").Value));
                }
                else
                {
                    // Only SPHistoryObjects with a zero error count are valid
                    fullHistoryObjects = tocFile.Elements().Where(ho =>
                                                                  (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") &&
                                                                  ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" &&
                                                                  ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" &&
                                                                  Convert.ToInt32(ho.Element("SPErrorCount").Value) == 0).OrderByDescending(fho =>
                                                                                                                                            Convert.ToInt32(fho.Element("SPDirectoryNumber").Value));
                }
            }
        }
        /// <summary>
        /// This method is called to trim the contents of an <see cref="SPBackupCatalog"/> to retain
        /// only a certain number of full backups. The method is responsible for ensuring that only
        /// the most recent number of full backups specified are retained. Older full backups (and any
        /// differential backups that depend on those full backups) are removed from the backup set.
        /// <para>"Removal" of a backup set involves two different operations. The first operation is
        /// the actual updating of the table of contents file to reflect that the backup set is no
        /// longer present. The second part of the removal is the actual deletion of the backup
        /// subfolders housing the backup data being trimmed out.</para>
        /// </summary>
        /// <seealso cref="SPBackupCatalog"/>
        /// <seealso cref="SPCmdletRemoveSPBackupCatalog"/>
        /// <param name="workingCatalog">An <see cref="SPBackupCatalog"/> object that represents the
        /// backup set to be trimmed.</param>
        /// <param name="retainCount">An <c>Int32</c> that contains the total number of full backups
        /// to retain in the <paramref name="workingCatalog"/>.</param>
        /// <param name="ignoreErrors">TRUE if backup set errors should be ignored for purposes of
        /// determining what to delete, FALSE if backup sets with errors should not count against the
        /// number of backups retained.</param>
        /// <remarks>If an exception occurs, it is assumed that the calling method will trap it and
        /// handle it accordingly (including a possible re-throw).</remarks>
        public static void TrimBackupCatalogCount(SPBackupCatalog workingCatalog, Int32 retainCount, Boolean ignoreErrors)
        {
            // Use the XML for the backup catalog's TOC to grab all of the full backup SPHistoryObject
            // nodes and reverse sort them by directory number.
            var tocFile = workingCatalog.TableOfContents;
            IOrderedEnumerable <XElement> fullHistoryObjects;

            // If we aren't ignoring errors, then we'll need to limit (potentially) the fullHistoryObjects
            // needed to just those backup sets that don't contain errors.
            if (ignoreErrors)
            {
                // All SPHistoryObjects are valid
                fullHistoryObjects = tocFile.Elements().Where(ho =>
                                                              (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") &&
                                                              ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" &&
                                                              ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE").OrderByDescending(fho =>
                                                                                                                                           Convert.ToInt32(fho.Element("SPDirectoryNumber").Value));
            }
            else
            {
                // Only SPHistoryObjects with a zero error count are valid
                fullHistoryObjects = tocFile.Elements().Where(ho =>
                                                              (ho.Element("SPBackupMethod").Value.Trim().ToUpper() == "FULL") &&
                                                              ho.Element("SPConfigurationOnly").Value.Trim().ToUpper() == "FALSE" &&
                                                              ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" &&
                                                              Convert.ToInt32(ho.Element("SPErrorCount").Value) == 0).OrderByDescending(fho =>
                                                                                                                                        Convert.ToInt32(fho.Element("SPDirectoryNumber").Value));
            }

            // If we've got more full history objects than our count, then it's time to do some trimming.
            if (fullHistoryObjects.Count() > retainCount)
            {
                // We now have all of the SPHistoryObject nodes that correspond to full backups, and they
                // are sorted from most recent to oldest. We want to keep the most recent ones (number
                // specified by retainCount) and dump everything else.
                //
                // Get the directory number of the last full backup we want to keep; anything below that
                // will be deleted. We'll also get a quick reference to the backup root folder.
                Int32  lastDirNumber = Convert.ToInt32(fullHistoryObjects.ElementAt(retainCount - 1).Element("SPDirectoryNumber").Value);
                String backupRoot    = workingCatalog.CatalogPath;

                // We need each of the folders we'll be deleting as part of the operation. This has to be
                // dumped to a list because subsequent deferred evaluation (when deleting folders) wouldn't
                // work properly following PHASE 1 (below).
                var backupSetsToDelete = tocFile.Elements().Where(ho =>
                                                                  (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" &&
                                                                   Convert.ToInt32(ho.Element("SPDirectoryNumber").Value) < lastDirNumber)).Select(dho =>
                                                                                                                                                   new { ID = dho.Element("SPId").Value, Folder = Path.Combine(backupRoot, dho.Element("SPDirectoryName").Value) }).ToList();

                // CLEANUP PHASE 1: clean-up the SPHistoryObject elements in the backup TOC file and save off the file
                tocFile.Elements().Where(ho =>
                                         (ho.Element("SPIsBackup").Value.Trim().ToUpper() == "TRUE" &&
                                          Convert.ToInt32(ho.Element("SPDirectoryNumber").Value) < lastDirNumber)).Remove();
                workingCatalog.TableOfContents = tocFile;
                workingCatalog.SaveTableOfContents();

                // CLEANUP PHASE 2: Iterate through the backup folders to delete and clear them out.
                foreach (var backupSet in backupSetsToDelete)
                {
                    Directory.Delete(backupSet.Folder, true);
                }
            }
        }