using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.IO;
using System.Security.AccessControl;
using System.Management;
using System.Security.Principal;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Drawing;
using System.Xml;
using System.Xml.XPath;
using System.Net;

using CommonLib.Windows.Forms;
using CommonLib.IO;
using CommonLib.Misc;

namespace All_Purpose_Auto_Setup
{
   class FilePackageTransferManager
   {
      public enum eFileAction
      {
         None
      };
      public enum eFileCopyStatus { FAIL, SUCCESS };

      public enum eCopyType { FOLDERS, FILES };

      // An event that can be raised, allowing other classes to
      // subscribe to it and do what they like with the message.
      public event Action<string, string, List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>> UpdateStatusDisplayAndLogEvent;

      public event Action<string, int, List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>> UpdateStatusDisplayAtLine;

      // event for thread to notify other threads that it has exited
      private AutoResetEvent _resetEvent = new AutoResetEvent(false);

      public List<ConfigFileManager.Ini_KeyValue<string>> fileTransferPackages = new List<ConfigFileManager.Ini_KeyValue<string>>();

      private List<SourceFile> sourceFiles = new List<SourceFile>();

      FileManip.FileDownloadProgressTracker downloadProgressTracker = new FileManip.FileDownloadProgressTracker();

      int m_copyProgressLineIndex = 0;

      Form m_parentForm;

      bool m_filePackageProcessedSuccessful = false;

      int m_fileCreatedCount = 0;

      int m_fileCopyCount = 0;

      public FilePackageTransferManager(Form parentForm)
      {
         m_parentForm = parentForm;
      }

      public bool manageFilePackageTransfer(string sectionName)
      {
         bool setupSuccessful = true;
         string msg, indentation = String.Empty;

         CommonLib.Windows.Forms.TextFormat.TextFontAndColor defaultTextProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         CommonLib.Windows.Forms.TextFormat.TextFontAndColor textProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> textPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();

         // set font to default text and color
         textProp.textFont = defaultTextProp.textFont;
         textProp.textColor = Common.getSetupStepTitleColor();
         textProp.wordIndex = 0;
         textProp.wordCount = 100;
         textPropList.Add(textProp);

         DateTime dat1 = DateTime.Now;
         msg = "\n\n<-------------------Date and Time: " + dat1.ToString(@"MM/dd/yyyy     hh:mm:ss tt", System.Globalization.CultureInfo.InvariantCulture) + "------------------->";
         UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

         // set font to default text and color
         textProp.textFont = defaultTextProp.textFont;
         textProp.textColor = Common.getSetupStepTitleColor();
         textProp.wordIndex = 0;
         textProp.wordCount = 10;
         // set font size
         textProp.textFont = new Font(textProp.textFont.Name, 12);
         // set font style
         textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
         textPropList.Add(textProp);

         msg = "\n-=Copying File Packages=-";
         UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

         XPathDocument doc = null;

         fileTransferPackages.Clear();

         ConfigFileManager.parseConfigInfo(sectionName, fileTransferPackages);

         setupSuccessful = verifyFilePackagesInfo();

         if (setupSuccessful)
         {
            foreach (ConfigFileManager.Ini_KeyValue<string> filePackage in fileTransferPackages)
            {
               try
               {
                  XmlReaderSettings readerSettings = new XmlReaderSettings();
                  // tells the xmlreader to ignore comment in XML file
                  readerSettings.IgnoreComments = true;
                  using (XmlReader reader = XmlReader.Create(filePackage.iniValue, readerSettings))
                  {
                     // load the XML file
                     doc = new XPathDocument(reader);
                  }
               }
               catch (Exception e)
               {
                  // set font to default text and color
                  textProp.textFont = defaultTextProp.textFont;
                  textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                  textProp.wordIndex = 0;
                  textProp.wordCount = 2;
                  // set font style
                  textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                  textPropList.Add(textProp);

                  indentation = Common.getIndentation(1);
                  msg = "\n" + indentation + "- FAILED. Unable to parse file " + filePackage.iniValue + ". " + e.Message;
                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                  setupSuccessful = false;
               }

               if (setupSuccessful)
               {
                  XPathNavigator nav = doc.CreateNavigator();

                  // move to root node
                  nav.MoveToRoot();

                  // move to 1st level node
                  if (nav.MoveToFirstChild())
                  {
                     // move to 2nd level node
                     if (nav.MoveToFirstChild())
                     {
                        if (!ProcessXML2ndLevelNodes(nav, Path.GetDirectoryName(filePackage.iniValue)))
                        {
                           setupSuccessful = false;
                        }
                     }
                  }
               }

               if (!setupSuccessful)
                  break;
            }

            if (setupSuccessful)
            {
               if (!m_filePackageProcessedSuccessful)
               {
                  // set font to default text and color
                  textProp.textFont = defaultTextProp.textFont;
                  textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.SYSTEM_SKIP);
                  textProp.wordIndex = 0;
                  textProp.wordCount = 2;
                  // set font style
                  textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                  textPropList.Add(textProp);

                  indentation = Common.getIndentation(1);
                  msg = "\n" + indentation + "- SKIPPED. Nothing to be copied from the provided information.";
                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);
               }
            }
         }
         return setupSuccessful;
      }

      private void DownloadProgressHandler(UInt64 totalBytesCopied, UInt64 totalBytesFile)
      {
         string totalSizeDescription = FileManip.DescribeFileSize(totalBytesFile, FileManip.eFileSizeResolution.AUTO);
         double totalPercentageReceived = ((double)totalBytesCopied / (double)totalBytesFile) * 100;

         CommonLib.Windows.Forms.TextFormat.TextFontAndColor defaultTextProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         CommonLib.Windows.Forms.TextFormat.TextFontAndColor textProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> textPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();

         if (!downloadProgressTracker.stopwatchForFileDownload.IsRunning)
         {
            downloadProgressTracker.stopwatchForFileDownload.Start();
         }

         if (!downloadProgressTracker.stopwatchForUpdateInterval.IsRunning)
         {
            downloadProgressTracker.stopwatchForUpdateInterval.Start();
         }

         // when an interval is reached, update download progress
         if (downloadProgressTracker.stopwatchForUpdateInterval.ElapsedMilliseconds >= downloadProgressTracker.updateIntervalInMs)
         {
            downloadProgressTracker.stopwatchForUpdateInterval.Reset();

            if (downloadProgressTracker.averageBytesReceivedPerUpdateInterval == 0)
               downloadProgressTracker.averageBytesReceivedPerUpdateInterval = totalBytesCopied - downloadProgressTracker.totalBytesReceived;
            else
            {
               downloadProgressTracker.averageBytesReceivedPerUpdateInterval += totalBytesCopied - downloadProgressTracker.totalBytesReceived;
               downloadProgressTracker.averageBytesReceivedPerUpdateInterval /= 2;
            }
            downloadProgressTracker.totalBytesReceived = totalBytesCopied;

            downloadProgressTracker.millisecondsCount += downloadProgressTracker.updateIntervalInMs;

            string receivedSizeDescription = FileManip.DescribeFileSize(totalBytesCopied, downloadProgressTracker.fileSizeResolution);

            if (downloadProgressTracker.strTotalBytesReceived.Length > 0)
            {
               // determine what the highest resolution transfer rate change
               FileManip.eFileSizeResolution res = FileManip.GetFileResolutionOfTransferRate(receivedSizeDescription, downloadProgressTracker.strTotalBytesReceived);
               // increate the count for this resolution
               downloadProgressTracker.unitCount[res]++;
            }

            // every 2 seconds, we determine which resolution change the most, and use the resolution
            // that's changed the most as the update progress
            if (downloadProgressTracker.millisecondsCount >= 2000)
            {
               FileManip.eFileSizeResolution res = downloadProgressTracker.getResolutionWithHighestCount();

               if (res != FileManip.eFileSizeResolution.AUTO)
                  downloadProgressTracker.fileSizeResolution = res;

               downloadProgressTracker.millisecondsCount = 0;
               downloadProgressTracker.resetUnitCount();
            }

            receivedSizeDescription = FileManip.DescribeFileSize(totalBytesCopied, downloadProgressTracker.fileSizeResolution);

            downloadProgressTracker.strTotalBytesReceived = receivedSizeDescription;

            // shorten the size description
            receivedSizeDescription = FileManip.ShortenFileSizeDescription(receivedSizeDescription);

            long downloadSpeed = (long)Math.Ceiling((double)downloadProgressTracker.averageBytesReceivedPerUpdateInterval / ((double)downloadProgressTracker.updateIntervalInMs / 1000.0));
            string downloadSpeedDesc = FileManip.DescribeFileSize((UInt64)downloadSpeed, FileManip.eFileSizeResolution.AUTO);
            downloadSpeedDesc = Regex.Replace(downloadSpeedDesc, @"(\d+)\.\d+(.+)", "$1$2", RegexOptions.IgnoreCase);

            string msg = Common.getIndentation(1) + "Progress: " + string.Format("{0:N2}%", totalPercentageReceived) +
               "      Speed: " + downloadSpeedDesc + "/s      Copied: " + receivedSizeDescription + "      Total Size: " + totalSizeDescription +
                 "      Elapsed time: " + DateTimeManip.DescribeTimeElapsed(downloadProgressTracker.stopwatchForFileDownload.Elapsed);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(0, 82, 204);
            textProp.wordIndex = 1;
            textProp.wordCount = 1;
            // set font style
            textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
            textPropList.Add(textProp);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(0, 82, 204);
            textProp.wordIndex = 3;
            textProp.wordCount = 2;
            // set font style
            textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
            textPropList.Add(textProp);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(0, 82, 204);
            textProp.wordIndex = StringManip.GetPhraseWordIndexInText(msg, "copied:") + 1;
            textProp.wordCount = StringManip.GetPhraseWordIndexInText(msg, "total size:") - textProp.wordIndex;
            // set font style
            textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
            textPropList.Add(textProp);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(0, 82, 204);
            textProp.wordIndex = StringManip.GetPhraseWordIndexInText(msg, "total size:") + 2;
            textProp.wordCount = 2;
            // set font style
            textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
            textPropList.Add(textProp);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(0, 82, 204);
            textProp.wordIndex = StringManip.GetPhraseWordIndexInText(msg, "elapsed time:") + 2;
            textProp.wordCount = 100;
            // set font style
            textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
            textPropList.Add(textProp);

            UpdateStatusDisplayAtLine(msg, m_copyProgressLineIndex, textPropList);
         }
      }

      bool processFilePackage()
      {
         bool setupSuccessful = true;
         string msg = "", msg2 = "", displayMsg = "", logMsg = "", indentation = String.Empty, errMsg = "", sourcePath = "", destPath = "", tempMsg = "";

         CommonLib.Windows.Forms.TextFormat.TextFontAndColor defaultTextProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         CommonLib.Windows.Forms.TextFormat.TextFontAndColor textProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> textPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();
         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> tempTextPropList;

         bool overwriteDestinationFile = false;
         bool overwriteDestinationFileAlways = false;
         bool promptForOverwriteConfirmation = true;

         frmSetupStatusDisplay displayFrm = (frmSetupStatusDisplay)m_parentForm;

         int fileCountLineIndex = displayFrm.updateStatusDisplayAndLog("\n  ", "", textPropList);
         int sourceFileLineIndex = displayFrm.updateStatusDisplayAndLog("\n  ", "", textPropList);
         int destFileLineIndex = displayFrm.updateStatusDisplayAndLog("\n  ", "", textPropList);
         m_copyProgressLineIndex = displayFrm.updateStatusDisplayAndLog("\n  ", "", textPropList);

         foreach (SourceFile file in sourceFiles)
         {
            indentation = Common.getIndentation(1);
            msg = indentation + "Files Copied: " + m_fileCopyCount.ToString() + "     Files Created: " + m_fileCreatedCount.ToString();

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(5, 112, 1);
            textProp.wordIndex = 0;
            textProp.wordCount = 2;
            // set font style
            textPropList.Add(textProp);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(5, 112, 1);
            textProp.wordIndex = 2;
            textProp.wordCount = 1;
            // set font style
            textProp.textFont = new Font(textProp.textFont.FontFamily, 10, FontStyle.Bold);
            textPropList.Add(textProp);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(5, 112, 1);
            textProp.wordIndex = StringManip.GetPhraseWordIndexInText(msg, "files created:");
            textProp.wordCount = 2;
            textPropList.Add(textProp);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(5, 112, 1);
            textProp.wordIndex = StringManip.GetPhraseWordIndexInText(msg, "files created:") + 2;
            textProp.wordCount = 1;
            // set font style
            textProp.textFont = new Font(textProp.textFont.FontFamily, 10, FontStyle.Bold);
            textPropList.Add(textProp);

            UpdateStatusDisplayAtLine(msg, fileCountLineIndex, textPropList);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            //textProp.textColor = Color.FromArgb(157, 150, 19);
            textProp.wordIndex = 0;
            textProp.wordCount = 1;
            textPropList.Add(textProp);

            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(255, 60, 0);
            textProp.wordIndex = 1;
            textProp.wordCount = 100;
            // set font style
            textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
            textPropList.Add(textProp);
            tempTextPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>(textPropList);

            sourcePath = file.SourcePath;
            destPath = file.destPath;

            if (sourcePath.Length > 0 && !PathManip.PathContainsAtLeastOneFolder(sourcePath))
               sourcePath = PathManip.AddTrailingSlashToPath(sourcePath);

            if (!PathManip.PathContainsAtLeastOneFolder(destPath))
               destPath = PathManip.AddTrailingSlashToPath(destPath);

            string destFileName;
            // get the actual path and filename letter casing
            string sourceFilePathAndName = PathManip.GetProperFilePathCapitalization(Path.Combine(sourcePath, file.FileName));
            string sourceFileName = Path.GetFileName(sourceFilePathAndName);
            string sourceFilePath = Path.GetDirectoryName(sourceFilePathAndName);

            // if the source file is to be save as a different at the destination
            if (file.FileNameSaveAs.Length > 0)
               destFileName = file.FileNameSaveAs;
            else
               destFileName = sourceFileName;

            indentation = Common.getIndentation(1);
            if (file.SourcePath.Length > 0)
            {
               msg = indentation + "Copying " + StringManip.ShortenStringToSize(sourceFilePathAndName, 65);
               msg2 = indentation + "To " + StringManip.ShortenStringToSize(Path.Combine(destPath, destFileName), 65);
            }
            else
               msg = indentation + "Creating " + StringManip.ShortenStringToSize(Path.Combine(destPath, destFileName), 65);

            UpdateStatusDisplayAtLine(msg, sourceFileLineIndex, textPropList);

            if (msg2.Length > 0)
            {
               textPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>(tempTextPropList);
               UpdateStatusDisplayAtLine(msg2, destFileLineIndex, textPropList);
            }

            if (file.individualOverwrite.Length != 0)
            {
               promptForOverwriteConfirmation = false;
               if (String.Equals(file.individualOverwrite, "yes", StringComparison.OrdinalIgnoreCase) || String.Equals(file.individualOverwrite, "true", StringComparison.OrdinalIgnoreCase))
               {
                  overwriteDestinationFile = true;
               }
               else
                  overwriteDestinationFile = false;
            }

            if (promptForOverwriteConfirmation && File.Exists(Path.Combine(destPath, destFileName)))
            {
               MessageBoxCustom.clsCustomButton button1 = new MessageBoxCustom.clsCustomButton("YES - This File", "YesOneTime");
               MessageBoxCustom.clsCustomButton button2 = new MessageBoxCustom.clsCustomButton("NO - This File", "NoOneTime");
               MessageBoxCustom.clsCustomButton button3 = new MessageBoxCustom.clsCustomButton("YES - All Files", "YesAlways");
               MessageBoxCustom.clsCustomButton button4 = new MessageBoxCustom.clsCustomButton("No - All Files", "NoAlways");

               List<MessageBoxCustom.clsCustomButton> options = new List<MessageBoxCustom.clsCustomButton>() { button1, button2, button3, button4 };

               msg = ((frmSetupStatusDisplay)m_parentForm).displayMessageBox(MessageBoxCustom.PopUpMsgType.CUSTOMQUESTION, options,
                  "File " + Path.Combine(destPath, destFileName) + "\nalready exists. Would you like to overwrite?", "Warning!!!", -1);

               if (String.Equals(msg, "YesOneTime", StringComparison.OrdinalIgnoreCase))
               {
                  overwriteDestinationFile = true;
               }
               else if (String.Equals(msg, "YesAlways", StringComparison.OrdinalIgnoreCase))
               {
                  overwriteDestinationFileAlways = true;
                  overwriteDestinationFile = true;
                  promptForOverwriteConfirmation = false;
               }
               else if (String.Equals(msg, "NoAlways", StringComparison.OrdinalIgnoreCase))
               {
                  promptForOverwriteConfirmation = false;
               }
            }

            if (m_fileCopyCount == 0 && m_fileCreatedCount == 0)
               UpdateStatusDisplayAtLine(indentation + "Initializing. Please wait...", m_copyProgressLineIndex, textPropList);

            // if the file to be created already exists
            if (file.SourcePath.Length == 0 && File.Exists(Path.Combine(destPath, destFileName)))
            {
               logMsg = "\n" + Common.getIndentation(2) + "Create " + Path.Combine(destPath, destFileName) + " - SKIPPED.  File already exists at destination";
            }
            else if (file.SourcePath.Length > 0 && File.Exists(Path.Combine(destPath, destFileName)) && !overwriteDestinationFile)
            {
               logMsg = "\n" + Common.getIndentation(2) + "Copy " + sourceFilePathAndName + " --> " + Path.Combine(destPath, destFileName) + " - SKIPPED. File already exists at destination";
            }
            else
            {
               foreach (var drive in DriveInfo.GetDrives()
                                  .Where(d => d.DriveType == DriveType.CDRom))
               {
                  if (Regex.IsMatch(sourceFilePathAndName, @"^" + Regex.Escape(PathManip.RemoveTrailingSlashInPath(drive.Name)) + @".*", RegexOptions.IgnoreCase))
                  {
                     while (!drive.IsReady || !File.Exists(sourceFilePathAndName))
                     {
                        Hardware.OpenCdDrive(drive.Name);

                        tempMsg = "Please insert a disc";

                        if (file.cdLabel.Length > 0)
                           tempMsg += " labeled \"" + file.cdLabel + "\"";

                        tempMsg += " into drive " + drive.Name + " that has the following file:\n" + sourceFilePathAndName + "\n\n Click OK to proceed";

                        msg = ((frmSetupStatusDisplay)m_parentForm).displayMessageBox(MessageBoxCustom.PopUpMsgType.OKCANCEL, null,
                           tempMsg, "Info", -1);

                        if (String.Equals(msg, "cancel", StringComparison.OrdinalIgnoreCase))
                        {
                           // set font to default text and color
                           textProp.textFont = defaultTextProp.textFont;
                           textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                           textProp.wordIndex = 0;
                           textProp.wordCount = 2;
                           // set font style
                           textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                           textPropList.Add(textProp);

                           indentation = Common.getIndentation(1);

                           logMsg = "\n" + Common.getIndentation(2) + "Copy " + sourceFilePathAndName + " --> " + Path.Combine(destPath, destFileName) + " - FAILED";
                           displayMsg = "\n" + indentation + "- FAILED. Unable to locate file: " + sourceFilePathAndName;

                           setupSuccessful = false;
                           break;
                        }
                     }

                     break;
                  }
               }

               if (setupSuccessful)
               {
                  if (FileManip.CopyFileWithProgress(sourceFilePath, sourceFileName, file.destPath, destFileName,
                          true, FileAttributes.Normal,
                          new FileManip.FileCopyProgressChangeDelegate(DownloadProgressHandler), downloadProgressTracker))
                  {
                     indentation = Common.getIndentation(2);
                     if (file.SourcePath.Length > 0)
                     {
                        logMsg = "\n" + indentation + "Copy " + sourceFilePathAndName + " --> " + Path.Combine(destPath, destFileName) + " - SUCCESS";
                        m_fileCopyCount++;
                     }
                     else
                     {
                        logMsg = "\n" + indentation + "Create " + Path.Combine(destPath, destFileName) + " - SUCCESS";
                        m_fileCreatedCount++;
                     }
                  }
                  else
                  {
                     errMsg = downloadProgressTracker.errorMsg;

                     // set font to default text and color
                     textProp.textFont = defaultTextProp.textFont;
                     textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                     textProp.wordIndex = 0;
                     textProp.wordCount = 2;
                     // set font style
                     textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                     textPropList.Add(textProp);

                     indentation = Common.getIndentation(1);

                     if (file.SourcePath.Length > 0)
                     {
                        logMsg = "\n" + Common.getIndentation(2) + "Copy " + sourceFilePathAndName + " --> " + Path.Combine(destPath, destFileName) + " - FAILED";
                        displayMsg = "\n" + indentation + "- FAILED. Copying file failed. " + Regex.Replace(errMsg, @"\r\n", "", RegexOptions.IgnoreCase) + "\n" + indentation + "From: " + sourceFilePathAndName + "\n" + indentation + "To: " + Path.Combine(destPath, destFileName);
                     }
                     else
                     {
                        logMsg = "\n" + Common.getIndentation(2) + "Create " + Path.Combine(destPath, destFileName) + " - FAILED";
                        displayMsg = "\n" + indentation + "- FAILED. Creating file failed. " + Regex.Replace(errMsg, @"\r\n", "", RegexOptions.IgnoreCase) + "\n" + indentation + "File to be created: " + Path.Combine(destPath, destFileName);
                     }

                     setupSuccessful = false;
                  }
               }
            }

            // blank out the line
            displayFrm.removeStatusDisplayLine(fileCountLineIndex, false);

            // blank out the line
            displayFrm.removeStatusDisplayLine(m_copyProgressLineIndex, false);

            if (!overwriteDestinationFileAlways)
               overwriteDestinationFile = false;

            if (!setupSuccessful)
            {
               break;
            }

            UpdateStatusDisplayAndLogEvent("", logMsg, textPropList);
         }

         for (int i = 1; i <= 4; i++)
         {
            // delete the very first line that is the start of the progress update
            // all the lines below the first starting lines will get deleted too
            displayFrm.removeStatusDisplayLine(fileCountLineIndex, true);
         }

         if (!setupSuccessful)
         {
            UpdateStatusDisplayAndLogEvent(displayMsg, logMsg, textPropList);
         }

         return setupSuccessful;
      }

      ///===================================================================================
      /// ProcessXML2ndLevelNodes
      ///===================================================================================
      /// <summary>
      /// Process the child nodes <software></software> of the 2nd level node 
      /// <softwarebuildstructure></softwarebuildstructure> in the XML file.
      /// </summary>
      /// <param name="nav">navigator object for traversing XML tree</param>
      /// <param name="sourceFile"></param>
      /// <param name="packageInSourceControl">info about software package in source control
      /// </param>
      /// <param name="fileActions">file actions associated with source files</param>
      /// <param name="filesSaveAs">file name to be saved as at destination</param>
      /// <param name="bgw">backgroundworker</param>
      /// <param name="e">backgroundworker event arguments</param>
      /// <returns>true if processing node successfully, otherwise false</returns>
      ///===================================================================================
      private bool ProcessXML2ndLevelNodes(XPathNavigator nav, string defaultStartingSourcePath)
      {
         bool setupSuccessful = true;

         CommonLib.Windows.Forms.TextFormat.TextFontAndColor defaultTextProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         CommonLib.Windows.Forms.TextFormat.TextFontAndColor textProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> textPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();

         string msg, indentation = String.Empty;
         string startingSourcePath = "";
         Match regexMatch;

         if (nav == null)
            setupSuccessful = false;

         SourceFile sourceFile = new SourceFile("");

         if (setupSuccessful)
         {
            do
            {
               string packageName = nav.GetAttribute("name", String.Empty);

               if (packageName.Length == 0)
               {
                  // set font to default text and color
                  textProp.textFont = defaultTextProp.textFont;
                  textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                  textProp.wordIndex = 0;
                  textProp.wordCount = 2;
                  // set font style
                  textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                  textPropList.Add(textProp);

                  indentation = Common.getIndentation(1);
                  msg = "\n" + indentation + "- FAILED. Attribute \"name\" not found or has invalid value in node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                  setupSuccessful = false;
               }
               else
               {
                  // set font to default text and color
                  textProp.textFont = defaultTextProp.textFont;
                  textProp.textColor = Color.FromArgb(233, 31, 195);
                  textProp.wordIndex = 3;
                  textProp.wordCount = 100;
                  // set font style
                  textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                  textPropList.Add(textProp);

                  indentation = Common.getIndentation(1);
                  msg = "\n" + indentation + "Copying File Package " + packageName;
                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);


                  string customStartingSourcePath = nav.GetAttribute("startingsourcepath", string.Empty);
                  string startingDestPath = nav.GetAttribute("startingdestpath", string.Empty);

                  // match macro defined in the value of they key, macro is in the form of {xxxxx} such as {user_desktop}, {user_temp_folder}, {cd_drive}
                  regexMatch = Regex.Match(customStartingSourcePath, @"^\{([^\{\}]+)\}.*", RegexOptions.IgnoreCase);

                  // if rootpath is ., then we take the path of the XML package as the root path
                  if (string.Equals(customStartingSourcePath, "."))
                     customStartingSourcePath = defaultStartingSourcePath;
                  else if (regexMatch.Success)
                  {
                     if (Common.pathContainsMacro(regexMatch.Groups[0].Value))
                     {
                        string resolvedPath = Common.resolveMacroPath(regexMatch.Groups[0].Value);

                        if (Common.pathContainsCdDriveMacro(regexMatch.Groups[0].Value))
                        {
                           if (resolvedPath.Length == 0)
                           {
                              // set font to default text and color
                              textProp.textFont = defaultTextProp.textFont;
                              textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                              textProp.wordIndex = 0;
                              textProp.wordCount = 2;
                              // set font style
                              textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                              textPropList.Add(textProp);

                              indentation = Common.getIndentation(2);
                              msg = "\n" + indentation + "- FAILED. " + regexMatch.Groups[0].Value + " macro was defined. But no CD drive is found in the system" + ". XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                              UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                              setupSuccessful = false;
                              break;
                           }

                           sourceFile.cdLabel = nav.GetAttribute("cdlabel", string.Empty);
                        }

                        customStartingSourcePath = resolvedPath;
                     }
                     else
                     {
                        // set font to default text and color
                        textProp.textFont = defaultTextProp.textFont;
                        textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                        textProp.wordIndex = 0;
                        textProp.wordCount = 2;
                        // set font style
                        textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                        textPropList.Add(textProp);

                        indentation = Common.getIndentation(2);
                        msg = "\n" + indentation + "- FAILED. Attribute \"startingsourcepath\" specifies an invalid macro in the path. XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                        UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                        setupSuccessful = false;
                        break;
                     }
                  }

                  if (setupSuccessful && customStartingSourcePath.Length > 0)
                  {
                     // if path is not absolute path
                     if (!Path.IsPathRooted(customStartingSourcePath) && !Regex.IsMatch(customStartingSourcePath, @"^\{([^\{\}]+)\}", RegexOptions.IgnoreCase))
                        customStartingSourcePath = Path.GetFullPath(Path.Combine(defaultStartingSourcePath, customStartingSourcePath));

                     if (!Hardware.IsCdDrive(customStartingSourcePath) && !Directory.Exists(customStartingSourcePath))
                     {
                        // set font to default text and color
                        textProp.textFont = defaultTextProp.textFont;
                        textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                        textProp.wordIndex = 0;
                        textProp.wordCount = 2;
                        // set font style
                        textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                        textPropList.Add(textProp);

                        indentation = Common.getIndentation(2);
                        msg = "\n" + indentation + "- FAILED. Attribute \"startingsourcepath\" specifies a path that does not exist. XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                        UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                        setupSuccessful = false;
                        break;
                     }
                  }

                  // match macro defined in the value of they key, macro is in the form of {xxxxx} such as {user_desktop}, {user_temp_folder}, {cd_drive}
                  regexMatch = Regex.Match(startingDestPath, @"^\{([^\{\}]+)\}.*", RegexOptions.IgnoreCase);

                  if (regexMatch.Success)
                  {
                     if (Common.pathContainsMacro(regexMatch.Groups[0].Value))
                     {
                        string resolvedPath = Common.resolveMacroPath(regexMatch.Groups[0].Value);

                        if (Common.pathContainsCdDriveMacro(regexMatch.Groups[0].Value))
                        {
                           // set font to default text and color
                           textProp.textFont = defaultTextProp.textFont;
                           textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                           textProp.wordIndex = 0;
                           textProp.wordCount = 2;
                           // set font style
                           textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                           textPropList.Add(textProp);

                           indentation = Common.getIndentation(2);
                           msg = "\n" + indentation + "- FAILED. " + regexMatch.Groups[0].Value + " macro is not allowed to be specified for attribute \"startingdestpath\"" + ". XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                           UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                           setupSuccessful = false;
                           break;
                        }

                        startingDestPath = resolvedPath;

                     }
                     else
                     {
                        // set font to default text and color
                        textProp.textFont = defaultTextProp.textFont;
                        textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                        textProp.wordIndex = 0;
                        textProp.wordCount = 2;
                        // set font style
                        textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                        textPropList.Add(textProp);

                        indentation = Common.getIndentation(2);
                        msg = "\n" + indentation + "- FAILED. Attribute \"startingdestpath\" specifies an invalid macro in the path. XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                        UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                        setupSuccessful = false;
                        break;
                     }
                  }
                  // if path is not absolute path
                  else if (startingDestPath.Length > 0 && !Path.IsPathRooted(startingDestPath))
                     startingDestPath = Path.GetFullPath(Path.Combine(defaultStartingSourcePath, startingDestPath));

                  if (!Directory.Exists(startingDestPath))
                  {
                     if (startingDestPath.Length > 0)
                     {
                        if (!PathManip.PathIsWritable(startingDestPath))
                           setupSuccessful = false;
                     }
                     else
                        setupSuccessful = false;

                     if (!setupSuccessful)
                     {
                        // set font to default text and color
                        textProp.textFont = defaultTextProp.textFont;
                        textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                        textProp.wordIndex = 0;
                        textProp.wordCount = 2;
                        // set font style
                        textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                        textPropList.Add(textProp);

                        indentation = Common.getIndentation(2);
                        if (startingDestPath.Length == 0)
                           msg = "\n" + indentation + "- FAILED. Attribute \"startingdestpath\" is missing or has no value. XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                        else
                           msg = "\n" + indentation + "- FAILED. Attribute \"startingdestpath\" specifies a path that is invalid or not accessible. XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                        UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                        break;
                     }
                  }

                  if (customStartingSourcePath.Length > 0)
                     startingSourcePath = customStartingSourcePath;

                  // move to 3rd level node
                  if (nav.MoveToFirstChild())
                  {
                     m_fileCreatedCount = 0;
                     m_fileCopyCount = 0;
                     if (!ProcessXML3rdLevelNodes(nav, defaultStartingSourcePath, startingSourcePath, startingDestPath, sourceFile))
                        setupSuccessful = false;

                     // move back to 2nd level node
                     nav.MoveToParent();
                  }

                  if (setupSuccessful)
                  {
                     if (!m_filePackageProcessedSuccessful)
                        m_filePackageProcessedSuccessful = true;

                     // set font to default text and color
                     textProp.textFont = defaultTextProp.textFont;
                     textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.SUCCESS);
                     textProp.wordIndex = 0;
                     textProp.wordCount = 2;
                     // set font style
                     textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);

                     if (m_fileCreatedCount == 0 && m_fileCopyCount == 0)
                     {
                        textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.SYSTEM_SKIP);
                        msg = " - SKIPPED. (Files Created: " + m_fileCreatedCount + " ) " + "(Files Copied: " + m_fileCopyCount + " ) ";
                     }
                     else
                     {
                        textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.SUCCESS);
                        msg = " - SUCCESS. (Files Created: " + m_fileCreatedCount + " ) " + "(Files Copied: " + m_fileCopyCount + " ) ";
                     }
                     textPropList.Add(textProp);
                     UpdateStatusDisplayAndLogEvent(msg, "", textPropList);

                     msg = "\n" + Common.getIndentation(2) + "Total Files Created: " + m_fileCreatedCount + "   |   Total Files Copied: " + m_fileCopyCount;
                     UpdateStatusDisplayAndLogEvent("", msg, textPropList);
                  }

                  if (!setupSuccessful)
                     break;
               }

            } while (nav.MoveToNext()); // move to next software package
         }

         return setupSuccessful;
      }

      ///===================================================================================
      /// ProcessXML3rdLevelNodes
      ///===================================================================================
      /// <summary>
      /// Process the child nodes <dircopy></dircopy> of the 3rd level node 
      /// <software></software> in the XML file. 
      /// </summary>
      /// <param name="nav">navigator object for traversing XML tree</param>
      /// <param name="sourceStartingPath"></param>
      /// <param name="startingDestPath"></param>
      /// <param name="sourceFile"></param>
      /// <returns>true if processing node successfully, otherwise false</returns>
      ///===================================================================================
      private bool ProcessXML3rdLevelNodes(XPathNavigator nav, string defaultStartingSourcePath, string startingSourcePath, string startingDestPath, SourceFile sourceFile)
      {
         bool setupSuccessful = true;

         CommonLib.Windows.Forms.TextFormat.TextFontAndColor defaultTextProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         CommonLib.Windows.Forms.TextFormat.TextFontAndColor textProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> textPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();

         string msg, indentation = String.Empty, errMsg = "", tempStr = "";

         Match regExMatch;

         eCopyType copyType = eCopyType.FILES;

         do
         {
            sourceFiles.Clear();
            sourceFile.SourcePath = "";
            sourceFile.destPath = "";

            if (string.Equals(nav.Name, "filescopy", StringComparison.CurrentCultureIgnoreCase))
            {
               copyType = eCopyType.FILES;
            }
            else if (string.Equals(nav.Name, "folderscopy", StringComparison.CurrentCultureIgnoreCase))
            {
               copyType = eCopyType.FOLDERS;
            }
            else
            {
               // set font to default text and color
               textProp.textFont = defaultTextProp.textFont;
               textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
               textProp.wordIndex = 0;
               textProp.wordCount = 2;
               // set font style
               textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
               textPropList.Add(textProp);

               indentation = Common.getIndentation(2);
               msg = "\n" + indentation + "- FAILED. Invalid tag name specified. XML Node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
               UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

               setupSuccessful = false;
               break;
            }

            // do not force sourcedir to be specified to allow user to be able to create a new file at the destination
            sourceFile.SourcePath = nav.GetAttribute("sourcedir", string.Empty);
            sourceFile.SourcePath = sourceFile.SourcePath.Trim(' ');

            // match macro defined in the value of they key, macro is in the form of {xxxxx} such as {user_desktop}, {user_temp_folder}, {cd_drive}
            regExMatch = Regex.Match(sourceFile.SourcePath, @"^\{([^\{\}]+)\}.*", RegexOptions.IgnoreCase);

            if (string.Equals(sourceFile.SourcePath, "."))
            {
               if (startingSourcePath.Length > 0)
                  sourceFile.SourcePath = startingSourcePath;
               else
                  sourceFile.SourcePath = defaultStartingSourcePath;
            }
            else if (regExMatch.Success)
            {
               if (Common.pathContainsMacro(regExMatch.Groups[0].Value))
               {
                  string resolvedPath = Common.resolveMacroPath(regExMatch.Groups[0].Value);

                  if (Common.pathContainsCdDriveMacro(regExMatch.Groups[0].Value))
                  {
                     if (resolvedPath.Length == 0)
                     {
                        // set font to default text and color
                        textProp.textFont = defaultTextProp.textFont;
                        textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                        textProp.wordIndex = 0;
                        textProp.wordCount = 2;
                        // set font style
                        textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                        textPropList.Add(textProp);

                        indentation = Common.getIndentation(2);
                        msg = "\n" + indentation + "- FAILED. " + regExMatch.Groups[0].Value + " macro was defined. But no CD drive is found in the system" + ". XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                        UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                        setupSuccessful = false;
                        break;
                     }
                  }

                  sourceFile.SourcePath = resolvedPath;
               }
               else
               {
                  // set font to default text and color
                  textProp.textFont = defaultTextProp.textFont;
                  textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                  textProp.wordIndex = 0;
                  textProp.wordCount = 2;
                  // set font style
                  textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                  textPropList.Add(textProp);

                  indentation = Common.getIndentation(2);
                  msg = "\n" + indentation + "- FAILED. Attribute \"sourcedir\" specifies an invalid macro in the path. XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                  setupSuccessful = false;
                  break;
               }
            }

            tempStr = nav.GetAttribute("cdlabel", string.Empty);

            if (tempStr.Length > 0)
            {
               sourceFile.cdLabel = tempStr;
            }

            if (sourceFile.SourcePath.Length > 0)
            {
               // if path is not absolute path
               if (!Path.IsPathRooted(sourceFile.SourcePath))
               {
                  if (startingSourcePath.Length > 0)
                  {
                     if (!PathManip.PathContainsAtLeastOneFolder(startingSourcePath))
                        startingSourcePath = PathManip.AddTrailingSlashToPath(startingSourcePath);

                     sourceFile.SourcePath = Path.GetFullPath(Path.Combine(startingSourcePath, sourceFile.SourcePath));
                  }
                  else
                  {
                     // set font to default text and color
                     textProp.textFont = defaultTextProp.textFont;
                     textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                     textProp.wordIndex = 0;
                     textProp.wordCount = 2;
                     // set font style
                     textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                     textPropList.Add(textProp);

                     indentation = Common.getIndentation(2);
                     msg = "\n" + indentation + "- FAILED. Attribute \"sourcedir\" cannot specify a relative path here since there's no absolute path defined in the parent node. XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                     UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                     setupSuccessful = false;
                     break;
                  }
               }
            }

            if (setupSuccessful && sourceFile.SourcePath.Length > 0 && !Hardware.IsCdDrive(sourceFile.SourcePath) && !Directory.Exists(sourceFile.SourcePath))
            {
               // set font to default text and color
               textProp.textFont = defaultTextProp.textFont;
               textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
               textProp.wordIndex = 0;
               textProp.wordCount = 2;
               // set font style
               textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
               textPropList.Add(textProp);

               indentation = Common.getIndentation(2);

               // the directory exists but the account being used by application is denied access
               if (PathManip.DirectoryVisible(sourceFile.SourcePath, ref errMsg))
               {
                  msg = "\n" + indentation + "- FAILED. " + errMsg + ".";

                  if (PathManip.PathIsNetworkPath(sourceFile.SourcePath))
                  {
                     msg += "\n" + indentation + "This error is caused by the fact that this application is run as an account that does not have access privileges to the network path. "
                             + "\n" + indentation + "Run this application using an account that is allowed by the network path you're trying to access. XML Node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                  }
               }
               else
               {
                  msg = "\n" + indentation + "- FAILED. Attribute \"sourcedir\" specifies a relative/absolute path that does not exist in file system. XML Node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
               }

               UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);
               setupSuccessful = false;
               break;

            }

            sourceFile.destPath = nav.GetAttribute("destdir", string.Empty).TrimEnd('\\');
            sourceFile.destPath = sourceFile.destPath.Trim(' ');

            // match macro defined in the value of they key, macro is in the form of {xxxxx} such as {user_desktop}, {user_temp_folder}, {cd_drive}
            regExMatch = Regex.Match(sourceFile.destPath, @"^\{([^\{\}]+)\}.*", RegexOptions.IgnoreCase);

            if (regExMatch.Success)
            {
               if (Common.pathContainsMacro(regExMatch.Groups[0].Value))
               {
                  string resolvedPath = Common.resolveMacroPath(regExMatch.Groups[0].Value);

                  if (Common.pathContainsCdDriveMacro(regExMatch.Groups[0].Value))
                  {
                     // set font to default text and color
                     textProp.textFont = defaultTextProp.textFont;
                     textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                     textProp.wordIndex = 0;
                     textProp.wordCount = 2;
                     // set font style
                     textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                     textPropList.Add(textProp);

                     indentation = Common.getIndentation(2);
                     msg = "\n" + indentation + "- FAILED. " + regExMatch.Groups[0].Value + " macro is not allowed to be specified for attribute \"destdir\"" + ". XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                     UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                     setupSuccessful = false;
                     break;
                  }

                  sourceFile.destPath = resolvedPath;

               }
               else
               {
                  // set font to default text and color
                  textProp.textFont = defaultTextProp.textFont;
                  textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                  textProp.wordIndex = 0;
                  textProp.wordCount = 2;
                  // set font style
                  textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                  textPropList.Add(textProp);

                  indentation = Common.getIndentation(2);
                  msg = "\n" + indentation + "- FAILED. Attribute \"destdir\" specifies an invalid macro in the path. XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                  setupSuccessful = false;
                  break;
               }
            }
            else if (string.Equals(sourceFile.destPath, "."))
            {
               if (startingDestPath.Length > 0)
                  sourceFile.destPath = startingDestPath;
               else
                  sourceFile.destPath = defaultStartingSourcePath;
            }

            if (sourceFile.destPath.Length > 0)
            {
               // if path is not absolute path
               if (!Path.IsPathRooted(sourceFile.destPath))
               {
                  if (startingDestPath.Length > 0)
                  {
                     string absPathFiltered = sourceFile.destPath;
                     // remove unwated characters at beginning of relative path
                     regExMatch = Regex.Match(sourceFile.destPath, @"^[\\\.]+(.+)", RegexOptions.IgnoreCase);
                     if (regExMatch.Success)
                        absPathFiltered = regExMatch.Groups[1].Value;

                     if (!PathManip.PathContainsAtLeastOneFolder(startingDestPath))
                        startingDestPath = PathManip.AddTrailingSlashToPath(startingDestPath);

                     sourceFile.destPath = Path.Combine(startingDestPath, absPathFiltered);
                  }
                  else
                  {
                     // set font to default text and color
                     textProp.textFont = defaultTextProp.textFont;
                     textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                     textProp.wordIndex = 0;
                     textProp.wordCount = 2;
                     // set font style
                     textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                     textPropList.Add(textProp);

                     indentation = Common.getIndentation(2);
                     msg = "\n" + indentation + "- FAILED. Attribute \"destdir\" cannot specify a relative path here since there's no absolute path defined in the parent node. XML node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
                     UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                     setupSuccessful = false;
                     break;
                  }
               }
            }
            else
            {
               // set font to default text and color
               textProp.textFont = defaultTextProp.textFont;
               textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
               textProp.wordIndex = 0;
               textProp.wordCount = 2;
               // set font style
               textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
               textPropList.Add(textProp);

               indentation = Common.getIndentation(2);
               msg = "\n" + indentation + "- FAILED. Attribute \"destdir\" is missing or has no value in node " + nav.OuterXml.Substring(0, nav.OuterXml.IndexOf(">") + 1) + " on line " + ((IXmlLineInfo)nav).LineNumber.ToString();
               UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

               setupSuccessful = false;
               break;
            }

            sourceFile.masterOverwrite = nav.GetAttribute("overwriteallfiles", string.Empty).TrimEnd('\\');
            sourceFile.Recursive = nav.GetAttribute("recursive", string.Empty).TrimEnd('\\');

            if (!ProcessXML4thLevelNodes(nav, sourceFile, copyType))
               setupSuccessful = false;

            // after processing a package, perform file transfer
            if (setupSuccessful && sourceFiles.Count > 0)
            {
               setupSuccessful = processFilePackage();
            }

         } while (nav.MoveToNext());

         return setupSuccessful;
      }

      ///===================================================================================
      /// ProcessXML4thLevelNodes
      ///===================================================================================
      /// <summary>
      /// Process the child nodes <fileinclude></fileinclude> of the 4th level node 
      /// <dircopy></dircopy> in the XML file. Get all the files to be built
      /// </summary>
      /// <param name="nav">navigator object for traversing XML tree</param>
      /// <param name="sourceFile"></param>
      /// <returns>true if processing node successfully, otherwise false</returns>
      ///===================================================================================
      private bool ProcessXML4thLevelNodes(XPathNavigator nav, SourceFile sourceFile, eCopyType copyType)
      {
         List<string[,]> fileActions = new List<string[,]>();
         List<string[,]> filesSaveAs = new List<string[,]>();
         List<string[,]> listOverwriteFiles = new List<string[,]>();
         string fileInclude = "", fileExclude = "";
         string[,] fileAction = new string[1, 2];
         string[,] fileSaveAs = new string[1, 2];
         string[,] overwriteFiles = new string[1, 2];
         string msg = "", tempMsg = "";
         bool setupSuccessful = true;

         CommonLib.Windows.Forms.TextFormat.TextFontAndColor defaultTextProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         CommonLib.Windows.Forms.TextFormat.TextFontAndColor textProp = new CommonLib.Windows.Forms.TextFormat.TextFontAndColor("");
         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> textPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();

         string indentation = String.Empty;

         if (nav.MoveToFirstChild())
         {
            // extract all the files and actions for files
            do
            {
               fileAction[0, 0] = "";
               fileAction[0, 1] = "";

               fileSaveAs[0, 0] = "";
               fileSaveAs[0, 1] = "";

               overwriteFiles[0, 0] = "";
               overwriteFiles[0, 1] = "";

               if (string.Equals(nav.Name, "fileinclude", StringComparison.CurrentCultureIgnoreCase))
               {
                  if (nav.Value.Length > 0)
                  {
                     if (fileInclude == string.Empty)
                        fileInclude = nav.Value.Trim();
                     else if (nav.Value.Trim() == "*.*")
                        fileInclude = nav.Value.Trim();
                     else if (fileInclude != "*.*")
                        fileInclude += "," + nav.Value.Trim();

                     // check to see if this file needs to be renamed at the destination
                     overwriteFiles[0, 0] = nav.GetAttribute("overwritefile", string.Empty);

                     if (overwriteFiles[0, 0].Length > 0)
                     {
                        // this is the file
                        overwriteFiles[0, 1] = nav.Value;
                     }

                     if (overwriteFiles[0, 0] != string.Empty && overwriteFiles[0, 1] != string.Empty)
                     {
                        listOverwriteFiles.Add(new string[,] { { overwriteFiles[0, 0], overwriteFiles[0, 1] } });
                     }

                     if (fileInclude != "*.*")
                     {
                        // check to see if this file needs to be renamed at the destination
                        fileSaveAs[0, 0] = nav.GetAttribute("saveas", string.Empty);

                        if (fileSaveAs[0, 0].Length > 0)
                        {
                           // this is the file
                           fileSaveAs[0, 1] = nav.Value;
                        }
                     }

                     if (fileSaveAs[0, 0] != string.Empty && fileSaveAs[0, 1] != string.Empty && sourceFile.SourcePath.Length > 0)
                        filesSaveAs.Add(new string[,] { { fileSaveAs[0, 0], fileSaveAs[0, 1] } });

                     // as long as the file specified doesn't contain wildcard
                     if (!Regex.IsMatch(nav.Value, @"\*", RegexOptions.IgnoreCase))
                     {
                        // check if there are any actions specified for this file
                        string attr = nav.GetAttribute("actions", string.Empty);

                        if (attr.Length > 0)
                        {
                           string[] actions = attr.Split(',');

                           if (actions.Length > 0)
                           {
                              foreach (string action in actions)
                                 fileActions.Add(new string[,] { { action, nav.Value } });
                           }
                        }
                     }
                  }
               }
               else if (string.Equals(nav.Name, "fileexclude", StringComparison.CurrentCultureIgnoreCase))
               {
                  if (nav.Value.Length > 0)
                  {
                     if (fileExclude == string.Empty)
                        fileExclude += nav.Value;
                     else
                        fileExclude += "," + nav.Value;
                  }
               }
            } while (nav.MoveToNext());

            nav.MoveToParent();
         }

         Match regExMatch;

         string[] filesToExclude = new string[] { };

         if (fileExclude.Length > 0)
         {
            List<string> paths = new List<string>();
            string[] files = fileExclude.Split(',');
            string[] files2;

            foreach (string file in files)
            {
               regExMatch = Regex.Match(file, @"\*\.[^\*,]+");
               if (regExMatch.Success)
               {
                  foreach (var drive in DriveInfo.GetDrives()
                                  .Where(d => d.DriveType == DriveType.CDRom))
                  {
                     if (Regex.IsMatch(sourceFile.SourcePath, @"^" + Regex.Escape(PathManip.RemoveTrailingSlashInPath(drive.Name)) + @".*", RegexOptions.IgnoreCase))
                     {
                        while (!drive.IsReady || !Directory.Exists(sourceFile.SourcePath))
                        {
                           Hardware.OpenCdDrive(drive.Name);

                           tempMsg = "Please insert a disc";

                           if (sourceFile.cdLabel.Length > 0)
                              tempMsg += " labeled \"" + sourceFile.cdLabel + "\"";

                           tempMsg += " into drive " + drive.Name + " that has the following path:\n" + sourceFile.SourcePath + "\n\n Click OK to proceed";

                           msg = ((frmSetupStatusDisplay)m_parentForm).displayMessageBox(MessageBoxCustom.PopUpMsgType.OKCANCEL, null,
                              tempMsg, "Info", -1);

                           if (String.Equals(msg, "cancel", StringComparison.OrdinalIgnoreCase))
                           {
                              // set font to default text and color
                              textProp.textFont = defaultTextProp.textFont;
                              textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                              textProp.wordIndex = 0;
                              textProp.wordCount = 2;
                              // set font style
                              textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                              textPropList.Add(textProp);

                              indentation = Common.getIndentation(2);
                              msg = "\n" + indentation + "- FAILED. Unable to locate path: " + sourceFile.SourcePath;
                              UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                              setupSuccessful = false;
                              break;
                           }
                        }

                        break;
                     }
                  }

                  if (setupSuccessful)
                  {
                     if (copyType == eCopyType.FILES)
                     {
                        if (String.Equals(sourceFile.Recursive, "yes", StringComparison.OrdinalIgnoreCase) || String.Equals(sourceFile.Recursive, "true", StringComparison.OrdinalIgnoreCase))
                        {
                           files2 = Directory.GetFiles(sourceFile.SourcePath, regExMatch.Value, SearchOption.AllDirectories);
                        }
                        else
                           files2 = Directory.GetFiles(sourceFile.SourcePath, regExMatch.Value, SearchOption.TopDirectoryOnly);
                     }
                     else
                        files2 = Directory.GetFiles(sourceFile.SourcePath, regExMatch.Value, SearchOption.AllDirectories);

                     paths.AddRange(files2.ToList());
                  }
               }
               else
               {
                  paths.Add(sourceFile.SourcePath + @"\" + file);
               }
            }

            filesToExclude = paths.ToArray();
         }

         string[] filesToInclude = new string[] { };

         if (setupSuccessful)
         {
            // look for wildcard search for files
            regExMatch = Regex.Match(fileInclude, @"^\*\.\*$");

            // if file to be copied is defined as *.*, then we want to include all files in the source directory
            if (regExMatch.Success && sourceFile.SourcePath.Length > 0)
            {
               foreach (var drive in DriveInfo.GetDrives()
                                  .Where(d => d.DriveType == DriveType.CDRom))
               {
                  if (Regex.IsMatch(sourceFile.SourcePath, @"^" + Regex.Escape(PathManip.RemoveTrailingSlashInPath(drive.Name)) + @".*", RegexOptions.IgnoreCase))
                  {
                     while (!drive.IsReady || !Directory.Exists(sourceFile.SourcePath))
                     {
                        Hardware.OpenCdDrive(drive.Name);

                        tempMsg = "Please insert a disc";

                        if (sourceFile.cdLabel.Length > 0)
                           tempMsg += " labeled \"" + sourceFile.cdLabel + "\"";

                        tempMsg += " into drive " + drive.Name + " that has the following path:\n" + sourceFile.SourcePath + "\n\n Click OK to proceed";

                        msg = ((frmSetupStatusDisplay)m_parentForm).displayMessageBox(MessageBoxCustom.PopUpMsgType.OKCANCEL, null,
                           tempMsg, "Info", -1);

                        if (String.Equals(msg, "cancel", StringComparison.OrdinalIgnoreCase))
                        {
                           // set font to default text and color
                           textProp.textFont = defaultTextProp.textFont;
                           textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                           textProp.wordIndex = 0;
                           textProp.wordCount = 2;
                           // set font style
                           textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                           textPropList.Add(textProp);

                           indentation = Common.getIndentation(2);
                           msg = "\n" + indentation + "- FAILED. Unable to locate path: " + sourceFile.SourcePath;
                           UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                           setupSuccessful = false;
                           break;
                        }
                     }

                     break;
                  }
               }

               if (setupSuccessful)
               {
                  if (copyType == eCopyType.FOLDERS)
                  {
                     filesToInclude = Directory.GetFiles(sourceFile.SourcePath, fileInclude, SearchOption.AllDirectories);
                  }
                  else
                  {
                     if (String.Equals(sourceFile.Recursive, "yes", StringComparison.OrdinalIgnoreCase) || String.Equals(sourceFile.Recursive, "true", StringComparison.OrdinalIgnoreCase))
                     {
                        filesToInclude = Directory.GetFiles(sourceFile.SourcePath, fileInclude, SearchOption.AllDirectories);
                     }
                     else
                        filesToInclude = Directory.GetFiles(sourceFile.SourcePath, fileInclude, SearchOption.TopDirectoryOnly);

                     // save overwrite file flag
                     foreach (string[,] overwriteFlag in listOverwriteFiles)
                     {
                        // if there's an overwrite flag associated with this file
                        if (string.Equals(overwriteFlag[0, 1], fileInclude, StringComparison.CurrentCultureIgnoreCase))
                        {
                           sourceFile.masterOverwrite = overwriteFlag[0, 0];
                           break;
                        }
                     }
                  }
               }
            }
            else if (fileInclude.Length > 0)   // get the individual files specified in the XML file
            {
               List<string> paths = new List<string>();
               string[] files = fileInclude.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
               string[] files2;
               foreach (string file in files)
               {
                  regExMatch = Regex.Match(file, @"\*\.[^\*,]+");
                  if (regExMatch.Success)
                  {
                     foreach (var drive in DriveInfo.GetDrives()
                                  .Where(d => d.DriveType == DriveType.CDRom))
                     {
                        if (Regex.IsMatch(sourceFile.SourcePath, @"^" + Regex.Escape(PathManip.RemoveTrailingSlashInPath(drive.Name)) + @".*", RegexOptions.IgnoreCase))
                        {
                           while (!drive.IsReady || !Directory.Exists(sourceFile.SourcePath))
                           {
                              Hardware.OpenCdDrive(drive.Name);

                              tempMsg = "Please insert a disc";

                              if (sourceFile.cdLabel.Length > 0)
                                 tempMsg += " labeled \"" + sourceFile.cdLabel + "\"";

                              tempMsg += " into drive " + drive.Name + " that has the following path:\n" + sourceFile.SourcePath + "\n\n Click OK to proceed";

                              msg = ((frmSetupStatusDisplay)m_parentForm).displayMessageBox(MessageBoxCustom.PopUpMsgType.OKCANCEL, null,
                                 tempMsg, "Info", -1);

                              if (String.Equals(msg, "cancel", StringComparison.OrdinalIgnoreCase))
                              {
                                 // set font to default text and color
                                 textProp.textFont = defaultTextProp.textFont;
                                 textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                                 textProp.wordIndex = 0;
                                 textProp.wordCount = 2;
                                 // set font style
                                 textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                                 textPropList.Add(textProp);

                                 indentation = Common.getIndentation(2);
                                 msg = "\n" + indentation + "- FAILED. Unable to locate path: " + sourceFile.SourcePath;
                                 UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                                 setupSuccessful = false;
                                 break;
                              }
                           }

                           break;
                        }
                     }

                     if (setupSuccessful)
                     {
                        if (String.Equals(sourceFile.Recursive, "yes", StringComparison.OrdinalIgnoreCase) || String.Equals(sourceFile.Recursive, "true", StringComparison.OrdinalIgnoreCase))
                        {
                           files2 = Directory.GetFiles(sourceFile.SourcePath, regExMatch.Value, SearchOption.AllDirectories);
                        }
                        else
                           files2 = Directory.GetFiles(sourceFile.SourcePath, regExMatch.Value, SearchOption.TopDirectoryOnly);

                        if (files2.Length > 0)
                        {
                           string overwriteFlag = "";
                           // save overwrite file flag
                           foreach (string[,] overwriteFlag2 in listOverwriteFiles)
                           {
                              // if there's an overwrite flag associated with this file
                              if (string.Equals(overwriteFlag2[0, 1], file, StringComparison.CurrentCultureIgnoreCase))
                              {
                                 overwriteFlag = overwriteFlag2[0, 0];
                                 break;
                              }
                           }

                           if (overwriteFlag.Length > 0)
                           {
                              for (int i = 0; i < files2.Length; i++)
                              {
                                 files2[i] += "," + overwriteFlag;
                              }
                           }
                        }

                        paths.AddRange(files2.ToList());
                     }
                  }
                  else
                  {
                     if (sourceFile.SourcePath.Length > 0)
                        paths.Add(sourceFile.SourcePath + @"\" + file);
                     else
                        paths.Add(file);
                  }
               }

               filesToInclude = paths.ToArray();
            }
         }

         if (setupSuccessful)
         {
            if (filesToInclude.Length == 0 && copyType == eCopyType.FOLDERS)
            {
               foreach (var drive in DriveInfo.GetDrives()
                                  .Where(d => d.DriveType == DriveType.CDRom))
               {
                  if (Regex.IsMatch(sourceFile.SourcePath, @"^" + Regex.Escape(PathManip.RemoveTrailingSlashInPath(drive.Name)) + @".*", RegexOptions.IgnoreCase))
                  {
                     while (!drive.IsReady || !Directory.Exists(sourceFile.SourcePath))
                     {
                        Hardware.OpenCdDrive(drive.Name);

                        tempMsg = "Please insert a disc";

                        if (sourceFile.cdLabel.Length > 0)
                           tempMsg += " labeled \"" + sourceFile.cdLabel + "\"";

                        tempMsg += " into drive " + drive.Name + " that has the following path:\n" + sourceFile.SourcePath + "\n\n Click OK to proceed";

                        msg = ((frmSetupStatusDisplay)m_parentForm).displayMessageBox(MessageBoxCustom.PopUpMsgType.OKCANCEL, null,
                           tempMsg, "Info", -1);

                        if (String.Equals(msg, "cancel", StringComparison.OrdinalIgnoreCase))
                        {
                           // set font to default text and color
                           textProp.textFont = defaultTextProp.textFont;
                           textProp.textColor = Common.getSetupStatusColor(Common.EXECUTION_STATUS.FAILURE);
                           textProp.wordIndex = 0;
                           textProp.wordCount = 2;
                           // set font style
                           textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
                           textPropList.Add(textProp);

                           indentation = Common.getIndentation(2);
                           msg = "\n" + indentation + "- FAILED. Unable to locate path: " + sourceFile.SourcePath;
                           UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                           setupSuccessful = false;
                           break;
                        }
                     }

                     break;
                  }
               }

               if (setupSuccessful)
                  filesToInclude = Directory.GetFiles(sourceFile.SourcePath, "*.*", SearchOption.AllDirectories);
            }
         }

         if (setupSuccessful)
         {
            // remove files that are excluded from build
            if (filesToExclude.Length > 0 && filesToInclude.Length > 0 && sourceFile.SourcePath.Length > 0)
            {
               List<string> filesToIncludeList = filesToInclude.ToList();
               List<string> filesToExcludeList = filesToExclude.ToList();

               foreach (string excludePath in filesToExcludeList)
               {
                  string pathToRemove = "";
                  foreach (string includePath in filesToIncludeList)
                  {
                     if (Regex.IsMatch(includePath, Regex.Escape(excludePath) + @"(,.+)*", RegexOptions.IgnoreCase))
                     {
                        pathToRemove = includePath;
                        break;
                     }
                  }

                  if (pathToRemove.Length > 0)
                  {
                     filesToIncludeList.Remove(pathToRemove);
                  }
               }

               filesToInclude = filesToIncludeList.ToArray();
            }
         }

         string baseSourceFolder = sourceFile.SourcePath;
         string baseDestFolder = sourceFile.destPath;

         if (setupSuccessful)
         {
            // for each file to be copied, determine if there's an action associated with it
            // or that it needs to be renamed, etc
            foreach (string file in filesToInclude)
            {
               string[] fileParts = file.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
               sourceFile.FileName = Path.GetFileName(fileParts[0]);
               sourceFile.FileNameSaveAs = "";
               sourceFile.Actions = new List<eFileAction>();
               sourceFile.individualOverwrite = "";

               if (sourceFile.masterOverwrite.Length > 0)
                  sourceFile.individualOverwrite = sourceFile.masterOverwrite;

               if (fileParts.Length == 1)
               {
                  // save overwrite file flag
                  foreach (string[,] overwriteFlag in listOverwriteFiles)
                  {
                     // if there's an overwrite flag associated with this file
                     if (string.Equals(overwriteFlag[0, 1], sourceFile.FileName, StringComparison.CurrentCultureIgnoreCase))
                     {
                        sourceFile.individualOverwrite = overwriteFlag[0, 0];
                        break;
                     }
                  }
               }
               else
               {
                  sourceFile.individualOverwrite = fileParts[1];
               }

               // save all the actions needed to be perform for this file
               foreach (string[,] action in fileActions)
               {
                  // if there's an action associated with this file
                  if (string.Equals(action[0, 1], sourceFile.FileName, StringComparison.CurrentCultureIgnoreCase))
                  {
                     //if (string.Equals(action[0, 0], eFileAction.CreateSWBuildIDEntries.ToString(), StringComparison.CurrentCultureIgnoreCase))
                     //   sourceFile.Actions.Add(eFileAction.CreateSWBuildIDEntries);
                     //else if (string.Equals(action[0, 0], eFileAction.BuildProjectUsingVS2010Compiler.ToString(), StringComparison.CurrentCultureIgnoreCase))
                     //   sourceFile.Actions.Add(eFileAction.BuildProjectUsingVS2010Compiler);
                     //else if (string.Equals(action[0, 0], eFileAction.BuildProjectUsingVS2009Compiler.ToString(), StringComparison.CurrentCultureIgnoreCase))
                     //   sourceFile.Actions.Add(eFileAction.BuildProjectUsingVS2009Compiler);
                     //else if (string.Equals(action[0, 0], eFileAction.BuildProjectInDebug.ToString(), StringComparison.CurrentCultureIgnoreCase))
                     //   sourceFile.Actions.Add(eFileAction.BuildProjectInDebug);
                     //else if (string.Equals(action[0, 0], eFileAction.BuildProjectInRelease.ToString(), StringComparison.CurrentCultureIgnoreCase))
                     //   sourceFile.Actions.Add(eFileAction.BuildProjectInRelease);
                  }
               }

               foreach (string[,] fileRename in filesSaveAs)
               {
                  // if a source file is to be renamed at the destination
                  if (string.Equals(fileRename[0, 1], sourceFile.FileName, StringComparison.CurrentCultureIgnoreCase) && sourceFile.SourcePath.Length > 0)
                  {
                     sourceFile.FileNameSaveAs = fileRename[0, 0];
                     break;
                  }
                  else if (Regex.IsMatch(fileRename[0, 1], @"^\*\.\w+$", RegexOptions.IgnoreCase))  // if the file to be renamed has a wildcard in it
                  {
                     regExMatch = Regex.Match(fileRename[0, 1], @".+(\.\w+)$", RegexOptions.IgnoreCase);

                     if (regExMatch.Success && regExMatch.Groups.Count > 1)
                     {
                        string pattern = Regex.Escape(regExMatch.Groups[1].Value) + @"$";

                        if (Regex.IsMatch(sourceFile.FileName, pattern, RegexOptions.IgnoreCase))
                        {
                           sourceFile.FileNameSaveAs = fileRename[0, 0];
                           break;
                        }
                     }
                  }
               }

               string fileSourcePath = Path.GetDirectoryName(file);
               string regexPattern = Regex.Escape(baseSourceFolder) + @"\\(.+)";
               if (copyType == eCopyType.FOLDERS)
               {
                  regExMatch = Regex.Match(fileSourcePath, regexPattern, RegexOptions.IgnoreCase);

                  if (regExMatch.Success)
                  {
                     sourceFile.SourcePath = fileSourcePath;
                     sourceFile.destPath = baseDestFolder + @"\" + regExMatch.Groups[1].Value;
                  }
               }
               else
                  sourceFile.SourcePath = fileSourcePath;

               sourceFiles.Add(sourceFile);
            }
         }

         return setupSuccessful;
      }

      bool verifyFilePackagesInfo()
      {
         bool setupSuccessful = true;
         ConfigFileManager.Ini_KeyValue<string> iniVal = new ConfigFileManager.Ini_KeyValue<string>("");
         string indentation = String.Empty;
         string msg;

         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> textPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();

         for (int i = 0; i < fileTransferPackages.Count; i++)
         {
            if (fileTransferPackages[i].iniValue.Length == 0)
            {
               indentation = Common.getIndentation(1);
               msg = "\n" + indentation + "- FAILED. "
                     + "\n" + indentation + "File: " + ConfigFileManager.ms_configGeneralInfo.pathAndNameOfConfigFile
                     + "\n" + indentation + "Section: " + fileTransferPackages[i].iniSectionName
                     + "\n" + indentation + "Key: " + fileTransferPackages[i].iniKeyName
                     + "\n" + indentation + "Value: " + fileTransferPackages[i].iniValue
                     + "\n" + indentation + "Error Description: The value is missing for the above section in the INI file.";

               Common.formatConfigIniFailureMessage(msg, textPropList);

               UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);
               setupSuccessful = false;
               break;
            }
            else
            {
               iniVal = fileTransferPackages[i];
               iniVal.iniValue = Path.GetFullPath(fileTransferPackages[i].iniValue);

               fileTransferPackages[i] = iniVal;

               if (!File.Exists(iniVal.iniValue))
               {
                  indentation = Common.getIndentation(1);
                  msg = "\n" + indentation + "- FAILED. "
                        + "\n" + indentation + "File: " + ConfigFileManager.ms_configGeneralInfo.pathAndNameOfConfigFile
                        + "\n" + indentation + "Section: " + fileTransferPackages[i].iniSectionName
                        + "\n" + indentation + "Key: " + fileTransferPackages[i].iniKeyName
                        + "\n" + indentation + "Value: " + fileTransferPackages[i].iniValue
                        + "\n" + indentation + "Error Description: The value indicates an invalid path in the above section in the INI file.";

                  Common.formatConfigIniFailureMessage(msg, textPropList);

                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);
                  setupSuccessful = false;
                  break;
               }
            }
         }

         return setupSuccessful;
      }

      public struct SourceFile
      {
         public string FileName
         {
            get;
            set;
         }

         public string FileNameSaveAs
         {
            get;
            set;
         }

         // specify whether to include files recursively
         public string Recursive
         {
            get;
            set;
         }

         // specify that all files will be overwritten
         public string masterOverwrite
         {
            get;
            set;
         }

         // specify that a particular file will be overwritten
         public string individualOverwrite
         {
            get;
            set;
         }

         public string SourcePath
         {
            get;
            set;
         }

         public string destPath
         {
            get;
            set;
         }

         public string cdLabel
         {
            get;
            set;
         }

         public string SoftwareProduct
         {
            get;
            set;
         }

         public List<eFileAction> Actions
         {
            get;
            set;
         }

         public SourceFile(string str)
            : this()
         {
            FileName = "";
            FileNameSaveAs = "";
            SourcePath = "";
            destPath = "";
            cdLabel = "";
            SoftwareProduct = "";
            Recursive = "";
            masterOverwrite = "";
            individualOverwrite = "";
         }

      }
   }
}
