using MicronSync.Components;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using MicronSync.Components.Forms;
using System.Drawing;

namespace MicronSync
{
    public partial class MainWindow : Form
    {
        internal static UserConfig _userConfig = new UserConfig();
        internal static Settings _Settings = new Settings();
        public static string[] args { get; set; }
        private readonly CommonIO _CommonIO = new CommonIO();
        private readonly ParseUserInput _ParseUserInput = new ParseUserInput();
        private bool isLoading;
        private bool hasCancelledExit;

        public MainWindow() { InitializeComponent(); }

        #region Startup events

        private void Form1_Load(object sender, EventArgs e)
        {
            if (PerformDependencyChecks() > 0)
                Environment.Exit(1);

            _Settings.Load();
            CreateTooltips();
            PopulateRootLists();
            ProcessArgs(args);
            UseDarkTheme(_Settings.UseDarkMode);
            SetWindowLocation(_Settings.RememberWindowLoc,
                new Point (_Settings.WindowXPos, _Settings.WindowYPos));
            //Location = new Point(_Settings.WindowXPos, _Settings.WindowYPos);
            //labelDevBuild.Visible = false; // Comment out before releasing.
        }

        /// <summary>
        /// Process args passed directly to the executable.
        /// </summary>
        /// <param name="args"></param>
        private void ProcessArgs(IReadOnlyCollection<string> args)
        {
            List<string> listArgs = new List<string>();
            if (args.Count > 0)
            {
                foreach (var item in args)
                        listArgs.Add(" " + item);

                string fullPath = String.Join(String.Empty, listArgs.ToArray())
                    .TrimStart()
                    .TrimEnd();

                OpenConfig(false, fullPath, true);
            }

        }

        /// <summary>
        /// Initialize tooltips object and set tooltips for specified UI objects.
        /// </summary>
        private void CreateTooltips()
        {
            toolTip.AutomaticDelay = 8000;
            toolTip.InitialDelay = 1000;
            toolTip.ReshowDelay = 500;
            toolTip.ShowAlways = true;

            toolTip.SetToolTip(labelDescription, "Enter a description of your choice here.");
            toolTip.SetToolTip(labelBackupSource, "Select a directory to backup.");
            toolTip.SetToolTip(labelBackupDest, "Select a location to store the backup file.");
            toolTip.SetToolTip(labelRestoreSource, "Select the backup file to restore from.");
            toolTip.SetToolTip(labelRestoreDest, "Select a directory to restore to.");
            toolTip.SetToolTip(labelCompressionLevel, "Set a compression value below:\n" +
                "Higher levels of compression will require more time for your computer to process,\n" +
                "this will result in a smaller output file.");
            toolTip.SetToolTip(chkPurge, "This will COMPLETELY wipe clean the restore destination directory\n" +
                "prior to restoring from the selected backup.");
            toolTip.SetToolTip(chkBackup, "Enabling this option will create a backup of the restoration directory\n" +
                "with the extension \".Backup\". This backup will not be updated unless the overwrite option is enabled.");
            toolTip.SetToolTip(btnRepBackupSource, "Replicate path to restore destination.");
            toolTip.SetToolTip(btnRepBackupDest, "Replicate path to restore source.");
            toolTip.SetToolTip(btnRepRestoreSource, "Replicate path to backup destination.");
            toolTip.SetToolTip(btnRepRestoreDest, "Replicate path to backup source.");
            toolTip.SetToolTip(labelCompressionPreset, "Selecting a preset to use will automatically choose optimal compression settings.");
            toolTip.SetToolTip(btnExploreBSource, "Browse to specified directory.");
            toolTip.SetToolTip(btnExploreRDestination, "Browse to specified directory.");
            toolTip.SetToolTip(chkOverwriteBackup, "Overwriting your backup will consistently create a previous backup prior to each restore task.");
        }

        private void PopulateRootLists()
        {
            // Clear existing lists.
            cmbRootBSrc.Items.Clear(); 
            cmbRootBDst.Items.Clear();
            cmbRootRSrc.Items.Clear();
            cmbRootRDst.Items.Clear();

            // Add blank header for each of the dropdowns.
            cmbRootBSrc.Items.Add("");
            cmbRootBDst.Items.Add("");
            cmbRootRSrc.Items.Add("");
            cmbRootRDst.Items.Add("");

            string[] drives = Directory.GetLogicalDrives();
            foreach (var drive in drives)
            {
                cmbRootBSrc.Items.Add(drive);
                cmbRootBDst.Items.Add(drive);
                cmbRootRSrc.Items.Add(drive);
                cmbRootRDst.Items.Add(drive);
            }
            foreach (var var in UserConfig.SysVars)
            {
                cmbRootBSrc.Items.Add(var.Key);
                cmbRootBDst.Items.Add(var.Key);
                cmbRootRSrc.Items.Add(var.Key);
                cmbRootRDst.Items.Add(var.Key);
            }
        }

        #endregion

        #region Functions

        /// <summary>
        /// Open an existing configuration file.
        /// </summary>
        /// <param name="fileToLoad"></param>
        /// <param name="useFileSelector"></param>
        private void OpenConfig(bool useFileSelector, string fileToLoad, bool fromExe)
        {
            isLoading = true;

            // Load file selector if a file has not been specified.
            string loadedFile = useFileSelector ? _CommonIO.OpenFile(null, CommonIO.FileType.ini) : fileToLoad;

            if(!string.IsNullOrEmpty(loadedFile))
            if (loadedFile.EndsWith(".mini") || loadedFile.EndsWith(".ini"))
            {
                _userConfig = new UserConfig() { OpenFile = loadedFile };

                if (_userConfig.Load())
                {
                    // Set titlebar name.
                    UpTitlebar = loadedFile;
                    UpStatus = "Load successful";
                    saveToolStripMenuItem.Enabled = true;
                }
                else
                    _userConfig = new UserConfig();

                UpdateWithConfig();
            }
            else
                if (!fromExe)
                    MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_BadConfigFile, null);
                else
                    MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_BadConfigFile_FromEXE, null);
            //
            isLoading = false;
            _userConfig.UserModifiedConfig = false;
        }

        /// <summary>
        /// Syncronises form with data with configuration.
        /// </summary>
        private void UpdateWithConfig()
        {
            UpTitlebar = _userConfig.OpenFile;
            textDescription.Text = _userConfig.Description;
            UpRootBackupSourceUI = _userConfig.RootBackupSource;
            textBackupSource.Text = _userConfig.PathBackupSource;
            UpRootBackupDestinationUI = _userConfig.RootBackupDestination;
            textBackupDest.Text = _userConfig.PathBackupDestination;
            UpRootRestoreSourceUI = _userConfig.RootRestoreSource;
            textRestoreSource.Text = _userConfig.PathRestoreSource;
            UpRootRestoreDestinationUI = _userConfig.RootRestoreDestination;
            textRestoreDest.Text = _userConfig.PathRestoreDestination;
            trackBar.Value = _userConfig.CompressionLevel;
            chkBackup.Checked = _userConfig.EnableBackup;
            chkPurge.Checked = _userConfig.EnablePurge;
            chkOverwriteBackup.Checked = _userConfig.OverwriteBackup;
            UpTabSelection = _userConfig.InBackupMode;
            UpBackupSizeValue = "N/A";
            UpSourceSizeValue = "N/A";
            Width = _userConfig.WindowWidth;
        }

        /// <summary>
        /// Checks 7-Zip is installed on system before attempting to continue running program.
        /// </summary>
        /// <returns>Return error code.</returns>
        private int PerformDependencyChecks()
        {
            int errors = 0;
            bool isSysArch64;

            // Determine system architecture.
            if (IntPtr.Size == 8)
            {
                // x64
                isSysArch64 = true;
                Debug.Write("DEBUG: 64-bit system detected\n");
            }
            else
            {
                // x32
                isSysArch64 = false;
                Debug.Write("DEBUG: 32-bit system detected\n");
            }

            // Check 7-zip is installed on system.
            string sevenZipRegPath = @"SOFTWARE\7-Zip\";
            string sevenZipRegValueName = "Path";

            if (isSysArch64)
            {
                // x64
                var regPath64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
                var regKey64 = regPath64?.OpenSubKey(sevenZipRegPath);
                var value64 = regKey64?.GetValue(sevenZipRegValueName);

                if (File.Exists(value64 + "7z.exe"))
                {
                    LMZAParser.szExePath = value64 + "7z.exe";
                    LMZAParser.szWorkDir = Convert.ToString(value64);
                    Debug.Write("DEBUG: 7-Zip exists\n");
                }
                else
                {
                    DialogResult dr = MessageHandler.stdMessage(MessageHandler.msgCodes.MainWindow_SZNotInstalled, null);
                    errors++;

                    if (dr == DialogResult.Yes)
                        Process.Start("http://www.7-zip.org/");
                }
            }
            else
            {
                // x32
                var regPath32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
                var regKey32 = regPath32?.OpenSubKey(sevenZipRegPath);
                var value32 = regKey32?.GetValue(sevenZipRegValueName);

                if (File.Exists(value32 + "7z.exe"))
                {
                    LMZAParser.szExePath = value32 + "7z.exe";
                    LMZAParser.szWorkDir = Convert.ToString(value32);
                    Debug.Write("DEBUG: 7-Zip exists\n");
                }
                else
                {
                    DialogResult dr = MessageHandler.stdMessage(MessageHandler.msgCodes.MainWindow_SZNotInstalled, null);
                    errors++;

                    if (dr == DialogResult.Yes)
                        Process.Start("http://www.7-zip.org/");
                }
            }

            /*
            // Test local write access
            if (_CommonIO.WriteToDirectory("Write_Access_Test") == CommonIO.endResult.WriteToDirectory_Error)
            {
                MessageHandler.errorMessage(MessageHandler.errCodes.Settings_WriteError, null);
                errors++;
            }
            */

            return errors;
        }

        /// <summary>
        /// Ensures required fields are not empty before enabling backup button.
        /// </summary>
        private void BackupButtonQualifier()
        {
            int count = 0;
            if (!string.IsNullOrEmpty(_userConfig.BackupSource)) count++;
            if (!string.IsNullOrEmpty(_userConfig.BackupDestination)) count++;

            if (count == 2)
            {
                btnBackup.Enabled = true;
                UpStatus = "Ready";
            }
            else
            {
                btnBackup.Enabled = false;
                UpStatus = "Please fill in the required fields";
            }
        }

        /// <summary>
        /// Ensures required fields are not empty before enabling restore button.
        /// </summary>
        private void RestoreButtonQualifier()
        {
            int count = 0;
            if (!string.IsNullOrEmpty(_userConfig.RestoreSource)) count++;
            if (!string.IsNullOrEmpty(_userConfig.RestoreDestination)) count++;

            if (count == 2)
            {
                btnRestore.Enabled = true;
                UpStatus = "Ready";
            }
            else
            {
                btnRestore.Enabled = false;
                UpStatus = "Please fill in the required fields";
            }
        }

        /// <summary>
        /// Reports back status of job from WorkerUI instance.
        /// </summary>
        /// <param name="wui">Instance to pull status from</param>
        private void ReportPostJobStatus(WorkerUI wui)
        {
            // Show Errors:
            switch (wui._endResultLMZA)
            {
                case LMZAParser.endResult.MakePackage_Aborted:
                    statusLabel.Text = "Aborted backup operation";
                    break;
                case LMZAParser.endResult.ExtractPackage_Aborted:
                    statusLabel.Text = "Aborted restore operation";
                    break;
                case LMZAParser.endResult.ExtractPackage_Error:
                    statusLabel.Text = "Restoration failed";
                    MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_RestoreError, null);
                    break;
                case LMZAParser.endResult.MakePackage_Error:
                    MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_BackupError, null);
                    statusLabel.Text = "Backup failed";
                    break;
                case LMZAParser.endResult.Aborted:
                    break;
                case LMZAParser.endResult.Error:
                    break;
                case LMZAParser.endResult.Default:
                    statusLabel.Text = $"Operation successful, completion time: {wui.compTime}";
                    break;
                case LMZAParser.endResult.TestArchive_Aborted:
                    statusLabel.Text = "Aborted test archive operation";
                    break;
                case LMZAParser.endResult.TestArchive_Error:
                    statusLabel.Text = "Backup verification failed";
                    break;
                default:
                    break;
            }

            switch (wui._endResultCIO)
            {
                case CommonIO.endResult.ClearEntireDirectory_Error:
                    statusLabel.Text = "Failed to read/write onto filesystem";
                    break;
                case CommonIO.endResult.CopyEntireDirectory_Error:
                    statusLabel.Text = "Failed to read/write onto filesystem";
                    break;
                case CommonIO.endResult.Default:
                    break;
            }
        }

        /// <summary>
        /// Show save changes dialog on exit.
        /// </summary>
        private void PromptSaveChanges()
        {
            // Reset value before proceeding.
            hasCancelledExit = false;

            // Only prompt if there are unsaved changes.
            if (!_userConfig.UserModifiedConfig) return;
            DialogResult result;
            result = MessageHandler.stdMessage(MessageHandler.msgCodes.MainWindow_SaveChanges, _userConfig.OpenFile);

            // Save to currently open file or to new one if there isn't one loaded.
            if (result == DialogResult.Yes)
            {
                if (!string.IsNullOrEmpty(_userConfig.OpenFile))
                { _userConfig.Save(); }
                else
                {
                    _userConfig.OpenFile =
                      _CommonIO.SaveFile(null, CommonIO.FileType.ini);
                }

                _userConfig.Save();
            }
            else if (result == DialogResult.Cancel)
                hasCancelledExit = true;
        }

        private void OpenWithExplorer(string path)
        {
            if (Directory.Exists(path))
            {
                ProcessStartInfo psi = new ProcessStartInfo("explorer.exe", path);
                Process.Start(psi);
            }
            else if (path == string.Empty || path == null)
            {
                MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_EmptyExplorePath, null);
            }
            else
            {
                MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_BadExplorePath, path);
            }
        }

        private void UseDarkTheme(bool darkTheme)
        {
            var darkColour = Color.FromArgb(64, 64, 64);
            var lightColour = Color.White;
            var darkText = Color.Black;

            if (darkTheme)
            {
                enableDarkThemeToolStripMenuItem.CheckState = CheckState.Checked;
                //Settings.Default.UseDarkTheme = true;
                _Settings.UseDarkMode = true;

                // Backup Tab.
                tabBackup.BackColor = darkColour;
                labelBackupDest.ForeColor = lightColour;
                groupBackupOptions.BackColor = darkColour;
                groupBackupOptions.ForeColor = lightColour;
                trackBar.BackColor = darkColour;

                // Restore Tab.
                tabRestore.BackColor = darkColour;
                labelRestoreDest.ForeColor = lightColour;
                groupRestoreOptions.BackColor = darkColour;
                groupRestoreOptions.ForeColor = lightColour;
            }
            else
            {
                enableDarkThemeToolStripMenuItem.CheckState = CheckState.Unchecked;
                //Settings.Default.UseDarkTheme = false;
                _Settings.UseDarkMode = false;

                // Backup Tab.
                tabBackup.BackColor = lightColour;
                labelBackupDest.ForeColor = darkText;
                groupBackupOptions.BackColor = lightColour;
                groupBackupOptions.ForeColor = darkText;
                trackBar.BackColor = lightColour;

                // Restore Tab.
                tabRestore.BackColor = lightColour;
                labelRestoreDest.ForeColor = darkText;
                groupRestoreOptions.BackColor = lightColour;
                groupRestoreOptions.ForeColor = darkText;
            }
        }

        /// <summary>
        /// Set location of window based on a predefined coordinate otherwise a default
        /// position is used.
        /// </summary>
        /// <param name="enabled"></param>
        /// <param name="loc"></param>
        private void SetWindowLocation(bool enabled, Point loc)
        {
            if (enabled)
            {
                Location = loc;
                rememberWindowPositionToolStripMenuItem.CheckState = CheckState.Checked;
            }
            else
            { Location = new Point(150, 150); }
        }

        #endregion

        #region UI Functionality

        #region UpdateUI

        private string UpStatus
        {
            set
            {
                statusLabel.Text = value;
            }
        }

        private string UpTitlebar
        {
            set
            {
                if (!string.IsNullOrEmpty(value))
                    Text = String.Format("MicronSync - {0}", Path.GetFileName(value));
                else
                    Text = String.Format("MicronSync");
            }
        }

        private string UpSyncDate
        {
            set
            { labelSyncDate.Text = _CommonIO.CalculateDirectoryModifyDate(value); }
        }

        private string UpBackupDate
        {
            set
            { labelBackupDate.Text = _CommonIO.CalculateFileModifyDate(value); }
        }

        private string UpRootBackupSourceUI
        {
            set
            {
                if (!string.IsNullOrEmpty(value))
                    if (!cmbRootBSrc.Items.Contains(value))
                        cmbRootBSrc.Items.Add(value);
                cmbRootBSrc.SelectedItem = value;
            }
        }

        private string UpRootBackupDestinationUI
        {
            set
            {
                if (!string.IsNullOrEmpty(value))
                    if (!cmbRootBDst.Items.Contains(value))
                        cmbRootBDst.Items.Add(value);
                cmbRootBDst.SelectedItem = value;
            }
        }

        private string UpRootRestoreSourceUI
        {
            set
            {
                if (!string.IsNullOrEmpty(value))
                    if (!cmbRootRSrc.Items.Contains(value))
                        cmbRootRSrc.Items.Add(value);
                cmbRootRSrc.SelectedItem = value;
            }
        }

        private string UpRootRestoreDestinationUI
        {
            set
            {
                if (!string.IsNullOrEmpty(value))
                    if (!cmbRootRDst.Items.Contains(value))
                        cmbRootRDst.Items.Add(value);
                cmbRootRDst.SelectedItem = value;
            }
        }

        private bool UpTabSelection
        {
            set
            {
                if (value == true)
                    tabControl.SelectedTab = tabBackup;
                else
                    tabControl.SelectedTab = tabRestore;
            }
        }

        private int UpCompressionLevelText
        {
            set
            {
                textCompressionLevel.Text = Convert.ToString(value);

                if (value == 0)
                {
                    labelCompressionLevel.Text = "Compression Level: None";
                    cmbComPreset.SelectedItem = "Images/Video";
                    textCompressionLevel.BackColor = Color.RoyalBlue;
                }
                else if (value <= 3)
                {
                    labelCompressionLevel.Text = "Compression Level: Low";
                    cmbComPreset.SelectedItem = "Software/Games";
                    textCompressionLevel.BackColor = Color.Turquoise;
                }
                else if (value <= 5)
                {
                    labelCompressionLevel.Text = "Compression Level: Medium";
                    cmbComPreset.SelectedItem = "Mixed Content";
                    textCompressionLevel.BackColor = Color.Gold;
                }
                else if (value <= 7)
                {
                    labelCompressionLevel.Text = "Compression Level: High";
                    textCompressionLevel.BackColor = Color.Orange;
                }
                else if (value == 9)
                {
                    labelCompressionLevel.Text = "Compression Level: Maximum";
                    cmbComPreset.SelectedItem = "Databases/Text";
                    textCompressionLevel.BackColor = Color.Crimson;
                }
            }
        }

        private string UpBackupSizeValue
        {
            set { labelBackupSizeValue.Text = value; }
        }

        private string UpSourceSizeValue
        {
            set { labelSourceSizeValue.Text = value; }
        }

        #endregion

        #region Update Paths

        private void SetBackupSource(string fullPath)
        {
            if (!string.IsNullOrEmpty(fullPath))
            {
                string newRoot = _CommonIO.GetRootPath(fullPath);
                if (!string.IsNullOrEmpty(newRoot))
                {
                    _userConfig.RootBackupSource = newRoot;
                    _userConfig.PathBackupSource = fullPath.Replace(_CommonIO.ConvertVariableToPath(
                        _userConfig.RootBackupSource), String.Empty);
                }

                UpdateWithConfig();
            }
        }

        private void SetBackupDestination(string fullPath)
        {
            if (!string.IsNullOrEmpty(fullPath))
            {
                string newRoot = _CommonIO.GetRootPath(fullPath);
                if (!string.IsNullOrEmpty(newRoot))
                {
                    _userConfig.RootBackupDestination = newRoot;
                    _userConfig.PathBackupDestination = fullPath.Replace(_CommonIO.ConvertVariableToPath(
                        _userConfig.RootBackupDestination), String.Empty);
                }

                UpdateWithConfig();
            }
        }

        private void SetRestoreSource(string fullPath)
        {
            if (!string.IsNullOrEmpty(fullPath))
            {
                string newRoot = _CommonIO.GetRootPath(fullPath);
                if (!string.IsNullOrEmpty(newRoot))
                {
                    _userConfig.RootRestoreSource = newRoot;
                    _userConfig.PathRestoreSource = fullPath.Replace(_CommonIO.ConvertVariableToPath(
                        _userConfig.RootRestoreSource), String.Empty);
                }

                UpdateWithConfig();
            }
        }

        private void SetRestoreDestination(string fullPath)
        {
            if (!string.IsNullOrEmpty(fullPath))
            {
                string newRoot = _CommonIO.GetRootPath(fullPath);
                if (!string.IsNullOrEmpty(newRoot))
                {
                    _userConfig.RootRestoreDestination = newRoot;
                    _userConfig.PathRestoreDestination = fullPath.Replace(_CommonIO.ConvertVariableToPath(
                        _userConfig.RootRestoreDestination), String.Empty);
                }

                UpdateWithConfig();
            }
        }

        #endregion

        #region Form

        private void rememberWindowPositionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (rememberWindowPositionToolStripMenuItem.CheckState == CheckState.Checked)
                _Settings.RememberWindowLoc = true;
            else
                _Settings.RememberWindowLoc = false;
        }

        private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tabControl.SelectedTab == tabBackup)
                _userConfig.InBackupMode = true;
            else
                _userConfig.InBackupMode = false;
        }

        private void textDescription_TextChanged(object sender, EventArgs e)
        {
            _userConfig.Description = textDescription.Text;
        }

        private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Update window location before saving.
            _Settings.WindowYPos = Location.Y;
            _Settings.WindowXPos = Location.X;

            _Settings.Save();
            PromptSaveChanges();

            if (!hasCancelledExit)
                e.Cancel = false;
            else
                e.Cancel = true;

            hasCancelledExit = false;
        }

        private void textBackupSource_TextChanged(object sender, EventArgs e)
        {
            _userConfig.PathBackupSource = textBackupSource.Text;
            UpSyncDate = _userConfig.BackupSource;

            BackupButtonQualifier();
        }

        private void textBackupDest_TextChanged(object sender, EventArgs e)
        {
            _userConfig.PathBackupDestination = textBackupDest.Text;

            BackupButtonQualifier();
        }

        private void textRestoreSource_TextChanged(object sender, EventArgs e)
        {
            _userConfig.PathRestoreSource = textRestoreSource.Text;
            UpBackupDate = _userConfig.RestoreSource;

            RestoreButtonQualifier();
        }

        private void textRestoreDest_TextChanged(object sender, EventArgs e)
        {
            _userConfig.PathRestoreDestination = textRestoreDest.Text;

            RestoreButtonQualifier();
        }

        private void textRestoreDest_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (textRestoreDest.Text.EndsWith("\\"))
                textRestoreDest.Text =
                    textRestoreDest.Text.Remove(textRestoreDest.Text.Length - 1);
        }

        private void textBackupDest_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            string t = textBackupDest.Text;

            if (!t.EndsWith(".7z") && !t.EndsWith("\\"))
                if (t.Length > 4)
                    textBackupDest.Text += ".7z";
        }

        private void textRestoreSource_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            string t = textRestoreSource.Text;

            if (!t.EndsWith(".7z") && !t.EndsWith("\\"))
                if(t.Length > 4)
                textRestoreSource.Text += ".7z";
        }

        private void btnBackupBrowseSource_Click(object sender, EventArgs e)
        {
            string fullPath = _CommonIO.BrowseFolder(
                textBackupSource.Text);

            SetBackupSource(fullPath);
        }

        private void btnBackupBrowseDest_Click(object sender, EventArgs e)
        {
            string fullPath = _CommonIO.SaveFile(
                textBackupDest.Text, CommonIO.FileType.sz);

            SetBackupDestination(fullPath);
        }

        private void btnRestoreSource_Click(object sender, EventArgs e)
        {
            string fullPath = _CommonIO.OpenFile(
                textRestoreSource.Text, CommonIO.FileType.sz);

            SetRestoreSource(fullPath);
        }

        private void btnRestoreDest_Click(object sender, EventArgs e)
        {
            string fullPath = _CommonIO.BrowseFolder(
                textRestoreDest.Text);

            SetRestoreDestination(fullPath);
        }

        private void checkMirrorSrcToDst_CheckedChanged(object sender, EventArgs e)
        {
            // Warn user prior to enabling functionality if backup is also disabled.
            if (!isLoading)
            {
                DialogResult _dialogResult = DialogResult.None;

                if (chkPurge.Checked && !chkBackup.Checked)
                    if (!string.IsNullOrEmpty(_userConfig.RestoreDestination))
                        _dialogResult = MessageHandler.stdMessage(MessageHandler.msgCodes.MainWindow_Warn_Purge,
                        _userConfig.RestoreDestination);
                else
                        _dialogResult = MessageHandler.stdMessage(MessageHandler.msgCodes.MainWindow_Warn_Purge_Null,
                        null);


                if (_dialogResult == DialogResult.No)
                    chkPurge.CheckState = CheckState.Unchecked;
            }

            _userConfig.EnablePurge = chkPurge.Checked;
        }

        private void checkBackup_CheckedChanged(object sender, EventArgs e)
        {
            _userConfig.EnableBackup = chkBackup.Checked;

            if (chkBackup.CheckState == CheckState.Checked)
                chkOverwriteBackup.Enabled = true;
            else
                chkOverwriteBackup.Enabled = false;
        }

        private void trackBar_ValueChanged(object sender, EventArgs e)
        {
            _userConfig.CompressionLevel = trackBar.Value;
            UpCompressionLevelText = trackBar.Value;
        }

        private void btnRestore_Click(object sender, EventArgs e)
        {
            bool errors =
                _ParseUserInput.IsValidationInvalid(
                _userConfig.RestoreSource,
                _userConfig.RestoreDestination,
                false);

            if (!errors)
            {
                WorkerUI wui = new WorkerUI();
                wui.SetWorkerMode = WorkerUI.WorkerMode.Restore;
                wui.ShowDialog();

                ReportPostJobStatus(wui);
            }
        }

        private void btnBackup_Click(object sender, EventArgs e)
        {
            bool errors =
                _ParseUserInput.IsValidationInvalid(
                    _userConfig.BackupSource,
                    _userConfig.BackupDestination,
                    true);

                if (!errors)
                {
                    WorkerUI wui = new WorkerUI();
                    wui.SetWorkerMode = WorkerUI.WorkerMode.Backup;
                    wui.ShowDialog();

                    ReportPostJobStatus(wui);
                }
        }

        private void cmbRootBSrc_SelectedIndexChanged(object sender, EventArgs e)
        {
            _userConfig.RootBackupSource = cmbRootBSrc.SelectedItem.ToString();

            UpSyncDate = _userConfig.BackupSource;
        }

        private void cmbRootBDst_SelectedIndexChanged(object sender, EventArgs e)
        {
            _userConfig.RootBackupDestination = cmbRootBDst.SelectedItem.ToString();
        }

        private void cmbRootRSrc_SelectedIndexChanged(object sender, EventArgs e)
        {
            _userConfig.RootRestoreSource = cmbRootRSrc.SelectedItem.ToString();

            UpBackupDate = _userConfig.RestoreSource;
        }

        private void cmbRootRDst_SelectedIndexChanged(object sender, EventArgs e)
        {
            _userConfig.RootRestoreDestination = cmbRootRDst.SelectedItem.ToString();
        }

        private void cmbComPreset_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cmbComPreset.SelectedItem.ToString() == "Software/Games")
                trackBar.Value = 2;
            else if (cmbComPreset.SelectedItem.ToString() == "Images/Video")
                trackBar.Value = 0;
            else if (cmbComPreset.SelectedItem.ToString() == "Databases/Text")
                trackBar.Value = 9;
            else if (cmbComPreset.SelectedItem.ToString() == "Mixed Content")
                trackBar.Value = 4;
        }

        private void btnCalcBackupSize_Click(object sender, EventArgs e)
        {
            if (File.Exists(_userConfig.RestoreSource))
                UpBackupSizeValue = $"{_CommonIO.CalculateFileSizeMB(_userConfig.RestoreSource)}MB";
            else
                UpBackupSizeValue = "N/A";
        }

        private void btnSourceSize_Click(object sender, EventArgs e)
        {
            if (Directory.Exists(_userConfig.BackupSource))
            {
                DirSizeCalculatorPrompt dirCalc = new DirSizeCalculatorPrompt();
                dirCalc.TargetDirectory = _userConfig.BackupSource;
                dirCalc.ShowDialog();

                UpSourceSizeValue = $"{dirCalc.Result}MB";

            }
            else
                UpSourceSizeValue = "N/A";
        }

        private void btnExploreBSource_Click(object sender, EventArgs e)
        {
            OpenWithExplorer(_userConfig.BackupSource);
        }

        private void btnExploreRDestination_Click(object sender, EventArgs e)
        {
            OpenWithExplorer(_userConfig.RestoreDestination);
        }

        private void chkOverwriteBackup_CheckedChanged(object sender, EventArgs e)
        {
            _userConfig.OverwriteBackup = chkOverwriteBackup.Checked;
        }

        private void MainWindow_SizeChanged(object sender, EventArgs e)
        {
            _userConfig.WindowWidth = Width;
        }

        #region Drag and drop

        private void MainWindow_DragEnter(object sender, DragEventArgs e)
        {
            BringToFront();

            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Link;
        }

        private void MainWindow_DragDrop(object sender, DragEventArgs e)
        {
            string[] allDropData = (string[])e.Data.GetData(DataFormats.FileDrop);
            DropUI dropUi = new DropUI();
            DropUI.dropData = allDropData[0];
            dropUi.ShowDialog();

            switch (dropUi.DropResult)
            {
                case DropUI.DropAction.SzUseRestore:
                    SetRestoreSource(DropUI.dropData);
                    tabControl.SelectedTab = tabRestore;
                    break;
                case DropUI.DropAction.SzUseBackup:
                    SetBackupDestination(DropUI.dropData);
                    tabControl.SelectedTab = tabBackup;
                    break;
                case DropUI.DropAction.DirUseBackup:
                    SetBackupSource(DropUI.dropData);
                    tabControl.SelectedTab = tabBackup;
                    break;
                case DropUI.DropAction.DirUseRestore:
                    SetRestoreDestination(DropUI.dropData);
                    tabControl.SelectedTab = tabRestore;
                    break;
               case DropUI.DropAction.LoadConfig:
                    OpenConfig(false, DropUI.dropData, false);
                    break;
                case DropUI.DropAction.UnsupportedData:
                    break;
                case DropUI.DropAction.Cancelled:
                    break;
            }
        }

        #endregion

        #region Path Replication

        private void btnRepBackupSource_Click(object sender, EventArgs e)
        {
            SetRestoreDestination(_userConfig.BackupSource);
            tabControl.SelectedTab = tabRestore;
        }

        private void btnRepBackupDest_Click(object sender, EventArgs e)
        {
            SetRestoreSource(_userConfig.BackupDestination);
            tabControl.SelectedTab = tabRestore;
        }

        private void btnRepRestoreSource_Click(object sender, EventArgs e)
        {
            SetBackupDestination(_userConfig.RestoreSource);
            tabControl.SelectedTab = tabBackup;
        }

        private void btnRepRestoreDest_Click(object sender, EventArgs e)
        {
            SetBackupSource(_userConfig.RestoreDestination);
            tabControl.SelectedTab = tabBackup;
        }

        #endregion

        #endregion

        #region Menubar

        private void enableDarkThemeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (enableDarkThemeToolStripMenuItem.CheckState == CheckState.Checked)
                UseDarkTheme(true);
            else
                UseDarkTheme(false);
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_userConfig.UserModifiedConfig)
                PromptSaveChanges();
            if (!hasCancelledExit)
            {

                _userConfig = new UserConfig();
                UpdateWithConfig();

                UpStatus = "New job";
                saveToolStripMenuItem.Enabled = false;
                _userConfig.UserModifiedConfig = false;
            }
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_userConfig.UserModifiedConfig)
                PromptSaveChanges();
            if (!hasCancelledExit)
                OpenConfig(true, null, false);
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(_userConfig.OpenFile))
                _userConfig.Save();

            UpStatus = "Save successful";
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string saveFile = _CommonIO.SaveFile(null, CommonIO.FileType.ini);

            if (!string.IsNullOrEmpty(saveFile))
            {
                _userConfig.OpenFile = saveFile;
                _userConfig.Save();
                UpStatus = "Save successful";
            }

            UpTitlebar = saveFile;
            saveToolStripMenuItem.Enabled = true;
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox about = new AboutBox();
            about.ShowDialog();
        }

        private void changelogToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ChangeLog cl = new ChangeLog();
            cl.ShowDialog();
        }

        private void refreshDrivesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Retrieve user modified config status before altering values.
            bool _userModifiedConfig = _userConfig.UserModifiedConfig;

            string RootBSource = (cmbRootBSrc.SelectedItem != null) ? cmbRootBSrc.SelectedItem.ToString() : null;
            string RootBDestination = (cmbRootBDst.SelectedItem != null) ? cmbRootBDst.SelectedItem.ToString() : null;
            string RootRSource = (cmbRootRSrc.SelectedItem != null) ? cmbRootRSrc.SelectedItem.ToString() : null;
            string RootRDestination = (cmbRootRDst.SelectedItem != null) ? cmbRootRDst.SelectedItem.ToString() : null;

            PopulateRootLists();

            // Match selected items with loaded values.
            UpRootBackupSourceUI = RootBSource;
            UpRootBackupDestinationUI = RootBDestination;
            UpRootRestoreSourceUI = RootRSource;
            UpRootRestoreDestinationUI = RootRDestination;

            // Unflag save prompt if no changes have been made to config.
            if (!_userModifiedConfig)
                _userConfig.UserModifiedConfig = false;
        }

        #endregion

        #endregion
    }

    public class ParseUserInput
    {
        #region Private values
        private readonly List<string> restrictedDirs = new List<string>
        {
            @"C:\Windows",
            @"C:\Program",
            @"C:\$Recycle",
            @"C:\System Volume Information"
        };

        //private readonly int minPathLength = 4;

        private bool IsDirectoryAllowed(string input)
        {
            int errors = 0;
            foreach (var dir in restrictedDirs)
                if (input.StartsWith(dir))
                    errors++;

            return errors == 0;
        }

        private bool IsDstWithinSrcPath(string path, string file)
        {
            var fileRoot = Path.GetDirectoryName(file);
            return fileRoot.StartsWith(path);
        }

        #endregion

        #region Validation

        public bool IsValidationInvalid(string src, string dst, bool isBackupMode)
        {
            int errors = TestGeneralUserInput(src, dst);

            if (isBackupMode)
                errors += TestBackupUserInput(src, dst);
            else
                errors += TestRestoreUserInput(src);

            // Prompt user to replace backup if one already exists.
            if (errors == 0)
            {
                if (!isBackupMode) return errors != 0;
                if (!File.Exists(dst)) return errors != 0;
                DialogResult _dialogResult = MessageHandler.stdMessage(
                    MessageHandler.msgCodes.MainWindow_Warn_OverwriteFile, dst);

                if (_dialogResult == DialogResult.No)
                    errors++;
                else
                    File.Delete(dst);
            }

            return errors != 0;
        }

        private int TestGeneralUserInput(string src, string dst)
        {
            int errors = 0;

            if (!IsDirectoryAllowed(src))
            {
                MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_RestrictedPath, src);
                errors++;
            }
            else if (!IsDirectoryAllowed(dst))
            {
                MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_RestrictedPath, dst);
                errors++;
            }
            
            /* No longer checking for length/
            else if (src.Length < minPathLength || dst.Length < minPathLength)
            {
                MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_PathRootInvalid, null);
                errors++;
            }
            */

            return errors;
        }

        private int TestRestoreUserInput(string src)
        {
            int errors = 0;

            if (!File.Exists(src))
            {
                MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_ArchiveNotFound, null);
                errors++;
            }

            return errors;
        }

        private int TestBackupUserInput(string src, string dst)
        {
            int errors = 0;
            if (IsDstWithinSrcPath(src, dst))
            {
                MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_DstWithinSrc, null);
                errors++;
            }
            else if (!Directory.Exists(src))
            {
                MessageHandler.errorMessage(MessageHandler.errCodes.MainWindow_DirectoryNotFound, null);
                errors++;
            }

            return errors;
        }

        #endregion
    }
}
