using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using CommonLib.IO;
using CommonLib.Misc;
using CommonLib.Windows.Forms;

namespace All_Purpose_Auto_Setup
{
   class RunApplicationsManager
   {
      // 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;

      // this dictionary stores the application to be executed and the arguments associated with it
      // it would look like this:
      // [ c:\app1.exe ]  ->  [path arg arg_path]
      // [ c:\app2.exe ]  ->  [path arg arg_path]
      public Dictionary<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>> runAppToArguments = new Dictionary<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>>();

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

      int m_progressLine = 0;

      string m_consoleAppStatusMessage = "";

      Form m_parentForm;

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

      public bool runApplications(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-=Running applications=-";
         UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

         runAppToArguments.Clear();

         ConfigFileManager.readOneToManyDictionary(ConfigFileManager.ms_configGeneralInfo.iniFile,
                    sectionName, @"(app\d+)_path$", @"(app\d+)_.+$", runAppToArguments);

         if (setupSuccessful)
         {
            if (runAppToArguments.Count == 0 ||
               (runAppToArguments.Count == 1 && runAppToArguments.First().Value.Count == 0))
            {
               // 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 run";
               UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);
            }

            KeyValuePair<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>> processedConfigInfo = new KeyValuePair<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>>();

            // for every app to be run
            foreach (KeyValuePair<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>> entry in runAppToArguments)
            {
               string appDescription = "";
               string appArg = "";
               bool isConsoleApp = false;
               bool waitForExit = true;
               string cdLabel = "";
               string displayMessage = "";

               setupSuccessful = processConfigInfo(entry, ref processedConfigInfo);

               if (!setupSuccessful)
                  break;

               getAppInfo(processedConfigInfo.Value, ref appDescription, ref appArg, ref isConsoleApp, ref waitForExit, ref cdLabel, ref displayMessage);
               setupSuccessful = startApp(appDescription, processedConfigInfo.Key.alternateValue, appArg, isConsoleApp, waitForExit, cdLabel, displayMessage);

               if (!setupSuccessful)
                  break;
            }
         }

         return setupSuccessful;
      }

      public bool startApp(string appDescription, string appPath, string appArguments, bool isConsoleApp, bool waitForExit, string cdLabel, string displayMessage)
      {
         string msg1 = String.Empty, msg2 = String.Empty, msg3, indentation = String.Empty, tempMsg = string.Empty;
         bool setupSuccessful = true;
         Match regExMatch;

         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> textPropList1 = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();
         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> textPropList2 = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();

         indentation = Common.getIndentation(1);

         appPath = Path.GetFullPath(appPath);

         // the argument may specify equal sign in it.  But since INI files doesn't allow = to be
         // specified in the value of they key, we have to specify <eq> in place of =.
         // so now we replace instances of <eq> with =
         appArguments = Regex.Replace(appArguments, @"<eq>", "=", RegexOptions.IgnoreCase);

         if (appDescription.Length == 0)
         {
            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(233, 31, 195);
            textProp.wordIndex = 1;
            textProp.wordCount = 100;
            // set font style
            textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
            textPropList1.Add(textProp);

            msg1 = "\n" + indentation + "Running " + StringManip.ShortenStringToSize(appPath, 60);
            msg2 = "\n" + indentation + "Running " + appPath;
         }
         else
         {
            // set font to default text and color
            textProp.textFont = defaultTextProp.textFont;
            textProp.textColor = Color.FromArgb(233, 31, 195);
            textProp.wordIndex = 0;
            textProp.wordCount = 100;
            // set font style
            textProp.textFont = new Font(textProp.textFont, FontStyle.Bold);
            textPropList1.Add(textProp);

            msg1 = "\n" + indentation + appDescription;
            msg2 = "\n" + indentation + appDescription;
         }

         foreach (var drive in DriveInfo.GetDrives()
               .Where(d => d.DriveType == DriveType.CDRom))
         {
            if (Regex.IsMatch(appPath, @"^" + Regex.Escape(drive.Name) + @".+", RegexOptions.IgnoreCase))
            {
               while (!drive.IsReady || !File.Exists(appPath))
               {
                  Hardware.OpenCdDrive(drive.Name);

                  tempMsg = "Please insert a disc";

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

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

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

                  if (String.Equals(msg3, "cancel", StringComparison.OrdinalIgnoreCase))
                  {
                     break;
                  }
               }

               break;
            }
         }

         if (File.Exists(appPath))
         {
            if (displayMessage.Length > 0)
            {
               displayMessage = Regex.Replace(displayMessage, @"\{newline\}", "\n");
               ((frmSetupStatusDisplay)m_parentForm).displayMessageBox(MessageBoxCustom.PopUpMsgType.INFO, null, displayMessage, "Info", -1);
            }

            BackgroundWorker bgwInformUserToWait;

            bgwInformUserToWait = new BackgroundWorker();
            bgwInformUserToWait.WorkerSupportsCancellation = true;
            bgwInformUserToWait.DoWork += new DoWorkEventHandler(bgwInformUserToWait_DoWork);
            bgwInformUserToWait.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgwInformUserToWait_RunWorkerCompleted);

            frmSetupStatusDisplay displayFrm = (frmSetupStatusDisplay)m_parentForm;
            m_progressLine = displayFrm.updateStatusDisplayAndLog("\n  ", "", null);
            if (!bgwInformUserToWait.IsBusy)
            {
               indentation = Common.getIndentation(1);

               if (appDescription.Length == 0)
               {
                  bgwInformUserToWait.RunWorkerAsync(indentation + "Running " + Path.GetFileName(appPath) + ". Please wait");
               }
               else
               {
                  bgwInformUserToWait.RunWorkerAsync(indentation + appDescription + ". Please wait");
               }

            }

            try
            {
               regExMatch = Regex.Match(appPath, @"\.bat$", RegexOptions.IgnoreCase);

               // if we're executing a bat file
               if (regExMatch.Success)
               {
                  ProcessStartInfo ProcStartInfo = new ProcessStartInfo(@"C:\Windows\System32\cmd.exe");
                  ProcStartInfo.RedirectStandardOutput = false;
                  ProcStartInfo.UseShellExecute = false;
                  ProcStartInfo.CreateNoWindow = true;
                  ProcStartInfo.RedirectStandardError = true;
                  System.Diagnostics.Process process = new System.Diagnostics.Process();
                  ProcStartInfo.Arguments = "/c start /wait /d \"" + Path.GetDirectoryName(appPath) + "\" " + Path.GetFileName(appPath);
                  process.StartInfo = ProcStartInfo;
                  process.Start();

                  if (waitForExit)
                  {
                     process.WaitForExit();

                     int exitCode = process.ExitCode;
                     process.Close();

                     if (exitCode != 0)
                        throw new Exception(Path.GetFileName(appPath) + " exited with code: " + exitCode.ToString());
                  }
               }
               else if (isConsoleApp)
               {
                  ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
                  cmdStartInfo.FileName = @"C:\Windows\System32\cmd.exe";
                  cmdStartInfo.RedirectStandardOutput = true;
                  cmdStartInfo.RedirectStandardError = true;
                  cmdStartInfo.RedirectStandardInput = true;
                  cmdStartInfo.UseShellExecute = false;
                  cmdStartInfo.CreateNoWindow = true;

                  Process process = new Process();
                  process.StartInfo = cmdStartInfo;
                  process.ErrorDataReceived += cmd_Error;
                  process.OutputDataReceived += cmd_DataReceived;
                  process.EnableRaisingEvents = true;
                  process.Start();
                  process.BeginOutputReadLine();
                  process.BeginErrorReadLine();

                  process.StandardInput.WriteLine(appPath + " " + appArguments);     //Execute command

                  process.StandardInput.WriteLine("exit");                  //Execute exit.

                  if (waitForExit)
                  {
                     process.WaitForExit();

                     int exitCode = process.ExitCode;
                     process.Close();

                     if (exitCode != 0)
                     {
                        if (appReturnedFatalError(appPath, exitCode, ""))
                        {
                           throw new Exception(Path.GetFileName(appPath) + " exited with code: " + exitCode.ToString() + ". " + m_consoleAppStatusMessage);
                        }
                     }
                  }
               }
               else
               {
                  var process = Process.Start(appPath, appArguments);

                  if (waitForExit)
                  {
                     process.WaitForExit();

                     int exitCode = process.ExitCode;
                     process.Close();

                     if (exitCode != 0)
                        throw new Exception(Path.GetFileName(appPath) + " exited with code: " + exitCode.ToString());
                  }
               }

               // 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);
               textPropList2.Add(textProp);

               msg3 = " - SUCCESS";

            }
            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);
               textPropList2.Add(textProp);

               msg3 = " - FAILED. Error executing file " + appPath + ". " + e.Message;

               setupSuccessful = false;
            }
            finally
            {
               if (!bgwInformUserToWait.CancellationPending && bgwInformUserToWait.IsBusy)
               {
                  bgwInformUserToWait.CancelAsync();
                  _resetEvent.WaitOne();
                  _resetEvent.Reset();
               }

               displayFrm.removeStatusDisplayLine(m_progressLine, true);
            }
         }
         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);
            textPropList2.Add(textProp);

            msg3 = " - FAILED. File " + appPath + " was not found.";

            setupSuccessful = false;
         }

         UpdateStatusDisplayAndLogEvent(msg1, msg2, textPropList1);
         UpdateStatusDisplayAndLogEvent(msg3, msg3, textPropList2);

         return setupSuccessful;
      }

      void cmd_DataReceived(object sender, DataReceivedEventArgs e)
      {
         if (e.Data == null)
         {
            return;
         }

         if (e.Data.Length > 0)
         {
            // since we are receiving everything that's being output to the standard output
            // we're only interested in the response of the command, and not the command itself
            if (!Regex.IsMatch(e.Data, @"^[a-zA-Z]:[^>]+>.+", RegexOptions.IgnoreCase))
               m_consoleAppStatusMessage = e.Data;
         }
      }

      void cmd_Error(object sender, DataReceivedEventArgs e)
      {
         if (e.Data == null)
         {
            return;
         }

         if (e.Data.Length > 0)
         {
            // since we are receiving everything that's being output to the standard output
            // we're only interested in the response of the command, and not the command itself
            if (!Regex.IsMatch(e.Data, @"^[a-zA-Z]:[^>]+>.+", RegexOptions.IgnoreCase))
               m_consoleAppStatusMessage = e.Data;
         }
      }

      private bool appReturnedFatalError(string appPath, int errCode, string errMsg)
      {
         bool fatalError = true;

         if (Regex.IsMatch(appPath, @"c:\\windows\\system32\\taskkill.exe", RegexOptions.IgnoreCase))
         {
            if (errCode == 128)
               fatalError = false;
         }

         return fatalError;
      }

      private void bgwInformUserToWait_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
         // First, handle the case where an exception was thrown. 
         if (e.Error != null)
         {

         }
         else if (e.Cancelled)  // user cancelled
         {

         }
         else // thread completed on its own
         {

         }
         _resetEvent.Set();   // signal that execution of this thread is done
      }

      private void bgwInformUserToWait_DoWork(object sender, DoWorkEventArgs e)
      {
         BackgroundWorker bgw = sender as BackgroundWorker;

         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 text = (string)e.Argument;
         string dots = "";

         while (true)
         {
            if (bgw != null && bgw.CancellationPending)
            {
               if (e != null)
                  e.Cancel = true;

               break;
            }

            if (dots.Length < 3)
               dots += ".";
            else
               dots = ".";

            if (StringManip.GetPhraseWordIndexInText(text, "running") > -1)
            {
               // set font to default text and color
               textProp.textFont = defaultTextProp.textFont;
               textProp.textColor = Color.FromArgb(157, 150, 19);
               textProp.wordIndex = StringManip.GetPhraseWordIndexInText(text, "running");
               textProp.wordCount = 1;
               // set font style
               textProp.textFont = new Font(textProp.textFont.FontFamily, 13, FontStyle.Bold);
               textPropList.Add(textProp);

               // set font to default text and color
               textProp.textFont = defaultTextProp.textFont;
               textProp.textColor = Color.FromArgb(42, 176, 173);
               textProp.wordIndex = StringManip.GetPhraseWordIndexInText(text, "running") + 1;
               textProp.wordCount = StringManip.GetPhraseWordIndexInText(text, "please wait") - textProp.wordIndex;
               // set font style
               textProp.textFont = new Font(textProp.textFont.FontFamily, 13, FontStyle.Bold);
               textPropList.Add(textProp);

               // set font to default text and color
               textProp.textFont = defaultTextProp.textFont;
               textProp.textColor = Color.FromArgb(157, 150, 19);
               textProp.wordIndex = StringManip.GetPhraseWordIndexInText(text, "please wait");
               textProp.wordCount = 10;
               // set font style
               textProp.textFont = new Font(textProp.textFont.FontFamily, 13, FontStyle.Bold);
               textPropList.Add(textProp);
            }
            else
            {
               // set font to default text and color
               textProp.textFont = defaultTextProp.textFont;
               textProp.textColor = Color.FromArgb(17, 75, 237);
               textProp.wordIndex = 0;
               textProp.wordCount = 100;
               // set font style
               textProp.textFont = new Font(textProp.textFont.FontFamily, 13, FontStyle.Bold);
               textPropList.Add(textProp);
            }

            UpdateStatusDisplayAtLine(text + dots, m_progressLine, textPropList);

            Thread.Sleep(500);
         }
      }

      // get all the necessary info if user choose to verify software by looking at the registry
      public void getAppInfo(List<ConfigFileManager.Ini_KeyValue<string>> iniKeys, ref string appDescription, ref string appArg, ref bool isConsoleApp, ref bool waitForExit, ref string cdLabel, ref string displayMessage)
      {
         appDescription = "";
         appArg = "";
         displayMessage = "";
         isConsoleApp = false;
         waitForExit = true;

         // for every key specified for the app to be installed/checked
         foreach (ConfigFileManager.Ini_KeyValue<string> iniKey in iniKeys)
         {
            if (Regex.IsMatch(iniKey.iniKeyName, @"app\d+_description$", RegexOptions.IgnoreCase))
            {
               appDescription = iniKey.iniValue;
            }
            else if (Regex.IsMatch(iniKey.iniKeyName, @"app\d+_argument$", RegexOptions.IgnoreCase))
            {
               appArg = iniKey.iniValue;
            }
            else if (Regex.IsMatch(iniKey.iniKeyName, @"app\d+_cd_label$", RegexOptions.IgnoreCase))
            {
               cdLabel = iniKey.iniValue;
            }
            else if (Regex.IsMatch(iniKey.iniKeyName, @"app\d+_display_message_to_user$", RegexOptions.IgnoreCase))
            {
               displayMessage = iniKey.iniValue;
            }
            else if (Regex.IsMatch(iniKey.iniKeyName, @"app\d+_is_console$", RegexOptions.IgnoreCase))
            {
               if (String.Equals(iniKey.iniValue, "true", StringComparison.OrdinalIgnoreCase))
                  isConsoleApp = true;
            }
            else if (Regex.IsMatch(iniKey.iniKeyName, @"app\d+_wait_for_exit$", RegexOptions.IgnoreCase))
            {
               if (String.Equals(iniKey.iniValue, "true", StringComparison.OrdinalIgnoreCase))
                  waitForExit = true;
               else
                  waitForExit = false;
            }
         }
      }

      bool processConfigInfo(KeyValuePair<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>> entry,
         ref KeyValuePair<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>> processedEntry)
      {
         string fileExt = "";
         bool setupSuccessful = true;
         ConfigFileManager.Ini_KeyValue<string> iniVal = new ConfigFileManager.Ini_KeyValue<string>("");
         ConfigFileManager.Ini_KeyValue<string> iniVal2 = new ConfigFileManager.Ini_KeyValue<string>("");
         string indentation = String.Empty;
         string msg = "", msg2 = "";
         Match regExMatch;
         int matchedArgumentIndex = -1;

         Dictionary<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>> tempOneToManyDict = new Dictionary<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>>();
         List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor> textPropList = new List<CommonLib.Windows.Forms.TextFormat.TextFontAndColor>();

         Dictionary<string, string> idToPath = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

         iniVal = entry.Key;

         bool appTypeSpecified = false;
         string appId = "";

         regExMatch = Regex.Match(entry.Key.iniKeyName, @"(app\d+)_.+$", RegexOptions.IgnoreCase);

         if (regExMatch.Success)
            appId = regExMatch.Groups[1].Value;

         // process the key of the map which contains the setup file path
         if (!Common.pathContainsMacro(entry.Key.iniValue))
         {
            regExMatch = Regex.Match(entry.Key.iniValue, @".*\{browse_to_file:([^\}]+)\}.*", RegexOptions.IgnoreCase);
            if (regExMatch.Success)
            {
               fileExt = Path.GetExtension(regExMatch.Groups[1].Value);
               msg = "Please browse to location where file \"" + regExMatch.Groups[1].Value + "\" is located at";

               regExMatch = Regex.Match(entry.Key.iniValue, @".*\{message_to_user:([^\}]+)\}.*", RegexOptions.IgnoreCase);
               if (regExMatch.Success)
                  msg += "\n" + regExMatch.Groups[1].Value;

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

               m_parentForm.Invoke((MethodInvoker)(() =>
                        {
                           OpenFileDialog openFileDialog1 = new OpenFileDialog();

                           openFileDialog1.InitialDirectory = Application.StartupPath;

                           openFileDialog1.Filter = "File (*" + fileExt + ")|*" + fileExt;
                           openFileDialog1.FilterIndex = 2;
                           openFileDialog1.RestoreDirectory = true;

                           if (openFileDialog1.ShowDialog() == DialogResult.OK)
                           {
                              iniVal.alternateValue = openFileDialog1.FileName;
                           }
                        }));

               if (iniVal.alternateValue.Length == 0)
               {
                  indentation = Common.getIndentation(1);
                  msg = "\n" + indentation + "- FAILED. "
                     + "\n" + indentation + "File: " + ConfigFileManager.ms_configGeneralInfo.pathAndNameOfConfigFile
                     + "\n" + indentation + "Section: " + entry.Key.iniSectionName
                     + "\n" + indentation + "Key: " + entry.Key.iniKeyName
                     + "\n" + indentation + "Value: " + entry.Key.iniValue
                     + "\n" + indentation + "Error Description: User did not provide a valid file";

                  Common.formatConfigIniFailureMessage(msg, textPropList);

                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                  setupSuccessful = false;
               }
            }
            else
            {
               iniVal.alternateValue = Path.GetFullPath(entry.Key.iniValue);

               // check only relative path
               if (!Path.IsPathRooted(entry.Key.iniValue) && (!File.Exists(iniVal.alternateValue)))
               {
                  indentation = Common.getIndentation(1);
                  msg = "\n" + indentation + "- FAILED. "
                     + "\n" + indentation + "File: " + ConfigFileManager.ms_configGeneralInfo.pathAndNameOfConfigFile
                     + "\n" + indentation + "Section: " + entry.Key.iniSectionName
                     + "\n" + indentation + "Key: " + entry.Key.iniKeyName
                     + "\n" + indentation + "Value: " + entry.Key.iniValue
                     + "\n" + indentation + "Error Description: The path defined in the value above is invalid. Translated path " + iniVal.alternateValue;

                  Common.formatConfigIniFailureMessage(msg, textPropList);

                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                  setupSuccessful = false;
               }
            }
         }
         else  // the path specified contains macros, we have to resolve macros
         {
            iniVal.alternateValue = Common.resolveMacroPath(entry.Key.iniValue);

            if (Common.pathContainsCdDriveMacro(entry.Key.iniValue))
            {
               if (iniVal.alternateValue.Length == 0)
               {
                  setupSuccessful = false;
                  msg2 = PathManip.enumWindowsDesignatedPaths.CD_DRIVE.ToString() + " macro was defined. But no CD drive is found in the system";
               }
               //else
               //{
               //   setupSuccessful = Common.validatePathInCdDrive(iniVal.alternateValue, m_parentForm);

               //   if ( !setupSuccessful )
               //      msg2 = "\"" + iniVal.alternateValue + "\" does not exist. Please insert a CD with that path";
               //}

               if (!setupSuccessful)
               {
                  indentation = Common.getIndentation(1);
                  msg = "\n" + indentation + "- FAILED. "
                     + "\n" + indentation + "File: " + ConfigFileManager.ms_configGeneralInfo.pathAndNameOfConfigFile
                     + "\n" + indentation + "Section: " + entry.Key.iniSectionName
                     + "\n" + indentation + "Key: " + entry.Key.iniKeyName
                     + "\n" + indentation + "Value: " + entry.Key.iniValue
                     + "\n" + indentation + "Error Description: " + msg2;

                  Common.formatConfigIniFailureMessage(msg, textPropList);

                  UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);
               }
            }
         }

         // process the values of the map which contains various related things like application argument, description, is_console, wait_for_exit, etc
         if (setupSuccessful)
         {
            tempOneToManyDict[iniVal] = new List<ConfigFileManager.Ini_KeyValue<string>>(entry.Value);

            matchedArgumentIndex = tempOneToManyDict[iniVal].FindIndex(x => Regex.IsMatch(x.iniKeyName, @"app\d+_argument$", RegexOptions.IgnoreCase));

            // for every key specified for the app to be run
            foreach (ConfigFileManager.Ini_KeyValue<string> iniKey in entry.Value)
            {
               if (Regex.IsMatch(iniKey.iniKeyName, @"(app\d+)_is_console$", RegexOptions.IgnoreCase))
               {
                  appTypeSpecified = true;
               }
               else if (Regex.IsMatch(iniKey.iniKeyName, @"(app\d+)_argument$", RegexOptions.IgnoreCase))
               {
                  iniVal2 = tempOneToManyDict[iniVal][matchedArgumentIndex];

                  regExMatch = Regex.Match(iniKey.iniValue, @".*\{browse_to_file:([^\}]+)\}.*", RegexOptions.IgnoreCase);
                  if (regExMatch.Success)
                  {
                     fileExt = Path.GetExtension(regExMatch.Groups[1].Value);
                     msg = "Please browse to location where file \"" + regExMatch.Groups[1].Value + "\" is located at";

                     regExMatch = Regex.Match(iniKey.iniValue, @".*\{message_to_user:([^\}]+)\}.*", RegexOptions.IgnoreCase);
                     if (regExMatch.Success)
                        msg += "\n" + regExMatch.Groups[1].Value;

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

                     m_parentForm.Invoke((MethodInvoker)(() =>
                     {
                        OpenFileDialog openFileDialog1 = new OpenFileDialog();

                        openFileDialog1.InitialDirectory = Application.StartupPath;

                        openFileDialog1.Filter = "File (*" + fileExt + ")|*" + fileExt;
                        openFileDialog1.FilterIndex = 2;
                        openFileDialog1.RestoreDirectory = true;

                        if (openFileDialog1.ShowDialog() == DialogResult.OK)
                        {
                           iniVal2.iniValue = openFileDialog1.FileName;
                           tempOneToManyDict[iniVal][matchedArgumentIndex] = iniVal2;
                        }
                        else
                        {
                           indentation = Common.getIndentation(1);
                           msg = "\n" + indentation + "- FAILED. "
                              + "\n" + indentation + "File: " + ConfigFileManager.ms_configGeneralInfo.pathAndNameOfConfigFile
                              + "\n" + indentation + "Section: " + iniVal2.iniSectionName
                              + "\n" + indentation + "Key: " + iniVal2.iniKeyName
                              + "\n" + indentation + "Value: " + iniVal2.iniValue
                              + "\n" + indentation + "Error Description: User did not provide a valid file";

                           Common.formatConfigIniFailureMessage(msg, textPropList);

                           UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                           setupSuccessful = false;
                        }
                     }));
                  }
                  else
                  {
                     // matches every macro defined in the value of they key, macro is in the form of {xxxxx} such as {user_desktop}, {user_temp_folder}, {cd_drive}, {keyname}
                     MatchCollection matches = Regex.Matches(iniKey.iniValue, @"\{([^\{\}]+)\}", RegexOptions.IgnoreCase);

                     if (matches.Count > 0)
                     {
                        // each match can have multiple parts i.e. $1 $2...$n
                        for (int i = 0; i < matches.Count; i++)
                        {
                           // matches[i].Groups[0].Value is the actual macro itself, i.e. {keyname}
                           // matches[i].Groups[1].Value is the string inside curly brackets, i.e. keyname
                           string pattern = Regex.Escape(matches[i].Groups[1].Value);

                           // looking for a key that defines the actual path to be used in the argument
                           int matchedPathIndex = entry.Value.FindIndex(x => Regex.IsMatch(x.iniKeyName, pattern, RegexOptions.IgnoreCase));

                           // if the macro in the argument refers to another key the defines a path
                           if (matchedPathIndex >= 0 && !idToPath.ContainsKey(matches[i].Groups[0].Value))
                           {
                              if (PathManip.PathIsFile(entry.Value[matchedPathIndex].iniValue))
                                 idToPath[matches[i].Groups[0].Value] = Path.GetDirectoryName(Path.GetFullPath(entry.Value[matchedPathIndex].iniValue));
                              else
                                 idToPath[matches[i].Groups[0].Value] = Path.GetFullPath(entry.Value[matchedPathIndex].iniValue);

                           }
                           // if the macro in the argument refers to the path defined by primary key app#_path
                           else if (String.Equals(entry.Key.iniKeyName, matches[i].Groups[1].Value, StringComparison.OrdinalIgnoreCase) && !idToPath.ContainsKey(matches[i].Groups[0].Value))
                           {
                              if (PathManip.PathIsFile(iniVal.alternateValue))
                                 idToPath[matches[i].Groups[0].Value] = Path.GetDirectoryName(Path.GetFullPath(iniVal.alternateValue));
                              else
                                 idToPath[matches[i].Groups[0].Value] = Path.GetFullPath(iniVal.alternateValue);
                           }
                           // if the macro refers to user's desktop, temp folder, cd drive
                           else if (Common.pathContainsMacro(matches[i].Groups[0].Value))
                           {
                              if (!idToPath.ContainsKey(matches[i].Groups[0].Value))
                              {
                                 string resolvedPath = Common.resolveMacroPath(matches[i].Groups[0].Value);

                                 if (Common.pathContainsCdDriveMacro(matches[i].Groups[0].Value))
                                 {
                                    if (resolvedPath.Length == 0)
                                    {
                                       setupSuccessful = false;
                                       msg2 = matches[i].Groups[1].Value + " macro was defined. But no CD drive is found in the system";
                                    }

                                    if (!setupSuccessful)
                                    {
                                       indentation = Common.getIndentation(1);
                                       msg = "\n" + indentation + "- FAILED. "
                                          + "\n" + indentation + "File: " + ConfigFileManager.ms_configGeneralInfo.pathAndNameOfConfigFile
                                          + "\n" + indentation + "Section: " + iniKey.iniSectionName
                                          + "\n" + indentation + "Key: " + iniKey.iniKeyName
                                          + "\n" + indentation + "Value: " + iniKey.iniValue
                                          + "\n" + indentation + "Error Description: " + msg2;

                                       Common.formatConfigIniFailureMessage(msg, textPropList);

                                       UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                                       setupSuccessful = false;

                                       break;
                                    }
                                 }

                                 if (setupSuccessful)
                                    idToPath[matches[i].Groups[0].Value] = resolvedPath;
                              }
                           }
                           else
                           {
                              indentation = Common.getIndentation(1);
                              msg = "\n" + indentation + "- FAILED. "
                                 + "\n" + indentation + "File: " + ConfigFileManager.ms_configGeneralInfo.pathAndNameOfConfigFile
                                 + "\n" + indentation + "Section: " + iniKey.iniSectionName
                                 + "\n" + indentation + "Key: " + iniKey.iniKeyName
                                 + "\n" + indentation + "Value: " + iniKey.iniValue
                                 + "\n" + indentation + "Error Description: The value above specifies a variable \"" + matches[i].Groups[0].Value + "\" in the argument."
                                                         + " Key \"" + iniKey.iniKeyName + "_" + matches[i].Groups[1].Value
                                                         + "\" must be defined to specify the actual path for variable \"" + matches[i].Groups[0].Value + "\"";

                              Common.formatConfigIniFailureMessage(msg, textPropList);

                              UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

                              setupSuccessful = false;

                              break;
                           }
                        }
                     }
                  }

                  if (!setupSuccessful)
                     break;
               }
            }
         }

         if (setupSuccessful && !appTypeSpecified)
         {
            indentation = Common.getIndentation(1);
            msg = "\n" + indentation + "- FAILED. "
               + "\n" + indentation + "File: " + ConfigFileManager.ms_configGeneralInfo.pathAndNameOfConfigFile
               + "\n" + indentation + "Section: " + entry.Key.iniSectionName
               + "\n" + indentation + "Error Description: The key name \"" + appId + "_is_console\" is missing in the above section in the INI file.";

            Common.formatConfigIniFailureMessage(msg, textPropList);

            UpdateStatusDisplayAndLogEvent(msg, msg, textPropList);

            setupSuccessful = false;

         }

         if (setupSuccessful)
         {
            if (idToPath.Count > 0)
            {
               // goes through each marco in the argument ini key and replace with actual paths
               foreach (KeyValuePair<string, string> item in idToPath)
               {
                  if (matchedArgumentIndex >= 0)
                  {
                     iniVal2 = tempOneToManyDict[iniVal][matchedArgumentIndex];
                     iniVal2.iniValue = Regex.Replace(iniVal2.iniValue, Regex.Escape(item.Key), item.Value, RegexOptions.IgnoreCase);

                     tempOneToManyDict[iniVal][matchedArgumentIndex] = iniVal2;
                  }
               }
            }

            processedEntry = new KeyValuePair<ConfigFileManager.Ini_KeyValue<string>, List<ConfigFileManager.Ini_KeyValue<string>>>(iniVal, new List<ConfigFileManager.Ini_KeyValue<string>>(tempOneToManyDict[iniVal]));
         }

         return setupSuccessful;
      }
   }
}
