/// <summary> Recalculates all the checksums for associated files </summary>
        /// <param name="Recalculate_All"> Flag indicates whether to recalculate all files, regardless of need </param>
        /// <param name="Progress_Delegate"> Progress delegate is called to update any progress displays </param>
        public void Calculate_Checksum(bool Recalculate_All, New_SobekCM_Bib_Package_Progress_Task_Group Progress_Delegate)
        {
            // Get the list of all pages from this division trees
            List<abstract_TreeNode> pageNodes = physicalDivisionTree.Pages_PreOrder;
            List<abstract_TreeNode> fileNodes = downloadDivisionTree.Pages_PreOrder;

            // Update progress
            if (Progress_Delegate != null)
            {
                Progress_Delegate("Calculating checksums", "Checking all files", 0, 2);
            }

            // Count how many pages to calculate for
            int total_files = 0;
            foreach (Page_TreeNode pageNode in pageNodes)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in pageNode.Files)
                {
                    // Recalculate this file info?
                    if ((Recalculate_All) || ((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0)))
                    {
                        total_files++;
                    }
                }
            }
            foreach (Page_TreeNode pageNode in fileNodes)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in pageNode.Files)
                {
                    // Recalculate this file info?
                    if ((Recalculate_All) || ((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0)))
                    {
                        total_files++;
                    }
                }
            }

            if (total_files == 0)
                return;

            // Now caulcate
            int file_count = 0;
            FileMD5 checksummer = new FileMD5();
            foreach (Page_TreeNode pageNode in pageNodes)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in pageNode.Files)
                {
                    // Recalculate this file info?
                    if ((thisFile.METS_LocType == SobekCM_File_Info_Type_Enum.SYSTEM) && ((Recalculate_All) || ((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0))))
                    {
                        // Perform in a try catch
                        try
                        {
                            // Get the size first
                            FileInfo thisFileInfo = new FileInfo(source_directory + "/" + thisFile.System_Name);
                            thisFile.Size = thisFileInfo.Length;

                            // Get the checksum, if it doesn't exist
                            if ((String.IsNullOrEmpty(thisFile.Checksum)) || (Recalculate_All))
                            {
                                thisFile.Checksum = checksummer.Calculate_Checksum(source_directory + "/" + thisFile.System_Name);
                                thisFile.Checksum_Type = "MD5";
                            }
                        }
                        catch {}

                        file_count++;

                        // Update progress
                        if (Progress_Delegate != null)
                        {
                            Progress_Delegate("Calculating checksums", "Calculating '" + thisFile.System_Name + "'", file_count, total_files);
                        }
                    }
                }
            }
            foreach (Page_TreeNode pageNode in fileNodes)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in pageNode.Files)
                {
                    // Recalculate this file info?
                    if ((thisFile.METS_LocType == SobekCM_File_Info_Type_Enum.SYSTEM) && ((Recalculate_All) || ((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0))))
                    {
                        // Perform in a try catch
                        try
                        {
                            // Get the size first
                            FileInfo thisFileInfo = new FileInfo(source_directory + "/" + thisFile.System_Name);
                            thisFile.Size = thisFileInfo.Length;

                            // Get the checksum, if it doesn't exist
                            if ((String.IsNullOrEmpty(thisFile.Checksum)) || (Recalculate_All))
                            {
                                thisFile.Checksum = checksummer.Calculate_Checksum(source_directory + "/" + thisFile.System_Name);
                                thisFile.Checksum_Type = "MD5";
                            }
                        }
                        catch{}

                        file_count++;

                        // Update progress
                        if (Progress_Delegate != null)
                        {
                            Progress_Delegate("Calculating checksums", "Calculating '" + thisFile.System_Name + "'", file_count, total_files);
                        }
                    }
                }
            }

            // Update progress
            if (Progress_Delegate != null)
            {
                Progress_Delegate("Checksum Complete", "Checksum Complete", file_count, total_files);
            }
        }
        /// <summary> Writes the formatted metadata from the provided item to a TextWriter (usually to an output stream) </summary>
        /// <param name="Output_Stream"></param>
        /// <param name="Item_To_Save"> Package with all the metadata to save </param>
        /// <param name="Options"> Dictionary of any options which this metadata reader/writer may utilize </param>
        /// <param name="Error_Message">[OUTPUT] Explanation of the error, if an error occurs during write </param>
        /// <returns>TRUE if successful, otherwise FALSE </returns>
        /// <remarks> OPTIONS: Accepts 'METS_File_ReaderWriter:METS_Writing_Profile', otherwise the default METS
        /// writing profile is utilized. </remarks>
        public bool Write_Metadata(TextWriter Output_Stream, SobekCM_Item Item_To_Save, Dictionary<string, object> Options, out string Error_Message)
        {
            // PERHAPS MAKE THESE OPTIONS?
            List<string> mimes_to_exclude = new List<string>();
            const bool MINIMIZE_FILE_SIZE = false;

            // Get the METS writing profile
            METS_Writing_Profile profile = ResourceObjectSettings.MetadataConfig.Default_METS_Writing_Profile;


            // Set default error outpt message
            Error_Message = String.Empty;

            // Ensure the METS ID is set from BibID and VID
            if (Item_To_Save.METS_Header.ObjectID.Length == 0)
            {
                Item_To_Save.METS_Header.ObjectID = Item_To_Save.BibID + "_" + Item_To_Save.VID;
                if (Item_To_Save.VID.Length == 0)
                    Item_To_Save.METS_Header.ObjectID = Item_To_Save.BibID;
            }

            // If this is bib level, it is different
            if ((Item_To_Save.VID == "*****") || (Item_To_Save.METS_Header.RecordStatus_Enum == METS_Record_Status.BIB_LEVEL))
                Item_To_Save.METS_Header.ObjectID = Item_To_Save.BibID;

            // If this is juat a project XML, do this differently
            if (Item_To_Save.Bib_Info.SobekCM_Type == TypeOfResource_SobekCM_Enum.Project)
            {
                Item_To_Save.Bib_Info.Main_Title.Title = "Project level metadata for '" + Item_To_Save.BibID + "'";
                Item_To_Save.METS_Header.ObjectID = Item_To_Save.BibID;
            }

            // Ensure the source code appears in the METS header correctly
            Item_To_Save.METS_Header.Creator_Organization = Item_To_Save.Bib_Info.Source.Code;
            if ((Item_To_Save.Bib_Info.Source.Statement.Length > 0) && (String.Compare(Item_To_Save.Bib_Info.Source.Statement, Item_To_Save.Bib_Info.Source.Code, StringComparison.OrdinalIgnoreCase) != 0))
            {
                Item_To_Save.METS_Header.Creator_Organization = Item_To_Save.Bib_Info.Source.Code + "," + Item_To_Save.Bib_Info.Source.XML_Safe_Statement;
            }

            // Get the list of divisions from the physical tree and the download/other file tre
            List<abstract_TreeNode> physicalDivisions = Item_To_Save.Divisions.Physical_Tree.Divisions_PreOrder;
            List<abstract_TreeNode> downloadDivisions = Item_To_Save.Divisions.Download_Tree.Divisions_PreOrder;
            List<abstract_TreeNode> allDivisions = new List<abstract_TreeNode>();
            List<SobekCM_File_Info> allFiles = new List<SobekCM_File_Info>();

            #region Prepare the list of divisions and files to be written by assigning proper ID's and groupid's to all

            // Create some private variables for this
            int page_and_group_number = 1;
            int division_number = 1;
            bool hasPageFiles = false;
            bool hasDownloadFiles = false;
            Dictionary<string, List<SobekCM_File_Info>> mimeHash = new Dictionary<string, List<SobekCM_File_Info>>();
            List<string> fileids_used = new List<string>();

            // Clear any existing ID's here since the ID's are only used for writing METS files
            foreach (abstract_TreeNode thisNode in physicalDivisions)
            {
                thisNode.ID = String.Empty;
                thisNode.DMDID = String.Empty;
                thisNode.ADMID = String.Empty;
            }

            // First, assign group numbers for all the files on each page (physical or other)
            // Group numbers in the METS file correspond to files on the same page (physical or other)
            // At the same time, we will build the list of all files and files by mime type
            foreach (abstract_TreeNode thisNode in physicalDivisions)
            {
                // If this node was already hit (perhaps if a div has two parent divs), 
                // then skip it
                if (thisNode.ID.Length > 0)
                    continue;

                // Add this to the list of all nodes
                allDivisions.Add(thisNode);

                // Was this a PAGE or a DIVISION node?
                if (thisNode.Page)
                {
                    // Get this page node
                    Page_TreeNode pageNode = (Page_TreeNode) thisNode;

                    // Only do anything if there are actually any files here
                    if ((pageNode.Files != null) && (pageNode.Files.Count > 0))
                    {

                        // Set the page ID here
                        pageNode.ID = "PAGE" + page_and_group_number;

                        // Step through any files under this page
                        foreach (SobekCM_File_Info thisFile in pageNode.Files)
                        {
                            // Set the ADMID and DMDID to empty in preparation for METS writing
                            thisFile.ADMID = String.Empty;
                            thisFile.DMDID = String.Empty;

                            // If no file name, skip this
                            if (String.IsNullOrEmpty(thisFile.System_Name))
                                continue;

                            // Get this file extension and MIME type
                            string fileExtension = thisFile.File_Extension;
                            string mimetype = thisFile.MIME_Type(thisFile.File_Extension);

                            // If this is going to be excluded from appearing in the METS file, just skip 
                            // it here as well.
                            if (!mimes_to_exclude.Contains(mimetype))
                            {
                                // Set the group number on this file
                                thisFile.Group_Number = "G" + page_and_group_number;

                                // Set the ID for this file as well
                                switch (mimetype)
                                {
                                    case "image/tiff":
                                        thisFile.ID = "TIF" + page_and_group_number;
                                        break;
                                    case "text/plain":
                                        thisFile.ID = "TXT" + page_and_group_number;
                                        break;
                                    case "image/jpeg":
                                        if (thisFile.System_Name.ToLower().IndexOf("thm.jp") > 0)
                                        {
                                            thisFile.ID = "THUMB" + page_and_group_number;
                                            mimetype = mimetype + "-thumbnails";
                                        }
                                        else
                                            thisFile.ID = "JPEG" + page_and_group_number;
                                        break;
                                    case "image/gif":
                                        if (thisFile.System_Name.ToLower().IndexOf("thm.gif") > 0)
                                        {
                                            thisFile.ID = "THUMB" + page_and_group_number;
                                            mimetype = mimetype + "-thumbnails";
                                        }
                                        else
                                            thisFile.ID = "GIF" + page_and_group_number;
                                        break;
                                    case "image/jp2":
                                        thisFile.ID = "JP2" + page_and_group_number;
                                        break;
                                    default:
                                        if (fileExtension.Length > 0)
                                        {
                                            thisFile.ID = fileExtension + page_and_group_number;
                                        }
                                        else
                                        {
                                            thisFile.ID = "NOEXT" + page_and_group_number;
                                        }
                                        break;
                                }

                                // Ensure this fileid is really unique.  It may not be if there are multiple
                                // files of the same mime-type in the same page.  (such as 0001.jpg and 0001.qc.jpg)
                                if (fileids_used.Contains(thisFile.ID))
                                {
                                    int count = 2;
                                    while (fileids_used.Contains(thisFile.ID + "." + count))
                                        count++;
                                    thisFile.ID = thisFile.ID + "." + count;
                                }

                                // Save this file id
                                fileids_used.Add(thisFile.ID);

                                // Also add to the list of files
                                allFiles.Add(thisFile);

                                // Also ensure we know there are page image files
                                hasPageFiles = true;


                                // If this is a new MIME type, add it, else just save this file in the MIME hash
                                if (!mimeHash.ContainsKey(mimetype))
                                {
                                    List<SobekCM_File_Info> newList = new List<SobekCM_File_Info> {thisFile};
                                    mimeHash[mimetype] = newList;
                                }
                                else
                                {
                                    mimeHash[mimetype].Add(thisFile);
                                }
                            }
                        }

                        // Prepare for the next page
                        page_and_group_number++;
                    }
                    else
                    {
                        // Page has no files, so it should be skipped when written
                        pageNode.ID = "SKIP";
                    }
                }
                else
                {
                    // This node is a DIVISION (non-page)
                    thisNode.ID = "PDIV" + division_number;
                    division_number++;
                }
            }


            // Clear any existing ID's here since the ID's are only used for writing METS files
            foreach (abstract_TreeNode thisNode in downloadDivisions)
            {
                thisNode.ID = String.Empty;
                thisNode.DMDID = String.Empty;
                thisNode.ADMID = String.Empty;
            }

            // Now, do the same thing for the download/other files division tree
            // Group numbers in the METS file correspond to files on the same page (physical or other)
            // At the same time, we will build the list of all files and files by mime type
            page_and_group_number = 1;
            division_number = 1;
            foreach (abstract_TreeNode thisNode in downloadDivisions)
            {
                // If this node was already hit (perhaps if a div has two parent divs), 
                // then skip it
                if (thisNode.ID.Length > 0)
                    continue;

                // Add this to the list of all nodes
                allDivisions.Add(thisNode);

                // Was this a PAGE or a DIVISION node?
                if (thisNode.Page)
                {
                    // Get this page node
                    Page_TreeNode pageNode = (Page_TreeNode) thisNode;

                    // Only do anything if there are actually any files here
                    if ((pageNode.Files != null) && (pageNode.Files.Count > 0))
                    {

                        // Set the page ID here
                        pageNode.ID = "FILES" + page_and_group_number;

                        // Step through any files under this page
                        foreach (SobekCM_File_Info thisFile in pageNode.Files)
                        {
                            // Set the ADMID and DMDID to empty in preparation for METS writing
                            thisFile.ADMID = String.Empty;
                            thisFile.DMDID = String.Empty;

                            // If no file name, skip this
                            if (String.IsNullOrEmpty(thisFile.System_Name))
                                continue;

                            // Get this file extension and MIME type
                            string fileExtension = thisFile.File_Extension;
                            string mimetype = thisFile.MIME_Type(thisFile.File_Extension);

                            // If this is going to be excluded from appearing in the METS file, just skip 
                            // it here as well.
                            if (!mimes_to_exclude.Contains(mimetype))
                            {
                                // Set the group number on this file
                                thisFile.Group_Number = "G" + page_and_group_number;

                                // Set the ID for this file as well
                                switch (mimetype)
                                {
                                    case "image/tiff":
                                        thisFile.ID = "TIF" + page_and_group_number;
                                        break;
                                    case "text/plain":
                                        thisFile.ID = "TXT" + page_and_group_number;
                                        break;
                                    case "image/jpeg":
                                        if (thisFile.System_Name.ToLower().IndexOf("thm.jp") > 0)
                                        {
                                            thisFile.ID = "THUMB" + page_and_group_number;
                                            mimetype = mimetype + "-thumbnails";
                                        }
                                        else
                                            thisFile.ID = "JPEG" + page_and_group_number;
                                        break;
                                    case "image/gif":
                                        if (thisFile.System_Name.ToLower().IndexOf("thm.gif") > 0)
                                        {
                                            thisFile.ID = "THUMB" + page_and_group_number;
                                            mimetype = mimetype + "-thumbnails";
                                        }
                                        else
                                            thisFile.ID = "GIF" + page_and_group_number;
                                        break;
                                    case "image/jp2":
                                        thisFile.ID = "JP2" + page_and_group_number;
                                        break;
                                    default:
                                        if (fileExtension.Length > 0)
                                        {
                                            thisFile.ID = fileExtension + page_and_group_number;
                                        }
                                        else
                                        {
                                            thisFile.ID = "NOEXT" + page_and_group_number;
                                        }
                                        break;
                                }

                                // Ensure this fileid is really unique.  It may not be if there are multiple
                                // files of the same mime-type in the same page.  (such as 0001.jpg and 0001.qc.jpg)
                                if (fileids_used.Contains(thisFile.ID))
                                {
                                    int count = 2;
                                    while (fileids_used.Contains(thisFile.ID + "." + count))
                                        count++;
                                    thisFile.ID = thisFile.ID + "." + count;
                                }

                                // Save this file id
                                fileids_used.Add(thisFile.ID);

                                // Also add to the list of files
                                allFiles.Add(thisFile);

                                // Also ensure we know there are page image files
                                hasDownloadFiles = true;


                                // If this is a new MIME type, add it, else just save this file in the MIME hash
                                if (!mimeHash.ContainsKey(mimetype))
                                {
                                    List<SobekCM_File_Info> newList = new List<SobekCM_File_Info> {thisFile};
                                    mimeHash[mimetype] = newList;
                                }
                                else
                                {
                                    mimeHash[mimetype].Add(thisFile);
                                }
                            }
                        }

                        // Prepare for the next page
                        page_and_group_number++;
                    }
                    else
                    {
                        // Page has no files, so it should be skipped when written
                        pageNode.ID = "SKIP";
                    }
                }
                else
                {
                    // This node is a DIVISION (non-page)
                    thisNode.ID = "ODIV" + division_number;
                    division_number++;
                }
            }

            #endregion

            // Add the XML declaration
            Output_Stream.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>");

            #region Write some processing instructions requested by Florida SUS's / FLVC (hope to deprecate)

            // Some special code for setting processing parameters
            bool daitssWriterIncluded = false;
            foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Package_Level_AmdSec_Writer_Configs)
            {
                if (thisConfig.Code_Class == "DAITSS_METS_amdSec_ReaderWriter")
                {
                    daitssWriterIncluded = true;
                    break;
                }
            }
            if (daitssWriterIncluded)
            {
                DAITSS_Info daitssInfo = Item_To_Save.Get_Metadata_Module(GlobalVar.DAITSS_METADATA_MODULE_KEY) as DAITSS_Info;
                if (daitssInfo != null)
                {
                    if ((daitssInfo.hasData) && (daitssInfo.toArchive))
                    {
                        Output_Stream.WriteLine("<?fcla fda=\"yes\"?>");
                    }
                    else
                    {
                        Output_Stream.WriteLine("<?fcla fda=\"no\"?>");
                    }
                }
            }

            #endregion

            // Add a remark here with the title and type
            Output_Stream.WriteLine("<!--  " + Item_To_Save.Bib_Info.Main_Title.Title_XML.Replace("-", " ") + " ( " + Item_To_Save.Bib_Info.SobekCM_Type_String + " ) -->");

            // Add the METS declaration information
            Output_Stream.WriteLine("<METS:mets OBJID=\"" + Item_To_Save.METS_Header.ObjectID + "\"");
            Output_Stream.WriteLine("  xmlns:METS=\"http://www.loc.gov/METS/\"");
            Output_Stream.WriteLine("  xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
            Output_Stream.WriteLine("  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");

            #region Add XMLNS and schema locations for active METS section reader/writers with data to write

            // Collect all the xmlns and schema locations
            List<string> xmlnsList = new List<string>();
            List<string> schemaLocList = new List<string>();
            foreach (METS_Section_ReaderWriter_Config thisRWconfig in profile.Package_Level_AmdSec_Writer_Configs)
            {
                iPackage_amdSec_ReaderWriter thisRw = (iPackage_amdSec_ReaderWriter) thisRWconfig.ReaderWriterObject;
                if (thisRw.Schema_Reference_Required_Package(Item_To_Save))
                {
                    string[] rwXmlns = thisRw.Schema_Namespace(Item_To_Save);
                    string[] rwSchema = thisRw.Schema_Location(Item_To_Save);
                    foreach (string thisValue in rwXmlns)
                    {
                        if (!xmlnsList.Contains(thisValue))
                            xmlnsList.Add(thisValue);
                    }
                    foreach (string thisValue in rwSchema)
                    {
                        if (!schemaLocList.Contains(thisValue))
                            schemaLocList.Add(thisValue);
                    }
                }
            }
            foreach (METS_Section_ReaderWriter_Config thisRWconfig in profile.Package_Level_DmdSec_Writer_Configs)
            {
                iPackage_dmdSec_ReaderWriter thisRw = (iPackage_dmdSec_ReaderWriter) thisRWconfig.ReaderWriterObject;
                if (thisRw.Schema_Reference_Required_Package(Item_To_Save))
                {
                    string[] rwXmlns = thisRw.Schema_Namespace(Item_To_Save);
                    string[] rwSchema = thisRw.Schema_Location(Item_To_Save);
                    foreach (string thisValue in rwXmlns)
                    {
                        if (!xmlnsList.Contains(thisValue))
                            xmlnsList.Add(thisValue);
                    }
                    foreach (string thisValue in rwSchema)
                    {
                        if (!schemaLocList.Contains(thisValue))
                            schemaLocList.Add(thisValue);
                    }
                }
            }
            foreach (METS_Section_ReaderWriter_Config thisRWconfig in profile.Division_Level_AmdSec_Writer_Configs)
            {
                iDivision_amdSec_ReaderWriter thisRw = (iDivision_amdSec_ReaderWriter)thisRWconfig.ReaderWriterObject;
                foreach (abstract_TreeNode thisNode in physicalDivisions)
                {
                    if (thisRw.Schema_Reference_Required_Division(thisNode))
                    {
                        string[] rwXmlns = thisRw.Schema_Namespace(Item_To_Save);
                        string[] rwSchema = thisRw.Schema_Location(Item_To_Save);
                        foreach (string thisValue in rwXmlns)
                        {
                            if (!xmlnsList.Contains(thisValue))
                                xmlnsList.Add(thisValue);
                        }
                        foreach (string thisValue in rwSchema)
                        {
                            if (!schemaLocList.Contains(thisValue))
                                schemaLocList.Add(thisValue);
                        }

                        break;
                    }
                }
            }
            foreach (METS_Section_ReaderWriter_Config thisRWconfig in profile.Division_Level_DmdSec_Writer_Configs)
            {
                iDivision_dmdSec_ReaderWriter thisRw = (iDivision_dmdSec_ReaderWriter)thisRWconfig.ReaderWriterObject;
                foreach (abstract_TreeNode thisNode in physicalDivisions)
                {
                    if (thisRw.Schema_Reference_Required_Division(thisNode))
                    {
                        string[] rwXmlns = thisRw.Schema_Namespace(Item_To_Save);
                        string[] rwSchema = thisRw.Schema_Location(Item_To_Save);
                        foreach (string thisValue in rwXmlns)
                        {
                            if (!xmlnsList.Contains(thisValue))
                                xmlnsList.Add(thisValue);
                        }
                        foreach (string thisValue in rwSchema)
                        {
                            if (!schemaLocList.Contains(thisValue))
                                schemaLocList.Add(thisValue);
                        }

                        break;
                    }
                }
            }

            // Add the namespaces
            foreach (string namespaces in xmlnsList)
            {
                Output_Stream.WriteLine("  xmlns:" + namespaces);
            }

            Output_Stream.WriteLine("  xsi:schemaLocation=\"http://www.loc.gov/METS/");
            Output_Stream.Write("    http://www.loc.gov/standards/mets/mets.xsd");

            // Add the schema locations
            foreach (string location in schemaLocList)
            {
                Output_Stream.WriteLine();
                Output_Stream.Write(location);
            }

            #endregion

            Output_Stream.WriteLine("\">");

            // Add the METS declaration information and METS header
            Item_To_Save.METS_Header.Add_METS(Item_To_Save, Output_Stream);

            // Create the options dictionary for these writers
            Dictionary<string, object> options = new Dictionary<string, object>();
            options["SobekCM_FileInfo_METS_amdSec_ReaderWriter:All_Files"] = allFiles;

            // Counters to keep track of the number of each section added
            int dmdSec_counter = 1;
            int digiProvMd = 1;
            int rightsMd = 1;
            int sourceMd = 1;
            int techMd = 1;

            #region Add all the package-level DMDSECs 

            // Prepare to add all the bibliographic section
            StringBuilder dmd_secid_builder = new StringBuilder();

            // Step through all the package level dmdSecs to be added
            foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Package_Level_DmdSec_Writer_Configs)
            {
                iPackage_dmdSec_ReaderWriter thisWriter = (iPackage_dmdSec_ReaderWriter) thisConfig.ReaderWriterObject;
                if (thisWriter.Include_dmdSec(Item_To_Save, options))
                {
                    // Save this DMD ID
                    dmd_secid_builder.Append("DMD" + dmdSec_counter + " ");

                    // Start this METS section
                    Output_Stream.WriteLine("<METS:dmdSec ID=\"DMD" + dmdSec_counter + "\">");
                    if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
                        Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
                    else
                        Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");

                    Output_Stream.WriteLine("<METS:xmlData>");

                    // Add the amd section to the stream
                    thisWriter.Write_dmdSec(Output_Stream, Item_To_Save, options);

                    // Close this METS section
                    Output_Stream.WriteLine("</METS:xmlData>");
                    Output_Stream.WriteLine("</METS:mdWrap>");
                    Output_Stream.WriteLine("</METS:dmdSec>");

                    dmdSec_counter++;
                }
            }

            // Now, add any unanalyzed DMD sections
            if (( Item_To_Save.Unanalyzed_DMDSECs != null ) && ( Item_To_Save.Unanalyzed_DMDSECs.Count > 0 ))
            {
                foreach (Unanalyzed_METS_Section thisSection in Item_To_Save.Unanalyzed_DMDSECs)
                {
                    // This wll be linked to the top-level
                    dmd_secid_builder.Append("DMD" + dmdSec_counter + " ");

                    // Add this to the output stream
                    Output_Stream.Write("<METS:dmdSec ID=\"DMD" + dmdSec_counter + "\"");

                    foreach (KeyValuePair<string, string> attribute in thisSection.Section_Attributes)
                    {
                        if (attribute.Key != "ID")
                            Output_Stream.Write(" " + attribute.Key + "=\"" + attribute.Value + "\"");
                    }
                    Output_Stream.WriteLine(">");
                    Output_Stream.WriteLine(thisSection.Inner_XML);
                    Output_Stream.WriteLine("</METS:dmdSec>");

                    dmdSec_counter++;
                }
            }

            #endregion

            #region Add all the division-level DMDSECs

            // Should we add DMDSEC metadata sections for the divisions?
            // Step through all the possible division level dmdSecs to be added
            foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Division_Level_DmdSec_Writer_Configs)
            {
                // Step through each division
                foreach (abstract_TreeNode thisDivision in allDivisions)
                {
                    iDivision_dmdSec_ReaderWriter thisWriter = (iDivision_dmdSec_ReaderWriter) thisConfig.ReaderWriterObject;

                    // Include the DMD Sec for this division?
                    if (thisWriter.Include_dmdSec(thisDivision, options))
                    {
                        // Save this DMD ID
                        thisDivision.DMDID = thisDivision.DMDID + "DMD" + dmdSec_counter + " ";

                        // Start this METS section
                        Output_Stream.WriteLine("<METS:dmdSec ID=\"DMD" + dmdSec_counter + "\">");
                        if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
                            Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
                        else
                            Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");

                        Output_Stream.WriteLine("<METS:xmlData>");

                        // Add the amd section to the stream
                        thisWriter.Write_dmdSec(Output_Stream, thisDivision, options);

                        // Close this METS section
                        Output_Stream.WriteLine("</METS:xmlData>");
                        Output_Stream.WriteLine("</METS:mdWrap>");
                        Output_Stream.WriteLine("</METS:dmdSec>");

                        dmdSec_counter++;
                    }
                }
            }


            #endregion

            #region Add all the file-level DMDSECs

            // Should we add DMDSEC metadata sections for the files?
            // Step through all the possible file level dmdSecs to be added
            foreach (METS_Section_ReaderWriter_Config thisConfig in profile.File_Level_DmdSec_Writer_Configs)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in allFiles)
                {
                    iFile_dmdSec_ReaderWriter thisWriter = (iFile_dmdSec_ReaderWriter)thisConfig.ReaderWriterObject;

                    // Include the DMD Sec for this file?
                    if (thisWriter.Include_dmdSec(thisFile, options))
                    {
                        // Save this DMD ID
                        thisFile.DMDID = thisFile.DMDID + "DMD" + dmdSec_counter + " ";

                        // Start this METS section
                        Output_Stream.WriteLine("<METS:dmdSec ID=\"DMD" + dmdSec_counter + "\">");
                        if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
                            Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
                        else
                            Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");

                        Output_Stream.WriteLine("<METS:xmlData>");

                        // Add the amd section to the stream
                        thisWriter.Write_dmdSec(Output_Stream, thisFile, options);

                        // Close this METS section
                        Output_Stream.WriteLine("</METS:xmlData>");
                        Output_Stream.WriteLine("</METS:mdWrap>");
                        Output_Stream.WriteLine("</METS:dmdSec>");

                        dmdSec_counter++;
                    }
                }
            }


            #endregion

            #region Add all the package-level AMDSECs

            // Prepare to add all the bibliographic section
            StringBuilder amd_secid_builder = new StringBuilder();

            // Step through all the package level amdSecs to be added
            foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Package_Level_AmdSec_Writer_Configs)
            {
                iPackage_amdSec_ReaderWriter thisWriter = (iPackage_amdSec_ReaderWriter)thisConfig.ReaderWriterObject;
                if (thisWriter.Include_amdSec(Item_To_Save, options))
                {
                    // Start this METS section
                    Output_Stream.WriteLine("<METS:amdSec>");
                    switch (thisConfig.AmdSecType)
                    {
                        case METS_amdSec_Type_Enum.DigiProvMD:
                            Output_Stream.WriteLine("<METS:digiprovMD ID=\"DIGIPROV" + digiProvMd + "\">");
                            amd_secid_builder.Append("DIGIPROV" + digiProvMd + " ");
                            digiProvMd++;
                            break;

                        case METS_amdSec_Type_Enum.RightsMD:
                            Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
                            amd_secid_builder.Append("RIGHTS" + rightsMd + " ");
                            rightsMd++;
                            break;

                        case METS_amdSec_Type_Enum.SourceMD:
                            Output_Stream.WriteLine("<METS:sourceMD ID=\"SOURCE" + sourceMd + "\">");
                            amd_secid_builder.Append("SOURCE" + sourceMd + " ");
                            sourceMd++;
                            break;

                        case METS_amdSec_Type_Enum.TechMD:
                            Output_Stream.WriteLine("<METS:techMD ID=\"TECH" + techMd + "\">");
                            amd_secid_builder.Append("TECH" + techMd + " ");
                            techMd++;
                            break;

                        default:
                            Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
                            amd_secid_builder.Append("RIGHTS" + rightsMd + " ");
                            rightsMd++;
                            break;
                    }


                    if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
                        Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
                    else
                        Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");

                    Output_Stream.WriteLine("<METS:xmlData>");

                    // Add the amd section to the stream
                    thisWriter.Write_amdSec(Output_Stream, Item_To_Save, options);

                    // Close this METS section
                    Output_Stream.WriteLine("</METS:xmlData>");
                    Output_Stream.WriteLine("</METS:mdWrap>");

                    switch (thisConfig.AmdSecType)
                    {
                        case METS_amdSec_Type_Enum.DigiProvMD:
                            Output_Stream.WriteLine("</METS:digiprovMD>");
                            break;

                        case METS_amdSec_Type_Enum.RightsMD:
                            Output_Stream.WriteLine("</METS:rightsMD>");
                            break;

                        case METS_amdSec_Type_Enum.SourceMD:
                            Output_Stream.WriteLine("</METS:sourceMD>");
                            break;

                        case METS_amdSec_Type_Enum.TechMD:
                            Output_Stream.WriteLine("</METS:techMD>");
                            break;

                        default:
                            Output_Stream.WriteLine("</METS:rightsMD>");
                            break;
                    }

                    Output_Stream.WriteLine("</METS:amdSec>");
                }
            }

            // Now, add any unanalyzed AMD sections
            if (Item_To_Save.Unanalyzed_AMDSECs != null)
            {
                foreach (Unanalyzed_METS_Section thisSection in Item_To_Save.Unanalyzed_AMDSECs)
                {
                    // Add this to the output stream
                    Output_Stream.Write("<METS:amdSec");
                    foreach (KeyValuePair<string, string> attribute in thisSection.Section_Attributes)
                    {
                        if (attribute.Key != "ID")
                            Output_Stream.Write(" " + attribute.Key + "=\"" + attribute.Value + "\"");
                    }
                    Output_Stream.WriteLine(">");
                    Output_Stream.WriteLine(thisSection.Inner_XML);
                    Output_Stream.WriteLine("</METS:amdSec>");
                }
            }

            #endregion

            #region Add all the division-level AMDSECs

            // Should we add AMDSEC metadata sections for the divisions?
            // Step through all the possible division level amdSecs to be added
            foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Division_Level_AmdSec_Writer_Configs)
            {
                // Step through each division
                foreach (abstract_TreeNode thisDivision in allDivisions)
                {

                    iDivision_amdSec_ReaderWriter thisWriter = (iDivision_amdSec_ReaderWriter) thisConfig.ReaderWriterObject;

                    // Include the DMD Sec for this division?
                    if (thisWriter.Include_amdSec(thisDivision, options))
                    {
                        // Start this METS section
                        Output_Stream.WriteLine("<METS:amdSec>");
                        switch (thisConfig.AmdSecType)
                        {
                            case METS_amdSec_Type_Enum.DigiProvMD:
                                Output_Stream.WriteLine("<METS:digiProvMD ID=\"DIGIPROV" + digiProvMd + "\">");
                                thisDivision.ADMID = thisDivision.ADMID + "DIGIPROV" + digiProvMd + " ";
                                digiProvMd++;
                                break;

                            case METS_amdSec_Type_Enum.RightsMD:
                                Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
                                thisDivision.ADMID = thisDivision.ADMID + "RIGHTS" + rightsMd + " ";
                                rightsMd++;
                                break;

                            case METS_amdSec_Type_Enum.SourceMD:
                                Output_Stream.WriteLine("<METS:sourceMD ID=\"SOURCE" + sourceMd + "\">");
                                thisDivision.ADMID = thisDivision.ADMID + "SOURCE" + sourceMd + " ";
                                sourceMd++;
                                break;

                            case METS_amdSec_Type_Enum.TechMD:
                                Output_Stream.WriteLine("<METS:techMD ID=\"TECH" + techMd + "\">");
                                thisDivision.ADMID = thisDivision.ADMID + "TECH" + techMd + " ";
                                techMd++;
                                break;

                            default:
                                Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
                                thisDivision.ADMID = thisDivision.ADMID + "RIGHTS" + rightsMd + " ";
                                rightsMd++;
                                break;
                        }


                        if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
                            Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
                        else
                            Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");

                        Output_Stream.WriteLine("<METS:xmlData>");
                        Output_Stream.WriteLine("<METS:xmlData>");

                        // Add the amd section to the stream
                        thisWriter.Write_amdSec(Output_Stream, thisDivision, options);

                        // Close this METS section
                        Output_Stream.WriteLine("</METS:xmlData>");
                        Output_Stream.WriteLine("</METS:mdWrap>");

                        switch (thisConfig.AmdSecType)
                        {
                            case METS_amdSec_Type_Enum.DigiProvMD:
                                Output_Stream.WriteLine("</METS:digiProvMD>");
                                break;

                            case METS_amdSec_Type_Enum.RightsMD:
                                Output_Stream.WriteLine("</METS:rightsMD>");
                                break;

                            case METS_amdSec_Type_Enum.SourceMD:
                                Output_Stream.WriteLine("</METS:sourceMD>");
                                break;

                            case METS_amdSec_Type_Enum.TechMD:
                                Output_Stream.WriteLine("</METS:techMD>");
                                break;

                            default:
                                Output_Stream.WriteLine("</METS:rightsMD>");
                                break;
                        }

                        Output_Stream.WriteLine("</METS:amdSec>");

                        dmdSec_counter++;
                    }
                }
            }



            #endregion

            #region Add all the file-level AMDSECs

            // Should we add AMDSEC metadata sections for the files?
            // Step through all the possible file level amdSecs to be added
            foreach (METS_Section_ReaderWriter_Config thisConfig in profile.File_Level_AmdSec_Writer_Configs)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in allFiles)
                {

                    iFile_amdSec_ReaderWriter thisWriter = (iFile_amdSec_ReaderWriter)thisConfig.ReaderWriterObject;

                    // Include the DMD Sec for this file?
                    if (thisWriter.Include_amdSec(thisFile, options))
                    {
                        // Start this METS section
                        Output_Stream.WriteLine("<METS:amdSec>");
                        switch (thisConfig.AmdSecType)
                        {
                            case METS_amdSec_Type_Enum.DigiProvMD:
                                Output_Stream.WriteLine("<METS:digiProvMD ID=\"DIGIPROV" + digiProvMd + "\">");
                                thisFile.ADMID = thisFile.ADMID + "DIGIPROV" + digiProvMd + " ";
                                digiProvMd++;
                                break;

                            case METS_amdSec_Type_Enum.RightsMD:
                                Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
                                thisFile.ADMID = thisFile.ADMID + "RIGHTS" + rightsMd + " ";
                                rightsMd++;
                                break;

                            case METS_amdSec_Type_Enum.SourceMD:
                                Output_Stream.WriteLine("<METS:sourceMD ID=\"SOURCE" + sourceMd + "\">");
                                thisFile.ADMID = thisFile.ADMID + "SOURCE" + sourceMd + " ";
                                sourceMd++;
                                break;

                            case METS_amdSec_Type_Enum.TechMD:
                                Output_Stream.WriteLine("<METS:techMD ID=\"TECH" + techMd + "\">");
                                thisFile.ADMID = thisFile.ADMID + "TECH" + techMd + " ";
                                techMd++;
                                break;

                            default:
                                Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
                                thisFile.ADMID = thisFile.ADMID + "RIGHTS" + rightsMd + " ";
                                rightsMd++;
                                break;
                        }


                        if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
                            Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
                        else
                            Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\"  MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");

                        Output_Stream.WriteLine("<METS:xmlData>");
                        Output_Stream.WriteLine("<METS:xmlData>");

                        // Add the amd section to the stream
                        thisWriter.Write_amdSec(Output_Stream, thisFile, options);

                        // Close this METS section
                        Output_Stream.WriteLine("</METS:xmlData>");
                        Output_Stream.WriteLine("</METS:mdWrap>");

                        switch (thisConfig.AmdSecType)
                        {
                            case METS_amdSec_Type_Enum.DigiProvMD:
                                Output_Stream.WriteLine("</METS:digiProvMD>");
                                break;

                            case METS_amdSec_Type_Enum.RightsMD:
                                Output_Stream.WriteLine("</METS:rightsMD>");
                                break;

                            case METS_amdSec_Type_Enum.SourceMD:
                                Output_Stream.WriteLine("</METS:sourceMD>");
                                break;

                            case METS_amdSec_Type_Enum.TechMD:
                                Output_Stream.WriteLine("</METS:techMD>");
                                break;

                            default:
                                Output_Stream.WriteLine("</METS:rightsMD>");
                                break;
                        }

                        Output_Stream.WriteLine("</METS:amdSec>");

                        dmdSec_counter++;
                    }
                }
            }



            #endregion

            // Add file and structure map sections
            if ( allFiles.Count > 0 )
            {
                #region Add the files section

                // Start the files section
                Output_Stream.WriteLine("<METS:fileSec>");

                // Step through each mime type
                foreach (string thisMimeType in mimeHash.Keys)
                {
                    List<SobekCM_File_Info> mimeCollection = mimeHash[thisMimeType];

                    // Start this file group section
                    Output_Stream.WriteLine(thisMimeType == "image/tiff" ? "<METS:fileGrp USE=\"archive\" >" : "<METS:fileGrp USE=\"reference\" >");

                    // Step through each file of this mime type and append the METS
                    foreach (SobekCM_File_Info thisFile in mimeCollection)
                    {
                        // Is the size and checksum information here?
                        if ((!MINIMIZE_FILE_SIZE) && ( !Item_To_Save.Divisions.Suppress_Checksum )) 
                        {
                            if ((((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0))) && (thisFile.METS_LocType == SobekCM_File_Info_Type_Enum.SYSTEM))
                            {
                                // Perform in a try catch
                                try
                                {
                                    if (File.Exists(Item_To_Save.Source_Directory + "/" + thisFile.System_Name))
                                    {
                                        // Get the size first
                                        if ( thisFile.Size < 0 )
                                        {
                                            FileInfo thisFileInfo = new FileInfo(Item_To_Save.Source_Directory + "/" + thisFile.System_Name);
                                            thisFile.Size = thisFileInfo.Length;
                                        }

                                        // Get the checksum, if it doesn't exist
                                        if (String.IsNullOrEmpty(thisFile.Checksum))
                                        {
                                            FileMD5 checksummer = new FileMD5(Item_To_Save.Source_Directory + "/" + thisFile.System_Name);
                                            thisFile.Checksum = checksummer.Checksum;

                                            // Set the checksum type
                                            thisFile.Checksum_Type = "MD5";
                                        }
                                    }
                                }
                                catch
                                {
                                }
                            }
                        }

                        // Start out this file 
                        Output_Stream.Write("<METS:file GROUPID=\"" + thisFile.Group_Number + "\" ID=\"" + thisFile.ID + "\" MIMETYPE=\"" + thisMimeType + "\"");

                        // Add links to dmd secs and amd secs
                        if (!String.IsNullOrEmpty(thisFile.DMDID))
                        {
                            Output_Stream.Write(" DMDID=\"" + thisFile.DMDID + "\"");
                        }
                        if (!String.IsNullOrEmpty(thisFile.ADMID))
                        {
                            Output_Stream.Write(" ADMID=\"" + thisFile.ADMID + "\"");
                        }

                        // Add the checksum it it exists
                        if ((!MINIMIZE_FILE_SIZE) && (!String.IsNullOrEmpty(thisFile.Checksum)))
                        {
                            Output_Stream.Write(" CHECKSUM=\"" + thisFile.Checksum + "\"");

                            // Add the checksum type if there was one (SHOULD be one here)
                            if (!String.IsNullOrEmpty(thisFile.Checksum_Type))
                            {
                                Output_Stream.Write(" CHECKSUMTYPE=\"" + thisFile.Checksum_Type + "\"");
                            }
                        }

                        // Add the size of the file, if it exists
                        if ((!MINIMIZE_FILE_SIZE) && (thisFile.Size > 0))
                        {
                            Output_Stream.Write(" SIZE=\"" + thisFile.Size + "\"");
                        }

                        // Close out this beginning file tag
                        Output_Stream.WriteLine(">");

                        // Include the system name or URL
                        if (thisFile.METS_LocType == SobekCM_File_Info_Type_Enum.URL)
                        {
                            Output_Stream.WriteLine("<METS:FLocat LOCTYPE=\"URL\" xlink:href=\"" + thisFile.System_Name.Replace(" ", "%20").Replace("&", "&amp;") + "\" />");
                        }
                        else
                        {
                            Output_Stream.WriteLine("<METS:FLocat LOCTYPE=\"OTHER\" OTHERLOCTYPE=\"SYSTEM\" xlink:href=\"" + thisFile.System_Name.Replace(" ", "%20").Replace("&", "&amp;").Replace("\\", "/") + "\" />");
                        }

                        // Add the closing file tag
                        Output_Stream.WriteLine("</METS:file>");
                    }

                    // Close out this file group section
                    Output_Stream.WriteLine("</METS:fileGrp>");
                }

                // Finish out the file section
                Output_Stream.WriteLine("</METS:fileSec>");

                #endregion

                #region Add the structure map section

                Dictionary<abstract_TreeNode, int> pages_to_appearances = new Dictionary<abstract_TreeNode, int>();

                // May or may not be AMDSecs and DMDsec
                string dmdSecIdString = dmd_secid_builder.ToString().Trim();
                string amdSecIdString = amd_secid_builder.ToString().Trim();

                // Add the physical page structure map first
                if (hasPageFiles)
                {
                    Output_Stream.WriteLine("<METS:structMap ID=\"STRUCT1\" TYPE=\"physical\">");

                    // Add any outer divisions here
                    if ( Item_To_Save.Divisions.Outer_Division_Count > 0)
                    {
                        foreach (Outer_Division_Info outerDiv in Item_To_Save.Divisions.Outer_Divisions)
                        {
                            Output_Stream.Write("<METS:div");
                            if (dmdSecIdString.Length > 0)
                            {
                                Output_Stream.Write(" DMDID=\"" + dmdSecIdString + "\"");
                            }
                            if (outerDiv.Label.Length > 0)
                            {
                                Output_Stream.Write(" LABEL=\"" + Convert_String_To_XML_Safe_Static(outerDiv.Label) + "\"");
                            }
                            if (outerDiv.OrderLabel > 0)
                            {
                                Output_Stream.Write(" ORDERLABEL=\"" + outerDiv.OrderLabel + "\"");
                            }
                            if (outerDiv.Type.Length > 0)
                            {
                                Output_Stream.Write(" TYPE=\"" + Convert_String_To_XML_Safe_Static(outerDiv.Type) + "\"");
                            }
                            Output_Stream.WriteLine(">");
                        }
                    }
                    else
                    {
                        // Start the main division information
                        Output_Stream.Write("<METS:div");
                        if (dmdSecIdString.Length > 0)
                        {
                            Output_Stream.Write(" DMDID=\"" + dmdSecIdString + "\"");
                        }
                        if (amdSecIdString.Length > 0)
                        {
                            Output_Stream.Write(" ADMID=\"" + amdSecIdString + "\"");
                        }


                        // Add the title, if one was provided
                        string title = Item_To_Save.Bib_Info.Main_Title.ToString();
                        if (title.Length > 0)
                        {
                            Output_Stream.Write(" LABEL=\"" + title + "\"");
                        }

                        // Finish out this first, main division tag
                        Output_Stream.WriteLine(" ORDER=\"0\" TYPE=\"main\">");
                    }

                    // Add all the divisions recursively
                    int order = 1;
                    foreach (abstract_TreeNode thisRoot in Item_To_Save.Divisions.Physical_Tree.Roots)
                    {
                        recursively_add_div_info(thisRoot, Output_Stream, pages_to_appearances, order++);
                    }

                    // Close any outer divisions here
                    if (Item_To_Save.Divisions.Outer_Division_Count > 0)
                    {
                        for (int index = 0; index < Item_To_Save.Divisions.Outer_Division_Count; index++)
                        {
                            Output_Stream.WriteLine("</METS:div>");
                        }
                    }
                    else
                    {
                        // Close out the main division tag
                        Output_Stream.WriteLine("</METS:div>");
                    }

                    // Close out this structure map portion
                    Output_Stream.WriteLine("</METS:structMap>");
                }

                if (hasDownloadFiles)
                {
                    Output_Stream.WriteLine("<METS:structMap ID=\"STRUCT2\" TYPE=\"other\">");

                   // Add any outer divisions here
                    if ( Item_To_Save.Divisions.Outer_Division_Count > 0)
                    {
                        foreach (Outer_Division_Info outerDiv in Item_To_Save.Divisions.Outer_Divisions)
                        {
                            Output_Stream.Write("<METS:div");
                            if (dmdSecIdString.Length > 0)
                            {
                                Output_Stream.Write(" DMDID=\"" + dmdSecIdString + "\"");
                            }
                            if (outerDiv.Label.Length > 0)
                            {
                                Output_Stream.Write(" LABEL=\"" + Convert_String_To_XML_Safe_Static(outerDiv.Label) + "\"");
                            }
                            if (outerDiv.OrderLabel > 0)
                            {
                                Output_Stream.Write(" ORDERLABEL=\"" + outerDiv.OrderLabel + "\"");
                            }
                            if (outerDiv.Type.Length > 0)
                            {
                                Output_Stream.Write(" TYPE=\"" + Convert_String_To_XML_Safe_Static(outerDiv.Type) + "\"");
                            }
                            Output_Stream.WriteLine(">");
                        }
                    }
                    else
                    {
                        // Start the main division information
                        Output_Stream.Write("<METS:div");
                        if (dmdSecIdString.Length > 0)
                        {
                            Output_Stream.Write(" DMDID=\"" + dmdSecIdString + "\"");
                        }
                        if (amdSecIdString.Length > 0)
                        {
                            Output_Stream.Write(" ADMID=\"" + amdSecIdString + "\"");
                        }

                        // Add the title, if one was provided
                        string title = Item_To_Save.Bib_Info.Main_Title.ToString();
                        if (title.Length > 0)
                        {
                            Output_Stream.Write(" LABEL=\"" + title + "\"");
                        }

                        // Finish out this first, main division tag
                        Output_Stream.WriteLine(" ORDER=\"0\" TYPE=\"main\">");
                    }

                    // Add all the divisions recursively
                    int order = 1;
                    foreach (abstract_TreeNode thisRoot in Item_To_Save.Divisions.Download_Tree.Roots)
                    {
                        recursively_add_div_info(thisRoot, Output_Stream, pages_to_appearances, order++);
                    }

                    // Close any outer divisions here
                    if (Item_To_Save.Divisions.Outer_Division_Count > 0)
                    {
                        for (int index = 0; index < Item_To_Save.Divisions.Outer_Division_Count; index++)
                        {
                            Output_Stream.WriteLine("</METS:div>");
                        }
                    }
                    else
                    {
                        // Close out the main division tag
                        Output_Stream.WriteLine("</METS:div>");
                    }

                    // Close out this structure map portion
                    Output_Stream.WriteLine("</METS:structMap>");
                }

                #endregion
            }
            else
            {
                // Structure map is a required element for METS
                Output_Stream.Write("<METS:structMap ID=\"STRUCT1\" > <METS:div /> </METS:structMap>\r\n");
            }

            // Add the behavior section for SobekCM views and interfaces
            //if (embedded_metadata_types.Contains(Metadata_Type_Enum.SobekCM_Behaviors))
            //{
            //    Item_To_Save.Behaviors.Add_BehaviorSec_METS(Output, Item_To_Save.Divisions.Physical_Tree.Has_Files);
            //}

            // Close out the METS file
            Output_Stream.Write("</METS:mets>\r\n");

            return true;
        }
        /// <summary> Recalculates all the checksums for associated files </summary>
        /// <param name="Recalculate_All"> Flag indicates whether to recalculate all files, regardless of need </param>
        /// <param name="Progress_Delegate"> Progress delegate is called to update any progress displays </param>
        public void Calculate_Checksum(bool Recalculate_All, New_SobekCM_Bib_Package_Progress_Task_Group Progress_Delegate)
        {
            // Get the list of all pages from this division trees
            List <abstract_TreeNode> pageNodes = physicalDivisionTree.Pages_PreOrder;
            List <abstract_TreeNode> fileNodes = downloadDivisionTree.Pages_PreOrder;

            // Update progress
            if (Progress_Delegate != null)
            {
                Progress_Delegate("Calculating checksums", "Checking all files", 0, 2);
            }

            // Count how many pages to calculate for
            int total_files = 0;

            foreach (Page_TreeNode pageNode in pageNodes)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in pageNode.Files)
                {
                    // Recalculate this file info?
                    if ((Recalculate_All) || ((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0)))
                    {
                        total_files++;
                    }
                }
            }
            foreach (Page_TreeNode pageNode in fileNodes)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in pageNode.Files)
                {
                    // Recalculate this file info?
                    if ((Recalculate_All) || ((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0)))
                    {
                        total_files++;
                    }
                }
            }

            if (total_files == 0)
            {
                return;
            }

            // Now caulcate
            int     file_count  = 0;
            FileMD5 checksummer = new FileMD5();

            foreach (Page_TreeNode pageNode in pageNodes)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in pageNode.Files)
                {
                    // Recalculate this file info?
                    if ((thisFile.METS_LocType == SobekCM_File_Info_Type_Enum.SYSTEM) && ((Recalculate_All) || ((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0))))
                    {
                        // Perform in a try catch
                        try
                        {
                            // Get the size first
                            FileInfo thisFileInfo = new FileInfo(source_directory + "/" + thisFile.System_Name);
                            thisFile.Size = thisFileInfo.Length;

                            // Get the checksum, if it doesn't exist
                            if ((String.IsNullOrEmpty(thisFile.Checksum)) || (Recalculate_All))
                            {
                                thisFile.Checksum      = checksummer.Calculate_Checksum(source_directory + "/" + thisFile.System_Name);
                                thisFile.Checksum_Type = "MD5";
                            }
                        }
                        catch {}

                        file_count++;

                        // Update progress
                        if (Progress_Delegate != null)
                        {
                            Progress_Delegate("Calculating checksums", "Calculating '" + thisFile.System_Name + "'", file_count, total_files);
                        }
                    }
                }
            }
            foreach (Page_TreeNode pageNode in fileNodes)
            {
                // Step through each file
                foreach (SobekCM_File_Info thisFile in pageNode.Files)
                {
                    // Recalculate this file info?
                    if ((thisFile.METS_LocType == SobekCM_File_Info_Type_Enum.SYSTEM) && ((Recalculate_All) || ((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0))))
                    {
                        // Perform in a try catch
                        try
                        {
                            // Get the size first
                            FileInfo thisFileInfo = new FileInfo(source_directory + "/" + thisFile.System_Name);
                            thisFile.Size = thisFileInfo.Length;

                            // Get the checksum, if it doesn't exist
                            if ((String.IsNullOrEmpty(thisFile.Checksum)) || (Recalculate_All))
                            {
                                thisFile.Checksum      = checksummer.Calculate_Checksum(source_directory + "/" + thisFile.System_Name);
                                thisFile.Checksum_Type = "MD5";
                            }
                        }
                        catch {}

                        file_count++;

                        // Update progress
                        if (Progress_Delegate != null)
                        {
                            Progress_Delegate("Calculating checksums", "Calculating '" + thisFile.System_Name + "'", file_count, total_files);
                        }
                    }
                }
            }

            // Update progress
            if (Progress_Delegate != null)
            {
                Progress_Delegate("Checksum Complete", "Checksum Complete", file_count, total_files);
            }
        }