using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using CommonLib.IO;
using CommonLib.Windows.Forms;
using CommonLib.Windows.Misc;

namespace All_Purpose_Auto_Setup
{
   public partial class frmMain : Form
   {
      frmSetupStatusDisplay dlgBuildSoftwarePackage;

      private bool restartApplicationRequested = false;

      private bool rebootRequested = false;

      string pathAndNameOfConfigFile = "";

      public frmMain()
      {
         InitializeComponent();

         Version appVersion = Assembly.GetExecutingAssembly().GetName().Version;

         DateTime buildDateTime = File.GetCreationTime(Assembly.GetExecutingAssembly().Location);

         this.Text = "Automated Windows Setup - " + buildDateTime.ToString("MM/dd/yyyy") + " - Version " + appVersion.Major.ToString() + "." + appVersion.Minor.ToString();
      }

      private void btnStart_Click(object sender, EventArgs e)
      {
         bool iniFileValid = false;
         string msg = string.Empty;

         while (!iniFileValid)
         {
            iniFileValid = true;
            if (pathAndNameOfConfigFile == null || pathAndNameOfConfigFile.Length == 0)
            {
               msg = "INI file is not provided";
               iniFileValid = false;
            }
            else if (!String.Equals(Path.GetExtension(pathAndNameOfConfigFile), ".ini", StringComparison.OrdinalIgnoreCase))
            {
               msg = "File " + pathAndNameOfConfigFile + " is not an INI file";
               iniFileValid = false;
            }
            else if (!File.Exists(pathAndNameOfConfigFile))
            {
               iniFileValid = false;
               msg = "INI File " + pathAndNameOfConfigFile + " doesn't exist";
            }

            msg += "\nPlease select a valid INI configuration file to continue...";

            if (!iniFileValid)
            {
               List<MessageBoxCustom.clsCustomButton> buttons = new List<MessageBoxCustom.clsCustomButton>()
                  { new MessageBoxCustom.clsCustomButton("Select File...", "0"),
                    new MessageBoxCustom.clsCustomButton("Cancel...", "1"),
                  };

               string result = MessageBoxCustom.Show(this, MessageBoxCustom.PopUpMsgType.CUSTOMQUESTION, buttons, msg, "ERROR!!!", -1);

               if (result == "0")
               {
                  OpenFileDialog openFileDialog1 = new OpenFileDialog();
                  openFileDialog1.InitialDirectory = Application.StartupPath;
                  openFileDialog1.Filter = "INI File (*.ini)|*.ini";
                  openFileDialog1.FilterIndex = 2;
                  openFileDialog1.RestoreDirectory = true;

                  if (openFileDialog1.ShowDialog() == DialogResult.OK)
                  {
                     pathAndNameOfConfigFile = openFileDialog1.FileName;
                     txtBoxConfigFile.Text = pathAndNameOfConfigFile;
                     processConfigFile();
                  }

               }
               else
                  break;
            }
         }

         if (iniFileValid)
         {
            bool proceed = runApplicationUsingAdminAccount();

            if (restartApplicationRequested)
            {
               this.Close();
            }
            else if (proceed)
            {
               // we only ask for login account if we're starting a new setup process
               if ((int)Properties.Settings.Default["CurrentSetupStep"] < 1)
               {
                  provideWindowsPasswordForAutoLogin();
               }

               dlgBuildSoftwarePackage = new frmSetupStatusDisplay(this);
               dlgBuildSoftwarePackage.FormClosing += getReasonForSetupFormClosing;
               dlgBuildSoftwarePackage.parentForm = this;
               dlgBuildSoftwarePackage.ShowDialog();

               if (restartApplicationRequested)
               {
                  this.Close();
               }

               if (rebootRequested)
               {
                  ProcessStartInfo proc = new ProcessStartInfo();
                  proc.WindowStyle = ProcessWindowStyle.Hidden;
                  proc.FileName = "cmd";
                  proc.Arguments = "/C shutdown -r -t 1";
                  Process.Start(proc);

                  this.Close();
               }
            }
         }
      }

      public string getConfigFile()
      {
         return pathAndNameOfConfigFile;
      }

      private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
      {
         frmAbout dlgAbout = new frmAbout();
         dlgAbout.parentForm = this;
         DialogResult result = dlgAbout.ShowDialog();
      }

      void getReasonForSetupFormClosing(Object sender, FormClosingEventArgs e)
      {
         if (dlgBuildSoftwarePackage.restartApplicationRequested)
            restartApplicationRequested = true;

         if (dlgBuildSoftwarePackage.rebootRequested)
            rebootRequested = true;
      }

      private void frmMain_Shown(object sender, EventArgs e)
      {
         string[] args = Environment.GetCommandLineArgs();

         if (args.Count() > 1)
         {
            pathAndNameOfConfigFile = args[1];
            if (!PathManip.IsAbsolutePath(pathAndNameOfConfigFile))
               pathAndNameOfConfigFile = Path.GetFullPath(Path.Combine(Application.StartupPath, pathAndNameOfConfigFile));
         }
         else if (Properties.Settings.Default["ConfigFile"].ToString().Length > 0)
         {
            pathAndNameOfConfigFile = Properties.Settings.Default["ConfigFile"].ToString();
         }
         else if (pathAndNameOfConfigFile.Length == 0)
         {
            pathAndNameOfConfigFile = Path.Combine(Application.StartupPath, "config.ini");
         }
         txtBoxConfigFile.Text = pathAndNameOfConfigFile;

         processConfigFile();

         if ((bool)Properties.Settings.Default["AutoRunOnStartup"])
         {
            Properties.Settings.Default["AutoRunOnStartup"] = false;

            Common.deleteTaskSchedulerForAutoRunAfterReboot("Auto_Win_Setup_Task");

            btnStart.PerformClick();
         }
      }

      private void processConfigFile()
      {
         if (pathAndNameOfConfigFile.Length > 0 && File.Exists(pathAndNameOfConfigFile))
         {
            IniFileManip iniFile = new IniFileManip(pathAndNameOfConfigFile);
            string sectionName = ConfigFileManager.enum_INI_SECTION_NAMES.General.ToString();
            string program = iniFile.ReadValue(sectionName, "program_name");

            if (program.Length > 0)
            {
               this.label1.Text = program;
            }
         }
      }

      private bool runApplicationUsingAdminAccount()
      {
         bool proceed = true;
         string msg = "";
         if (pathAndNameOfConfigFile.Length > 0 && File.Exists(pathAndNameOfConfigFile))
         {
            IniFileManip iniFile = new IniFileManip(pathAndNameOfConfigFile);
            string sectionName = ConfigFileManager.enum_INI_SECTION_NAMES.Global_Settings.ToString();
            string provideAdminAccount = iniFile.ReadValue(sectionName, "Always_Run_Application_Using_Admin_Account");

            if (String.Equals(provideAdminAccount, "true", StringComparison.OrdinalIgnoreCase)
                  ||
                  String.Equals(provideAdminAccount, "yes", StringComparison.OrdinalIgnoreCase))
            {
               // if application is not run using an admin account
               if (!WindowsAdministration.IsApplicationRunAsAdmin())
               {
                  msg = "This software application must be run using an Administrator account.\n\nClick OK to run this application using an Administrator account.";

                  MessageBoxCustom.Show(this, MessageBoxCustom.PopUpMsgType.INFO, null, msg, "INFO", -1);

                  ProcessStartInfo proc = new ProcessStartInfo();
                  proc.UseShellExecute = true;
                  proc.WorkingDirectory = Environment.CurrentDirectory;
                  proc.FileName = Application.ExecutablePath;
                  proc.Arguments = pathAndNameOfConfigFile;
                  proc.Verb = "runas";
                  try
                  {
                     this.Hide();
                     Process.Start(proc);
                     restartApplicationRequested = true;
                  }
                  catch
                  {
                     this.Show();
                     proceed = false;
                     // The user refused the elevation.
                     // Do nothing and return directly ...
                     //return;
                  }
               }
            }
         }

         return proceed;
      }

      private void provideWindowsPasswordForAutoLogin()
      {
         if (pathAndNameOfConfigFile.Length > 0 && File.Exists(pathAndNameOfConfigFile))
         {
            IniFileManip iniFile = new IniFileManip(pathAndNameOfConfigFile);
            string sectionName = ConfigFileManager.enum_INI_SECTION_NAMES.Global_Settings.ToString();
            string provideAdminAccount = iniFile.ReadValue(sectionName, "Provide_Windows_Password_For_Auto_Login");

            if (String.Equals(provideAdminAccount, "true", StringComparison.OrdinalIgnoreCase)
                  ||
                  String.Equals(provideAdminAccount, "yes", StringComparison.OrdinalIgnoreCase))
            {
               enableWindowsAutoLogin();
            }
         }
      }

      private void enableWindowsAutoLogin()
      {
         string errMsg = "";
         string password = "";
         string password2 = "";
         string registryPath = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon";

         frmUserInput inputDialog = new frmUserInput(this);
         inputDialog.parentForm = this;
         do
         {
            inputDialog.richTxtBoxOverwriteText("There might be one or more reboots during the setup process.\n\n");
            inputDialog.richTxtBoxAppendText("Please provide the password for " + WindowsAdministration.GetWindowLoginID() + " so that the application can auto-login to continue the setup process.\n");
            inputDialog.richTxtBoxAppendText("If there's no password, leave the field blank.");
            inputDialog.ShowDialog();

            password = inputDialog.getUserPassword();

            if (password.Length > 0)
            {
               inputDialog.richTxtBoxOverwriteText("Please re-enter the password.");
               inputDialog.clearUserPassword();
               inputDialog.ShowDialog();
               password2 = inputDialog.getUserPassword();
               inputDialog.clearUserPassword();

               if (password != password2)
               {
                  MessageBoxCustom.Show(this, MessageBoxCustom.PopUpMsgType.ERROR, null, "The passwords you entered do not match. Please re-enter the password.", "ERROR!!!", -1);
               }
            }
         } while (password.Length > 0 && password != password2);

         string username = Regex.Replace(WindowsAdministration.GetWindowLoginID(), @"[^\\]+\\(.+)", "$1");

         WindowsRegistry.AddOrModifyRegistryKeySingleValue(registryPath, Microsoft.Win32.RegistryHive.LocalMachine, "AutoAdminLogon", Microsoft.Win32.RegistryValueKind.String, "1", ref errMsg);

         if (WindowsAdministration.GetDomainName() != System.Environment.MachineName)
         {
            WindowsRegistry.AddOrModifyRegistryKeySingleValue(registryPath, Microsoft.Win32.RegistryHive.LocalMachine, "DefaultDomainName", Microsoft.Win32.RegistryValueKind.String, WindowsAdministration.GetDomainName(), ref errMsg);
         }

         WindowsRegistry.AddOrModifyRegistryKeySingleValue(registryPath, Microsoft.Win32.RegistryHive.LocalMachine, "DefaultUserName", Microsoft.Win32.RegistryValueKind.String, username, ref errMsg);

         if (password.Length > 0)
            WindowsRegistry.AddOrModifyRegistryKeySingleValue(registryPath, Microsoft.Win32.RegistryHive.LocalMachine, "DefaultPassword", Microsoft.Win32.RegistryValueKind.String, password, ref errMsg);
      }

      private void frmMain_Load(object sender, EventArgs e)
      {
         this.AllowDrop = true;
         this.DragEnter += frmMain_DragEnter;
         this.DragDrop += frmMain_DragDrop;
      }

      private void frmMain_DragEnter(object sender, DragEventArgs e)
      {
         if (e.Data.GetDataPresent(DataFormats.FileDrop))
         {
            e.Effect = DragDropEffects.Copy;
         }

         else
         {
            e.Effect = DragDropEffects.None;
         }
      }


      private void frmMain_DragDrop(object sender, DragEventArgs e)
      {

         if (e.Data.GetDataPresent(DataFormats.FileDrop))
         {
            string[] filePaths = (string[])(e.Data.GetData(DataFormats.FileDrop));

            if (filePaths.Length > 1)
            {
               MessageBoxCustom.Show(this, MessageBoxCustom.PopUpMsgType.ERROR, null, "More than one file detected. Expecting only one file of type INI", "ERROR!!!", -1);
            }
            else if (!String.Equals(Path.GetExtension(filePaths[0]), ".ini", StringComparison.OrdinalIgnoreCase))
            {
               MessageBoxCustom.Show(this, MessageBoxCustom.PopUpMsgType.ERROR, null, "Wrong file type detected. Expecting only file of type INI", "ERROR!!!", -1);
            }
            else
            {
               pathAndNameOfConfigFile = filePaths[0];
               processConfigFile();
            }
         }
      }

      private void btnSelectConfigFile_Click(object sender, EventArgs e)
      {
         OpenFileDialog openFileDialog1 = new OpenFileDialog();

         if (pathAndNameOfConfigFile.Length > 0)
         {
            openFileDialog1.InitialDirectory = Path.GetDirectoryName(pathAndNameOfConfigFile);
         }
         else
            openFileDialog1.InitialDirectory = Application.StartupPath;

         openFileDialog1.Filter = "INI File (*.ini)|*.ini";
         openFileDialog1.FilterIndex = 2;
         openFileDialog1.RestoreDirectory = true;

         if (openFileDialog1.ShowDialog() == DialogResult.OK)
         {
            pathAndNameOfConfigFile = openFileDialog1.FileName;
            txtBoxConfigFile.Text = pathAndNameOfConfigFile;

            processConfigFile();
         }
      }

      private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
      {
         Properties.Settings.Default["ConfigFile"] = pathAndNameOfConfigFile;
         Properties.Settings.Default.Save();
      }
   }
}