using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;

using Volpe.Cafe.EspressoUI.Utils;
using Volpe.Cafe.IO;
using Volpe.Cafe.IO.InputParsers;
using Volpe.Cafe.Logging;
using Volpe.Cafe.MarketData.Design;
using Volpe.Cafe.ModelSettings;
using Volpe.Cafe.Reporting;
using Volpe.Cafe.Utils;

namespace Volpe.Cafe.EspressoUI.LegacyControls.Panels
{
    /// <summary>
    /// Provides a panel for configuring input configuration settings for the compliance model.
    /// </summary>
	public class IOSettingsPanel : CafeUserControl
	{

        #region /*** Events ***/

        /// <summary>Occurs when the <see cref="Parsing"/> property has changed.</summary>
        public event EventHandler ParsingChanged;

        #endregion

        #region /*** Constructors ***/

        /// <summary>
        /// Initializes a new instance of the <see cref="IOSettingsPanel"/> class.
        /// </summary>
		public IOSettingsPanel()
		{
			this.InitializeComponent();
            this.PostInitialize();
        }

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			base.Dispose(disposing);
		}

        #endregion

        #region /*** Methods ***/

		#region /* Designer generated code */

		void InitializeComponent()
		{
            this.ux_outputPathBrowse = new System.Windows.Forms.Button();
            this.ux_outputPath = new System.Windows.Forms.TextBox();
            this.ux_outputPathLabel = new System.Windows.Forms.Label();
            this.ux_compressModelOutputs = new System.Windows.Forms.CheckBox();
            this.ux_reportingGroupBox = new System.Windows.Forms.GroupBox();
            this.ux_splitLargeReports = new System.Windows.Forms.CheckBox();
            this.ux_minimalReporting = new System.Windows.Forms.RadioButton();
            this.ux_defaultReporting = new System.Windows.Forms.RadioButton();
            this.ux_diagnosticReporting = new System.Windows.Forms.RadioButton();
            this.ux_loggingGroupBox = new System.Windows.Forms.GroupBox();
            this.ux_debugLogging = new System.Windows.Forms.RadioButton();
            this.ux_defaultLogging = new System.Windows.Forms.RadioButton();
            this.ux_minimalLogging = new System.Windows.Forms.RadioButton();
            this.ux_marketDataFileLabel = new System.Windows.Forms.Label();
            this.ux_scenariosFileLabel = new System.Windows.Forms.Label();
            this.ux_parametersFileLabel = new System.Windows.Forms.Label();
            this.ux_technologiesFileLabel = new System.Windows.Forms.Label();
            this.ux_technologiesFileOutdatedLabel = new System.Windows.Forms.Label();
            this.ux_parametersFileOutdatedLabel = new System.Windows.Forms.Label();
            this.ux_scenariosFileOutdatedLabel = new System.Windows.Forms.Label();
            this.ux_marketDataFile = new System.Windows.Forms.TextBox();
            this.ux_marketDataFileBrowse = new System.Windows.Forms.Button();
            this.ux_technologiesFile = new System.Windows.Forms.TextBox();
            this.ux_technologiesFileBrowse = new System.Windows.Forms.Button();
            this.ux_inputFilesLabel = new System.Windows.Forms.Label();
            this.ux_parametersFile = new System.Windows.Forms.TextBox();
            this.ux_parametersFileBrowse = new System.Windows.Forms.Button();
            this.ux_scenariosFile = new System.Windows.Forms.TextBox();
            this.ux_scenariosFileBrowse = new System.Windows.Forms.Button();
            this.ux_reloadAllFromDisk = new System.Windows.Forms.CheckBox();
            this.ux_inputFilesPanel = new System.Windows.Forms.FlowLayoutPanel();
            this.ux_marketDataFileOutdatedLabel = new System.Windows.Forms.Label();
            this.ux_outputSettingsPanel = new System.Windows.Forms.FlowLayoutPanel();
            this.ux_reportingGroupBox.SuspendLayout();
            this.ux_loggingGroupBox.SuspendLayout();
            this.ux_inputFilesPanel.SuspendLayout();
            this.ux_outputSettingsPanel.SuspendLayout();
            this.SuspendLayout();
            // 
            // ux_outputPathBrowse
            // 
            this.ux_outputPathBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_outputPathBrowse.AutoSize = true;
            this.ux_outputPathBrowse.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
            this.ux_outputPathBrowse.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_outputPathBrowse.Location = new System.Drawing.Point(537, 20);
            this.ux_outputPathBrowse.Margin = new System.Windows.Forms.Padding(0, 0, 8, 0);
            this.ux_outputPathBrowse.Name = "ux_outputPathBrowse";
            this.ux_outputPathBrowse.Padding = new System.Windows.Forms.Padding(8, 0, 8, 0);
            this.ux_outputPathBrowse.Size = new System.Drawing.Size(78, 26);
            this.ux_outputPathBrowse.TabIndex = 3;
            this.ux_outputPathBrowse.Text = "Browse";
            // 
            // ux_outputPath
            // 
            this.ux_outputPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_outputPath.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.ux_outputPath.Location = new System.Drawing.Point(0, 21);
            this.ux_outputPath.Margin = new System.Windows.Forms.Padding(0, 1, 1, 4);
            this.ux_outputPath.Name = "ux_outputPath";
            this.ux_outputPath.Size = new System.Drawing.Size(536, 22);
            this.ux_outputPath.TabIndex = 2;
            // 
            // ux_outputPathLabel
            // 
            this.ux_outputPathLabel.AutoSize = true;
            this.ux_outputSettingsPanel.SetFlowBreak(this.ux_outputPathLabel, true);
            this.ux_outputPathLabel.Location = new System.Drawing.Point(0, 0);
            this.ux_outputPathLabel.Margin = new System.Windows.Forms.Padding(0, 0, 0, 4);
            this.ux_outputPathLabel.Name = "ux_outputPathLabel";
            this.ux_outputPathLabel.Size = new System.Drawing.Size(380, 16);
            this.ux_outputPathLabel.TabIndex = 1;
            this.ux_outputPathLabel.Text = "Please specify where you would like to save the model outputs:";
            // 
            // ux_compressModelOutputs
            // 
            this.ux_compressModelOutputs.AutoSize = true;
            this.ux_compressModelOutputs.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_outputSettingsPanel.SetFlowBreak(this.ux_compressModelOutputs, true);
            this.ux_compressModelOutputs.Location = new System.Drawing.Point(0, 127);
            this.ux_compressModelOutputs.Margin = new System.Windows.Forms.Padding(0);
            this.ux_compressModelOutputs.Name = "ux_compressModelOutputs";
            this.ux_compressModelOutputs.Size = new System.Drawing.Size(173, 20);
            this.ux_compressModelOutputs.TabIndex = 4;
            this.ux_compressModelOutputs.Text = "Compress model outputs";
            // 
            // ux_reportingGroupBox
            // 
            this.ux_reportingGroupBox.Controls.Add(this.ux_splitLargeReports);
            this.ux_reportingGroupBox.Controls.Add(this.ux_minimalReporting);
            this.ux_reportingGroupBox.Controls.Add(this.ux_defaultReporting);
            this.ux_reportingGroupBox.Controls.Add(this.ux_diagnosticReporting);
            this.ux_outputSettingsPanel.SetFlowBreak(this.ux_reportingGroupBox, true);
            this.ux_reportingGroupBox.Location = new System.Drawing.Point(313, 50);
            this.ux_reportingGroupBox.Name = "ux_reportingGroupBox";
            this.ux_reportingGroupBox.Size = new System.Drawing.Size(304, 74);
            this.ux_reportingGroupBox.TabIndex = 15;
            this.ux_reportingGroupBox.TabStop = false;
            this.ux_reportingGroupBox.Text = "Please specify the level of reporting:";
            // 
            // ux_splitLargeReports
            // 
            this.ux_splitLargeReports.AutoSize = true;
            this.ux_splitLargeReports.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_splitLargeReports.Location = new System.Drawing.Point(8, 48);
            this.ux_splitLargeReports.Name = "ux_splitLargeReports";
            this.ux_splitLargeReports.Size = new System.Drawing.Size(202, 20);
            this.ux_splitLargeReports.TabIndex = 9;
            this.ux_splitLargeReports.Text = "Split large reports by scenario";
            this.ux_splitLargeReports.UseVisualStyleBackColor = true;
            // 
            // ux_minimalReporting
            // 
            this.ux_minimalReporting.AutoSize = true;
            this.ux_minimalReporting.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_minimalReporting.Location = new System.Drawing.Point(8, 24);
            this.ux_minimalReporting.Name = "ux_minimalReporting";
            this.ux_minimalReporting.Size = new System.Drawing.Size(70, 20);
            this.ux_minimalReporting.TabIndex = 1;
            this.ux_minimalReporting.Text = "Minimal";
            this.ux_minimalReporting.UseVisualStyleBackColor = true;
            // 
            // ux_defaultReporting
            // 
            this.ux_defaultReporting.AutoSize = true;
            this.ux_defaultReporting.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_defaultReporting.Location = new System.Drawing.Point(94, 24);
            this.ux_defaultReporting.Name = "ux_defaultReporting";
            this.ux_defaultReporting.Size = new System.Drawing.Size(66, 20);
            this.ux_defaultReporting.TabIndex = 2;
            this.ux_defaultReporting.Text = "Default";
            this.ux_defaultReporting.UseVisualStyleBackColor = true;
            // 
            // ux_diagnosticReporting
            // 
            this.ux_diagnosticReporting.AutoSize = true;
            this.ux_diagnosticReporting.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_diagnosticReporting.Location = new System.Drawing.Point(176, 24);
            this.ux_diagnosticReporting.Name = "ux_diagnosticReporting";
            this.ux_diagnosticReporting.Size = new System.Drawing.Size(88, 20);
            this.ux_diagnosticReporting.TabIndex = 3;
            this.ux_diagnosticReporting.Text = "Diagnostic";
            this.ux_diagnosticReporting.UseVisualStyleBackColor = true;
            // 
            // ux_loggingGroupBox
            // 
            this.ux_loggingGroupBox.Controls.Add(this.ux_debugLogging);
            this.ux_loggingGroupBox.Controls.Add(this.ux_defaultLogging);
            this.ux_loggingGroupBox.Controls.Add(this.ux_minimalLogging);
            this.ux_loggingGroupBox.Location = new System.Drawing.Point(3, 50);
            this.ux_loggingGroupBox.Name = "ux_loggingGroupBox";
            this.ux_loggingGroupBox.Size = new System.Drawing.Size(304, 56);
            this.ux_loggingGroupBox.TabIndex = 14;
            this.ux_loggingGroupBox.TabStop = false;
            this.ux_loggingGroupBox.Text = "Please specify the level of compliance logging:";
            // 
            // ux_debugLogging
            // 
            this.ux_debugLogging.AutoSize = true;
            this.ux_debugLogging.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_debugLogging.Location = new System.Drawing.Point(176, 24);
            this.ux_debugLogging.Margin = new System.Windows.Forms.Padding(4);
            this.ux_debugLogging.Name = "ux_debugLogging";
            this.ux_debugLogging.Size = new System.Drawing.Size(65, 20);
            this.ux_debugLogging.TabIndex = 3;
            this.ux_debugLogging.Text = "Debug";
            // 
            // ux_defaultLogging
            // 
            this.ux_defaultLogging.AutoSize = true;
            this.ux_defaultLogging.Cursor = System.Windows.Forms.Cursors.Default;
            this.ux_defaultLogging.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_defaultLogging.Location = new System.Drawing.Point(94, 24);
            this.ux_defaultLogging.Margin = new System.Windows.Forms.Padding(4);
            this.ux_defaultLogging.Name = "ux_defaultLogging";
            this.ux_defaultLogging.Size = new System.Drawing.Size(66, 20);
            this.ux_defaultLogging.TabIndex = 2;
            this.ux_defaultLogging.Text = "Default";
            // 
            // ux_minimalLogging
            // 
            this.ux_minimalLogging.AutoSize = true;
            this.ux_minimalLogging.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_minimalLogging.Location = new System.Drawing.Point(8, 24);
            this.ux_minimalLogging.Margin = new System.Windows.Forms.Padding(4);
            this.ux_minimalLogging.Name = "ux_minimalLogging";
            this.ux_minimalLogging.Size = new System.Drawing.Size(70, 20);
            this.ux_minimalLogging.TabIndex = 1;
            this.ux_minimalLogging.Text = "Minimal";
            // 
            // ux_marketDataFileLabel
            // 
            this.ux_marketDataFileLabel.AutoSize = true;
            this.ux_marketDataFileLabel.Location = new System.Drawing.Point(8, 20);
            this.ux_marketDataFileLabel.Margin = new System.Windows.Forms.Padding(8, 0, 12, 0);
            this.ux_marketDataFileLabel.Name = "ux_marketDataFileLabel";
            this.ux_marketDataFileLabel.Size = new System.Drawing.Size(104, 16);
            this.ux_marketDataFileLabel.TabIndex = 1;
            this.ux_marketDataFileLabel.Text = "Market-Data file:";
            // 
            // ux_scenariosFileLabel
            // 
            this.ux_scenariosFileLabel.AutoSize = true;
            this.ux_scenariosFileLabel.Location = new System.Drawing.Point(8, 149);
            this.ux_scenariosFileLabel.Margin = new System.Windows.Forms.Padding(8, 0, 25, 0);
            this.ux_scenariosFileLabel.Name = "ux_scenariosFileLabel";
            this.ux_scenariosFileLabel.Size = new System.Drawing.Size(91, 16);
            this.ux_scenariosFileLabel.TabIndex = 13;
            this.ux_scenariosFileLabel.Text = "Scenarios file:";
            // 
            // ux_parametersFileLabel
            // 
            this.ux_parametersFileLabel.AutoSize = true;
            this.ux_parametersFileLabel.Location = new System.Drawing.Point(8, 106);
            this.ux_parametersFileLabel.Margin = new System.Windows.Forms.Padding(8, 0, 16, 0);
            this.ux_parametersFileLabel.Name = "ux_parametersFileLabel";
            this.ux_parametersFileLabel.Size = new System.Drawing.Size(100, 16);
            this.ux_parametersFileLabel.TabIndex = 9;
            this.ux_parametersFileLabel.Text = "Parameters file:";
            // 
            // ux_technologiesFileLabel
            // 
            this.ux_technologiesFileLabel.AutoSize = true;
            this.ux_technologiesFileLabel.Location = new System.Drawing.Point(8, 63);
            this.ux_technologiesFileLabel.Margin = new System.Windows.Forms.Padding(8, 0, 3, 0);
            this.ux_technologiesFileLabel.Name = "ux_technologiesFileLabel";
            this.ux_technologiesFileLabel.Size = new System.Drawing.Size(113, 16);
            this.ux_technologiesFileLabel.TabIndex = 5;
            this.ux_technologiesFileLabel.Text = "Technologies file:";
            // 
            // ux_technologiesFileOutdatedLabel
            // 
            this.ux_technologiesFileOutdatedLabel.AutoEllipsis = true;
            this.ux_technologiesFileOutdatedLabel.AutoSize = true;
            this.ux_inputFilesPanel.SetFlowBreak(this.ux_technologiesFileOutdatedLabel, true);
            this.ux_technologiesFileOutdatedLabel.ForeColor = System.Drawing.Color.Gray;
            this.ux_technologiesFileOutdatedLabel.Location = new System.Drawing.Point(132, 63);
            this.ux_technologiesFileOutdatedLabel.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0);
            this.ux_technologiesFileOutdatedLabel.Name = "ux_technologiesFileOutdatedLabel";
            this.ux_technologiesFileOutdatedLabel.Size = new System.Drawing.Size(283, 16);
            this.ux_technologiesFileOutdatedLabel.TabIndex = 6;
            this.ux_technologiesFileOutdatedLabel.Text = "(The file on disk is newer than data in memory.)";
            this.ux_technologiesFileOutdatedLabel.Visible = false;
            // 
            // ux_parametersFileOutdatedLabel
            // 
            this.ux_parametersFileOutdatedLabel.AutoEllipsis = true;
            this.ux_parametersFileOutdatedLabel.AutoSize = true;
            this.ux_inputFilesPanel.SetFlowBreak(this.ux_parametersFileOutdatedLabel, true);
            this.ux_parametersFileOutdatedLabel.ForeColor = System.Drawing.Color.Gray;
            this.ux_parametersFileOutdatedLabel.Location = new System.Drawing.Point(132, 106);
            this.ux_parametersFileOutdatedLabel.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0);
            this.ux_parametersFileOutdatedLabel.Name = "ux_parametersFileOutdatedLabel";
            this.ux_parametersFileOutdatedLabel.Size = new System.Drawing.Size(283, 16);
            this.ux_parametersFileOutdatedLabel.TabIndex = 10;
            this.ux_parametersFileOutdatedLabel.Text = "(The file on disk is newer than data in memory.)";
            this.ux_parametersFileOutdatedLabel.Visible = false;
            // 
            // ux_scenariosFileOutdatedLabel
            // 
            this.ux_scenariosFileOutdatedLabel.AutoEllipsis = true;
            this.ux_scenariosFileOutdatedLabel.AutoSize = true;
            this.ux_inputFilesPanel.SetFlowBreak(this.ux_scenariosFileOutdatedLabel, true);
            this.ux_scenariosFileOutdatedLabel.ForeColor = System.Drawing.Color.Gray;
            this.ux_scenariosFileOutdatedLabel.Location = new System.Drawing.Point(132, 149);
            this.ux_scenariosFileOutdatedLabel.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0);
            this.ux_scenariosFileOutdatedLabel.Name = "ux_scenariosFileOutdatedLabel";
            this.ux_scenariosFileOutdatedLabel.Size = new System.Drawing.Size(283, 16);
            this.ux_scenariosFileOutdatedLabel.TabIndex = 14;
            this.ux_scenariosFileOutdatedLabel.Text = "(The file on disk is newer than data in memory.)";
            this.ux_scenariosFileOutdatedLabel.Visible = false;
            // 
            // ux_marketDataFile
            // 
            this.ux_marketDataFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_marketDataFile.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.ux_marketDataFile.Location = new System.Drawing.Point(8, 37);
            this.ux_marketDataFile.Margin = new System.Windows.Forms.Padding(8, 1, 1, 4);
            this.ux_marketDataFile.Name = "ux_marketDataFile";
            this.ux_marketDataFile.Size = new System.Drawing.Size(528, 22);
            this.ux_marketDataFile.TabIndex = 3;
            // 
            // ux_marketDataFileBrowse
            // 
            this.ux_marketDataFileBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_marketDataFileBrowse.AutoSize = true;
            this.ux_marketDataFileBrowse.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
            this.ux_marketDataFileBrowse.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_marketDataFileBrowse.Location = new System.Drawing.Point(537, 36);
            this.ux_marketDataFileBrowse.Margin = new System.Windows.Forms.Padding(0, 0, 8, 0);
            this.ux_marketDataFileBrowse.Name = "ux_marketDataFileBrowse";
            this.ux_marketDataFileBrowse.Padding = new System.Windows.Forms.Padding(8, 0, 8, 0);
            this.ux_marketDataFileBrowse.Size = new System.Drawing.Size(78, 26);
            this.ux_marketDataFileBrowse.TabIndex = 4;
            this.ux_marketDataFileBrowse.Text = "Browse";
            // 
            // ux_technologiesFile
            // 
            this.ux_technologiesFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_technologiesFile.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.ux_technologiesFile.Location = new System.Drawing.Point(8, 80);
            this.ux_technologiesFile.Margin = new System.Windows.Forms.Padding(8, 1, 1, 4);
            this.ux_technologiesFile.Name = "ux_technologiesFile";
            this.ux_technologiesFile.Size = new System.Drawing.Size(528, 22);
            this.ux_technologiesFile.TabIndex = 7;
            // 
            // ux_technologiesFileBrowse
            // 
            this.ux_technologiesFileBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_technologiesFileBrowse.AutoSize = true;
            this.ux_technologiesFileBrowse.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
            this.ux_technologiesFileBrowse.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_technologiesFileBrowse.Location = new System.Drawing.Point(537, 79);
            this.ux_technologiesFileBrowse.Margin = new System.Windows.Forms.Padding(0, 0, 8, 0);
            this.ux_technologiesFileBrowse.Name = "ux_technologiesFileBrowse";
            this.ux_technologiesFileBrowse.Padding = new System.Windows.Forms.Padding(8, 0, 8, 0);
            this.ux_technologiesFileBrowse.Size = new System.Drawing.Size(78, 26);
            this.ux_technologiesFileBrowse.TabIndex = 8;
            this.ux_technologiesFileBrowse.Text = "Browse";
            // 
            // ux_inputFilesLabel
            // 
            this.ux_inputFilesLabel.AutoSize = true;
            this.ux_inputFilesPanel.SetFlowBreak(this.ux_inputFilesLabel, true);
            this.ux_inputFilesLabel.Location = new System.Drawing.Point(0, 0);
            this.ux_inputFilesLabel.Margin = new System.Windows.Forms.Padding(0, 0, 0, 4);
            this.ux_inputFilesLabel.Name = "ux_inputFilesLabel";
            this.ux_inputFilesLabel.Size = new System.Drawing.Size(391, 16);
            this.ux_inputFilesLabel.TabIndex = 0;
            this.ux_inputFilesLabel.Text = "Please specify the input files you would like to use with the model:";
            // 
            // ux_parametersFile
            // 
            this.ux_parametersFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_parametersFile.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.ux_parametersFile.Location = new System.Drawing.Point(8, 123);
            this.ux_parametersFile.Margin = new System.Windows.Forms.Padding(8, 1, 1, 4);
            this.ux_parametersFile.Name = "ux_parametersFile";
            this.ux_parametersFile.Size = new System.Drawing.Size(528, 22);
            this.ux_parametersFile.TabIndex = 11;
            // 
            // ux_parametersFileBrowse
            // 
            this.ux_parametersFileBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_parametersFileBrowse.AutoSize = true;
            this.ux_parametersFileBrowse.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
            this.ux_parametersFileBrowse.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_parametersFileBrowse.Location = new System.Drawing.Point(537, 122);
            this.ux_parametersFileBrowse.Margin = new System.Windows.Forms.Padding(0, 0, 8, 0);
            this.ux_parametersFileBrowse.Name = "ux_parametersFileBrowse";
            this.ux_parametersFileBrowse.Padding = new System.Windows.Forms.Padding(8, 0, 8, 0);
            this.ux_parametersFileBrowse.Size = new System.Drawing.Size(78, 26);
            this.ux_parametersFileBrowse.TabIndex = 12;
            this.ux_parametersFileBrowse.Text = "Browse";
            // 
            // ux_scenariosFile
            // 
            this.ux_scenariosFile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_scenariosFile.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.ux_scenariosFile.Location = new System.Drawing.Point(8, 166);
            this.ux_scenariosFile.Margin = new System.Windows.Forms.Padding(8, 1, 1, 4);
            this.ux_scenariosFile.Name = "ux_scenariosFile";
            this.ux_scenariosFile.Size = new System.Drawing.Size(528, 22);
            this.ux_scenariosFile.TabIndex = 15;
            // 
            // ux_scenariosFileBrowse
            // 
            this.ux_scenariosFileBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.ux_scenariosFileBrowse.AutoSize = true;
            this.ux_scenariosFileBrowse.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
            this.ux_scenariosFileBrowse.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_scenariosFileBrowse.Location = new System.Drawing.Point(537, 165);
            this.ux_scenariosFileBrowse.Margin = new System.Windows.Forms.Padding(0, 0, 8, 0);
            this.ux_scenariosFileBrowse.Name = "ux_scenariosFileBrowse";
            this.ux_scenariosFileBrowse.Padding = new System.Windows.Forms.Padding(8, 0, 8, 0);
            this.ux_scenariosFileBrowse.Size = new System.Drawing.Size(78, 26);
            this.ux_scenariosFileBrowse.TabIndex = 16;
            this.ux_scenariosFileBrowse.Text = "Browse";
            // 
            // ux_reloadAllFromDisk
            // 
            this.ux_reloadAllFromDisk.AutoSize = true;
            this.ux_reloadAllFromDisk.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.ux_reloadAllFromDisk.Location = new System.Drawing.Point(8, 194);
            this.ux_reloadAllFromDisk.Margin = new System.Windows.Forms.Padding(8, 2, 0, 2);
            this.ux_reloadAllFromDisk.Name = "ux_reloadAllFromDisk";
            this.ux_reloadAllFromDisk.Size = new System.Drawing.Size(225, 20);
            this.ux_reloadAllFromDisk.TabIndex = 17;
            this.ux_reloadAllFromDisk.Text = "Reload all selected files from disk";
            // 
            // ux_inputFilesPanel
            // 
            this.ux_inputFilesPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
            this.ux_inputFilesPanel.Controls.Add(this.ux_inputFilesLabel);
            this.ux_inputFilesPanel.Controls.Add(this.ux_marketDataFileLabel);
            this.ux_inputFilesPanel.Controls.Add(this.ux_marketDataFileOutdatedLabel);
            this.ux_inputFilesPanel.Controls.Add(this.ux_marketDataFile);
            this.ux_inputFilesPanel.Controls.Add(this.ux_marketDataFileBrowse);
            this.ux_inputFilesPanel.Controls.Add(this.ux_technologiesFileLabel);
            this.ux_inputFilesPanel.Controls.Add(this.ux_technologiesFileOutdatedLabel);
            this.ux_inputFilesPanel.Controls.Add(this.ux_technologiesFile);
            this.ux_inputFilesPanel.Controls.Add(this.ux_technologiesFileBrowse);
            this.ux_inputFilesPanel.Controls.Add(this.ux_parametersFileLabel);
            this.ux_inputFilesPanel.Controls.Add(this.ux_parametersFileOutdatedLabel);
            this.ux_inputFilesPanel.Controls.Add(this.ux_parametersFile);
            this.ux_inputFilesPanel.Controls.Add(this.ux_parametersFileBrowse);
            this.ux_inputFilesPanel.Controls.Add(this.ux_scenariosFileLabel);
            this.ux_inputFilesPanel.Controls.Add(this.ux_scenariosFileOutdatedLabel);
            this.ux_inputFilesPanel.Controls.Add(this.ux_scenariosFile);
            this.ux_inputFilesPanel.Controls.Add(this.ux_scenariosFileBrowse);
            this.ux_inputFilesPanel.Controls.Add(this.ux_reloadAllFromDisk);
            this.ux_inputFilesPanel.Location = new System.Drawing.Point(8, 8);
            this.ux_inputFilesPanel.Margin = new System.Windows.Forms.Padding(4);
            this.ux_inputFilesPanel.Name = "ux_inputFilesPanel";
            this.ux_inputFilesPanel.Size = new System.Drawing.Size(624, 216);
            this.ux_inputFilesPanel.TabIndex = 0;
            // 
            // ux_marketDataFileOutdatedLabel
            // 
            this.ux_marketDataFileOutdatedLabel.AutoEllipsis = true;
            this.ux_marketDataFileOutdatedLabel.AutoSize = true;
            this.ux_inputFilesPanel.SetFlowBreak(this.ux_marketDataFileOutdatedLabel, true);
            this.ux_marketDataFileOutdatedLabel.ForeColor = System.Drawing.Color.Gray;
            this.ux_marketDataFileOutdatedLabel.Location = new System.Drawing.Point(132, 20);
            this.ux_marketDataFileOutdatedLabel.Margin = new System.Windows.Forms.Padding(8, 0, 0, 0);
            this.ux_marketDataFileOutdatedLabel.Name = "ux_marketDataFileOutdatedLabel";
            this.ux_marketDataFileOutdatedLabel.Size = new System.Drawing.Size(283, 16);
            this.ux_marketDataFileOutdatedLabel.TabIndex = 2;
            this.ux_marketDataFileOutdatedLabel.Text = "(The file on disk is newer than data in memory.)";
            this.ux_marketDataFileOutdatedLabel.Visible = false;
            // 
            // ux_outputSettingsPanel
            // 
            this.ux_outputSettingsPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
            this.ux_outputSettingsPanel.Controls.Add(this.ux_outputPathLabel);
            this.ux_outputSettingsPanel.Controls.Add(this.ux_outputPath);
            this.ux_outputSettingsPanel.Controls.Add(this.ux_outputPathBrowse);
            this.ux_outputSettingsPanel.Controls.Add(this.ux_loggingGroupBox);
            this.ux_outputSettingsPanel.Controls.Add(this.ux_reportingGroupBox);
            this.ux_outputSettingsPanel.Controls.Add(this.ux_compressModelOutputs);
            this.ux_outputSettingsPanel.Location = new System.Drawing.Point(8, 232);
            this.ux_outputSettingsPanel.Name = "ux_outputSettingsPanel";
            this.ux_outputSettingsPanel.Size = new System.Drawing.Size(624, 152);
            this.ux_outputSettingsPanel.TabIndex = 1;
            // 
            // IOSettingsPanel
            // 
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
            this.Controls.Add(this.ux_outputSettingsPanel);
            this.Controls.Add(this.ux_inputFilesPanel);
            this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.Name = "IOSettingsPanel";
            this.Size = new System.Drawing.Size(640, 394);
            this.ux_reportingGroupBox.ResumeLayout(false);
            this.ux_reportingGroupBox.PerformLayout();
            this.ux_loggingGroupBox.ResumeLayout(false);
            this.ux_loggingGroupBox.PerformLayout();
            this.ux_inputFilesPanel.ResumeLayout(false);
            this.ux_inputFilesPanel.PerformLayout();
            this.ux_outputSettingsPanel.ResumeLayout(false);
            this.ux_outputSettingsPanel.PerformLayout();
            this.ResumeLayout(false);

        }

		#endregion

        #region /* Methods for raising events */

        /// <summary>
        /// Raises the <see cref="ParsingChanged"/> event.
        /// </summary>
        /// <param name="e">Event data to pass to the event</param>
        protected virtual void OnParsingChanged(EventArgs e)
        {
            this.ParsingChanged?.Invoke(this, e);
        }

        #endregion

        /// <inheritdoc/>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            this.AdjustLayout();
        }

        /// <inheritdoc/>
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            this.AdjustLayout();
        }

        /// <inheritdoc/>
        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            this.AdjustLayout();
        }

        private void AdjustLayout()
        {
            if (!this.IsHandleCreated || !this.Visible) {
                return;
            }

            this.SuspendLayout();
            this.ux_inputFilesPanel.SetFlowBreak(this.ux_marketDataFileLabel, !this.ux_marketDataFileOutdatedLabel.Visible);
            this.ux_inputFilesPanel.SetFlowBreak(this.ux_technologiesFileLabel, !this.ux_technologiesFileOutdatedLabel.Visible);
            this.ux_inputFilesPanel.SetFlowBreak(this.ux_parametersFileLabel, !this.ux_parametersFileOutdatedLabel.Visible);
            this.ux_inputFilesPanel.SetFlowBreak(this.ux_scenariosFileLabel, !this.ux_scenariosFileOutdatedLabel.Visible);
            SetNewSize(this.ux_inputFilesPanel);
            SetNewSize(this.ux_outputSettingsPanel);
            this.ux_outputSettingsPanel.Top = this.ux_inputFilesPanel.Bottom + 8;
            this.ResumeLayout(true);

            void SetNewSize(Panel panel)
            {
                if (panel == null || panel.IsDisposed) {
                    return;
                }

                int w = this.ClientSize.Width - 16;
                panel.Width = w;

                foreach (Control c in panel.Controls) {
                    if (c is TextBox) {
                        c.Width = w - c.Margin.Horizontal - this.ux_outputPathBrowse.Width - this.ux_outputPathBrowse.Margin.Horizontal;
                    }
                }

                int h = 0;
                foreach (Control c in panel.Controls) {
                    if (c.Visible) {
                        h = Math.Max(h, c.Bottom + c.Margin.Bottom);
                    }
                }

                panel.Height = h;
            }
        }

        void PostInitialize()
        {
            // fix font for systems with custom DPI scalling
            // windows forms scales fonts, but its auto-size feature does not work for fixed pos controls
            this.Font = new Font("Microsoft Sans Serif", 9.75F * GlobalSettings.DPIScale, FontStyle.Regular, GraphicsUnit.Point, 0);

            // add support for dragging and dropping
            this.InitializeInputPanelDragDrop();
            this.InitializeOutputTextBoxDragDrop();

            // initialize check-changes
            this.AutoInitializeCheckChangesDetector(this.Controls);
            this.DeInitializeCheckChangesDetector(this.ux_reloadAllFromDisk);

            // add event handlers for certain controls
            this.ux_marketDataFile        .TextChanged    += new EventHandler(this.InputFile_TextChanged );
            this.ux_technologiesFile      .TextChanged    += new EventHandler(this.InputFile_TextChanged );
            this.ux_parametersFile        .TextChanged    += new EventHandler(this.InputFile_TextChanged );
            this.ux_scenariosFile         .TextChanged    += new EventHandler(this.InputFile_TextChanged );
            this.ux_outputPath            .TextChanged    += new EventHandler(this.OutputPath_TextChanged);
            //
            this.ux_marketDataFile        .EnabledChanged += new EventHandler(this.InputFile_EnabledChanged );
            this.ux_technologiesFile      .EnabledChanged += new EventHandler(this.InputFile_EnabledChanged );
            this.ux_parametersFile        .EnabledChanged += new EventHandler(this.InputFile_EnabledChanged );
            this.ux_scenariosFile         .EnabledChanged += new EventHandler(this.InputFile_EnabledChanged );
            this.ux_outputPath            .EnabledChanged += new EventHandler(this.OutputPath_EnabledChanged);
            //
            this.ux_marketDataFileBrowse  .Click          += new EventHandler(this.InputFileBrowse_Click );
            this.ux_technologiesFileBrowse.Click          += new EventHandler(this.InputFileBrowse_Click );
            this.ux_parametersFileBrowse  .Click          += new EventHandler(this.InputFileBrowse_Click );
            this.ux_scenariosFileBrowse   .Click          += new EventHandler(this.InputFileBrowse_Click );
            this.ux_outputPathBrowse      .Click          += new EventHandler(this.OutputPathBrowse_Click);

            // set initial value for certain controls to trigger events
            this.ux_marketDataFile        .Text = " ";
            this.ux_technologiesFile      .Text = " ";
            this.ux_parametersFile        .Text = " ";
            this.ux_scenariosFile         .Text = " ";
            this.ux_outputPath            .Text = " ";

            // initialize internal "working" arrays and other variables
            this._data = new object[FileCount];
            this._verifiedFiles = new bool[FileCount];
            this._loadFailed = new bool[FileCount];
            this._parsing = 0;
            this._finalizeParsing = false;
            this._saveThread = null;
        }

        void InputFile_TextChanged(object sender, EventArgs e)
        {
            TextBox tb = (TextBox)sender;
            bool isMarketData   = (tb == this.ux_marketDataFile);
            bool isTechnologies = (tb == this.ux_technologiesFile);
            bool isParameters   = (tb == this.ux_parametersFile);
            bool isScenarios    = (tb == this.ux_scenariosFile);
            //
            int fileID =
                (isMarketData  ) ? MarketDataID :
                (isTechnologies) ? TechnologiesID :
                (isParameters  ) ? ParametersID :
                (isScenarios   ) ? ScenariosID : -1;
            Label outdatedLbl = this.GetInputOutdatedLabel(fileID);
            //
            if (fileID == -1 || outdatedLbl == null) { return; }

            string filePath = tb.Text.Trim();
            if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
            {
                bool inputFilePathChanged =
                    (isMarketData   && !this.CompareFilePaths(this._ioSettings.MarketDataFile  , filePath)) ||
                    (isTechnologies && !this.CompareFilePaths(this._ioSettings.TechnologiesFile, filePath)) ||
                    (isParameters   && !this.CompareFilePaths(this._ioSettings.ParametersFile  , filePath)) ||
                    (isScenarios    && !this.CompareFilePaths(this._ioSettings.ScenariosFile   , filePath));
                tb.ForeColor = (this.VisualTracking && inputFilePathChanged) ? this.VisualTrackingColor : DefaultForeColor;
                tb.BackColor = Color.Empty;
                this._messages.RemoveMessage(tb);

                if (inputFilePathChanged)
                {
                    if (this.VerifyFile(fileID))
                    {
                        tb.ForeColor = (this.VisualTracking) ? this.VisualTrackingColor : DefaultForeColor;
                        tb.BackColor = Color.Empty;
                    }
                    outdatedLbl.Visible = false;
                }
            }
            else
            {
                string message = string.IsNullOrEmpty(filePath) 
                    ? $"Please select a {FriendlyTitles[fileID]} file you would like to use with the model."
                    : $"The specified {FriendlyTitles[fileID]} file does not exist on disk.";
                this._messages.AddMessage(message, null, Color.Red, tb);

                tb.BackColor = Color.Honeydew;
                tb.ForeColor = Color.Red;
            }
        }
        void InputFile_EnabledChanged(object sender, EventArgs e)
        {
            if (this._finalizeParsing) { return; }
            TextBox tb = (TextBox)sender;
            if (tb.Enabled && tb.Visible) { this.InputFile_TextChanged(sender, e); }
            else                          { this._messages.RemoveMessage(sender); tb.BackColor = Color.Empty; }
        }
        void InputFileBrowse_Click(object sender, EventArgs e)
        {
            TextBox tb = null;
            int id = -1;
            if      (sender == this.ux_marketDataFileBrowse  ) { id = MarketDataID  ; tb = this.ux_marketDataFile  ; }
            else if (sender == this.ux_technologiesFileBrowse) { id = TechnologiesID; tb = this.ux_technologiesFile; }
            else if (sender == this.ux_parametersFileBrowse  ) { id = ParametersID  ; tb = this.ux_parametersFile  ; }
            else if (sender == this.ux_scenariosFileBrowse   ) { id = ScenariosID   ; tb = this.ux_scenariosFile   ; }
            //
            OpenFileDialog dlg = new OpenFileDialog();
            if (id != -1) { dlg.Title = "Please select the " + FriendlyTitles[id] + " file you would like to use with the model:"; }
            if (File.Exists(tb.Text)) { dlg.FileName = tb.Text; }
            dlg.Multiselect = false;
            dlg.Filter = "All Microsoft Excel Files (*.xl*)|*.xl*";
            if (dlg.ShowDialog() == DialogResult.OK) { tb.Text = dlg.FileName; }
        }

        void OutputPath_TextChanged(object sender, EventArgs e)
        {
            TextBox tb = (TextBox)sender;
            if (Directory.Exists(tb.Text))
            {
                bool outputPathChanged = !this.CompareDirectories(this._ioSettings.BaseOutputPath, tb.Text);
                tb.ForeColor = (this.VisualTracking && outputPathChanged) ? this.VisualTrackingColor : DefaultForeColor;
                tb.BackColor = Color.Empty;
                this._messages.RemoveMessage(tb);

                this.ux_compressModelOutputs.Text =
                    $"Compress model outputs (saved to: ..\\{Path.GetFileName(tb.Text.TrimEnd('\\'))}_zip\\*)";
            }
            else
            {
                string message = string.IsNullOrEmpty(tb.Text.Trim())
                    ? $"Please select a location where you would like to save the model outputs."
                    : $"The specified location for model outputs does not exist on disk.";
                this._messages.AddMessage(message, null, Color.Red, tb);
                tb.BackColor = Color.Honeydew;
                tb.ForeColor = Color.Red;
            }
        }
        void OutputPath_EnabledChanged(object sender, EventArgs e)
        {
            TextBox tb = (TextBox)sender;
            if (tb.Enabled) { this.OutputPath_TextChanged(sender, e); }
            else            { this._messages.RemoveMessage(sender); tb.BackColor = Color.Empty; }
        }
        void OutputPathBrowse_Click(object sender, EventArgs e)
        {
            var dlg = new FolderBrowserDialog
            {
                Description         = "Please select a location where you would like to save the model outputs:",
                SelectedPath        = this.ux_outputPath.Text,
                ShowNewFolderButton = true
            };
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                this.ux_outputPath.Text = dlg.SelectedPath;
            }
        }

        bool CompareFilePaths(string path1, string path2)
        {
            if (File.Exists(path1)) { path1 = Path.GetFullPath(path1); }
            if (File.Exists(path2)) { path2 = Path.GetFullPath(path2); }
            return StringHelper.Equals(path1, path2);
        }
        bool CompareDirectories(string path1, string path2)
        {
            if (Directory.Exists(path1)) { path1 = Path.GetFullPath(path1.TrimEnd('\\')); }
            if (Directory.Exists(path2)) { path2 = Path.GetFullPath(path2.TrimEnd('\\')); }
            return (path1 == path2);
        }

        // ----- support for drag & drop -----
        void InitializeInputPanelDragDrop()
        {
            this.ux_inputFilesPanel.AllowDrop = true;
            this.ux_inputFilesPanel.DragEnter += new DragEventHandler(this.InputFilePanel_DragEnter);
            this.ux_inputFilesPanel.DragDrop  += new DragEventHandler(this.InputFilePanel_DragDrop);
        }
        void InputFilePanel_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
            {
                e.Effect = DragDropEffects.All;
            }
        }
        void InputFilePanel_DragDrop(object sender, DragEventArgs e)
        {
            // get the files and directories from the drag-drop and parse them
            string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            this.ParseFiles(files, false);
        }

        void InitializeOutputTextBoxDragDrop()
        {
            this.ux_outputPath.AllowDrop = true;
            this.ux_outputPath.DragEnter += new DragEventHandler(this.OutputTextBox_DragEnter);
            this.ux_outputPath.DragDrop  += new DragEventHandler(this.OutputTextBox_DragDrop );
        }
        void OutputTextBox_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
            {
                e.Effect = DragDropEffects.All;
            }
        }
        void OutputTextBox_DragDrop(object sender, DragEventArgs e)
        {
            // get the control handling the drop event
            TextBox tb = (TextBox)sender;

            // get the files and directories from the drag-drop and parse them
            string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            for (int i = 0; i < files.Length; i++)
            {
                if (Directory.Exists(files[i])) { tb.Text = files[i]; break; }
            }
        }

        // ----- begin/end parsing methods -----
        void BeginParse()
        {
            this._parsing++;
            if (this._parsing == 1)
            {
                this.ChangeInputState(this, EventArgs.Empty);
                this.OnParsingChanged(EventArgs.Empty);
            }
        }
        void EndParse()
        {
            if (this._parsing > 0)
            {
                this._parsing--;
                if (this._parsing == 0)
                {
                    this._finalizeParsing = true;
                    this.ChangeInputState(this, EventArgs.Empty);
                    this.OnParsingChanged(EventArgs.Empty);
                    this._finalizeParsing = false;
                }
            }
        }
        void ChangeInputState(object sender, EventArgs e)
        {
            if (this.Visible && this.IsHandleCreated)
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(new EventHandler(this.ChangeInputState));
                }
                else
                {
                    bool enabled = (this._parsing == 0);
                    //
                    this.AllowDrop                           =  enabled;
                    //
                    this.ux_inputFilesPanel       .AllowDrop =  enabled;
                    //
                    this.ux_marketDataFile        .ReadOnly  = !enabled;
                    this.ux_technologiesFile      .ReadOnly  = !enabled;
                    this.ux_parametersFile        .ReadOnly  = !enabled;
                    this.ux_scenariosFile         .ReadOnly  = !enabled;
                    //
                    this.ux_marketDataFileBrowse  .Enabled   =  enabled;
                    this.ux_technologiesFileBrowse.Enabled   =  enabled;
                    this.ux_parametersFileBrowse  .Enabled   =  enabled;
                    this.ux_scenariosFileBrowse   .Enabled   =  enabled;
                    //
                    if (enabled)
                    {
                        this.ux_reloadAllFromDisk.Checked = false;
                    }
                } // end if (invoke-required)
            } // end if (visible & handle created)
        }

        // ----- file parsing, verification and loading methods -----
        void ParseFiles(string[] paths, bool keepExisting)
        {
            if (paths == null || paths.Length == 0)
                return;

            // first, split paths into files and directories
            StringCollection scFiles = new StringCollection();
            StringCollection scDirs  = new StringCollection();
            for (int i = 0; i < paths.Length; i++)
            {
                if      (Directory.Exists(paths[i])) { scDirs .Add(paths[i]); }
                else if (File     .Exists(paths[i])) { scFiles.Add(paths[i]); }
            }
            string[] files = new string[scFiles.Count]; scFiles.CopyTo(files, 0);
            string[] dirs  = new string[scDirs .Count]; scDirs .CopyTo(dirs , 0);

            // next, initialize the "found" array
            bool[] found = new bool[FileCount];
            if (keepExisting)
            {
                if (File.Exists(this.ux_marketDataFile  .Text)) { found[MarketDataID  ] = true; }
                if (File.Exists(this.ux_technologiesFile.Text)) { found[TechnologiesID] = true; }
                if (File.Exists(this.ux_parametersFile  .Text)) { found[ParametersID  ] = true; }
                if (File.Exists(this.ux_scenariosFile   .Text)) { found[ScenariosID   ] = true; }
            }

            // next, parse files, then dirs
            // we only want to recurse up to 3 levels -- that should be enough
            this.ParseFilesHelper(files, dirs, found, 3);
        }
        void ParseFilesHelper(string[] files, string[] dirs, bool[] found, int recurseDepth)
        {
            // count up the number of files that have been found already
            int foundCount = 0;
            for (int i = 0; i < found.Length; i++)
            {
                if (found[i]) { foundCount++; }
            }

            // scan any available files first
            for (int i = 0; i < files.Length; i++)
            {
                // check to see if all files have been found already
                if (foundCount == FileCount)
                    return;

                int fileType = this.GetFileType(files[i]);
                for (int j = 0; j < FileCount; j++)
                {
                    if (!found[j] && j == fileType)
                    {
                        found[j] = true;
                        foundCount++;
                        // update file-path in the text-box
                        this.GetInputTextBox(j).Text = files[i];
                        break;
                    }
                }
            }

            // recurse into directories
            for (int i = 0; i < dirs.Length; i++)
            {
                string[] recurseFiles = Directory.GetFiles(dirs[i], "*.xlsx");
                string[] recurseDirs  = (recurseDepth > 0) ? Directory.GetDirectories(dirs[i]) : new string[0];
                this.ParseFilesHelper(recurseFiles, recurseDirs, found, recurseDepth - 1);
            }
        }

        bool VerifyFile(int fileID)
        {
            TextBox tb = this.GetInputTextBox(fileID);
            string fileName = tb.Text.Trim();
            bool fileExists = File.Exists(fileName);
            bool fileExcel = fileExists && (Path.GetExtension(fileName).ToLower() == ".xlsx");
            bool verified = fileExists && fileExcel;

            if (!verified)
            {
                string message = $"The specified {FriendlyTitles[fileID]} file " +
                    $"{(fileExists ? "is not a supported Excel file type." : "does not exist on disk.")}.";
                this._messages.AddMessage(message, null, Color.Red, tb);

                tb.BackColor = Color.Honeydew;
                tb.ForeColor = Color.Red;
            }

            return verified;
        }

        void LoadFiles()
        {
            this.BeginParse();
            //
            this.LoadFile(MarketDataID);
            this.LoadFile(TechnologiesID);
            this.LoadFile(ParametersID);
            this.LoadFile(ScenariosID);
            //
            this.EndParse();
        }
        bool LoadFile(int fileID)
        {
            TextBox tb = this.GetInputTextBox(fileID);
            string oldFile = this.GetInputFile(fileID);
            string newFile = tb.Text.Trim();

            // check if the file needs reloading -- (if not, return false)
            //   if "reload-from-disk" option is selected, always reload
            //   otherwise, reload if current file from input settings is not the same as the one in the textbox
            if (!(this.ux_reloadAllFromDisk.Checked || !this.CompareFilePaths(oldFile, newFile))) { return false; }

            // -------------------------------------------------- \\
            // continue with file loading
            this._messages.AddMessage("Loading " + FriendlyTitles[fileID] + " input file ...", null, Color.Blue, tb);
            Application.DoEvents();
            //
            bool fileLoaded = false;
            string exMessage = string.Empty;
            try
            {
                this._data[fileID] = XlParser.ParseInputFile((InputFileType)fileID, newFile);
                switch (fileID)
                {
                    case MarketDataID  : this._ioSettings.MarketDataFile   = newFile; break;
                    case TechnologiesID: this._ioSettings.TechnologiesFile = newFile; break;
                    case ParametersID  : this._ioSettings.ParametersFile   = newFile; break;
                    case ScenariosID   : this._ioSettings.ScenariosFile    = newFile; break;
                }
                fileLoaded = true;
            }
            catch (Exception ex) { exMessage = ex.Message; fileLoaded = false; }
            //
            this._messages.RemoveMessage(tb);
            Application.DoEvents();
            //
            if (!fileLoaded)
            {
                this._data[fileID] = null;
                this._messages.AddMessage("The selected " + FriendlyTitles[fileID] + " file could not be opened.", exMessage, Color.Red, tb);
                tb.BackColor = Color.Honeydew;
                tb.ForeColor = Color.Red;
            }
            //
            this._loadFailed[fileID] = !fileLoaded;
            return fileLoaded;
        }

        string GetInputFile(int fileID)
        {
            switch (fileID)
            {
                case MarketDataID  : return this._ioSettings.MarketDataFile;
                case TechnologiesID: return this._ioSettings.TechnologiesFile;
                case ParametersID  : return this._ioSettings.ParametersFile;
                case ScenariosID   : return this._ioSettings.ScenariosFile;

                default: throw new ArgumentOutOfRangeException(nameof(fileID), "The specified fileID is not valid.");
            }
        }

        TextBox GetInputTextBox(int fileID)
        {
            switch (fileID)
            {
                case MarketDataID  : return this.ux_marketDataFile;
                case TechnologiesID: return this.ux_technologiesFile;
                case ParametersID  : return this.ux_parametersFile;
                case ScenariosID   : return this.ux_scenariosFile;

                default: return null;
            }
        }
        Label GetInputOutdatedLabel(int fileID)
        {
            switch (fileID)
            {
                case MarketDataID  : return this.ux_marketDataFileOutdatedLabel;
                case TechnologiesID: return this.ux_technologiesFileOutdatedLabel;
                case ParametersID  : return this.ux_parametersFileOutdatedLabel;
                case ScenariosID   : return this.ux_scenariosFileOutdatedLabel;

                default: return null;
            }
        }

        int GetFileType(string filePath)
        {
            int fileType = -1;
            if (File.Exists(filePath))
            {
                string fileName = Path.GetFileName(filePath);
                if (Regex.IsMatch(fileName, @"\Amarket(_|-| |\.)?(data|forecast).*\.xlsx\z", RegexOptions.IgnoreCase))
                {
                    fileType = MarketDataID;
                }
                else if (Regex.IsMatch(fileName, @"\Atechnologies.*\.xlsx\z", RegexOptions.IgnoreCase))
                {
                    fileType = TechnologiesID;
                }
                else if (Regex.IsMatch(fileName, @"\Aparameters.*\.xlsx\z", RegexOptions.IgnoreCase))
                {
                    fileType = ParametersID;
                }
                else if (Regex.IsMatch(fileName, @"\Ascenarios.*\.xlsx\z", RegexOptions.IgnoreCase))
                {
                    fileType = ScenariosID;
                }
            }

            return fileType;
        }

        /// <summary>
        /// Sets up the initial states of the controls for the <see cref="IOSettingsPanel"/> instance based on the specified input and
        /// output configuration settings.
        /// </summary>
        /// <param name="ioSettings">The input settings for the modeling system.</param>
        public void SetInitialState(IOSettings ioSettings)
        {
            this._ioSettings = ioSettings;

            // set initial states
            bool forceReload = false;

            this.SetInitialState_ProcessFile(MarketDataID, ref forceReload);
            this.SetInitialState_ProcessFile(TechnologiesID, ref forceReload);
            this.SetInitialState_ProcessFile(ParametersID, ref forceReload);
            this.SetInitialState_ProcessFile(ScenariosID, ref forceReload);

            // set whether reloading of files is required
            this.ux_reloadAllFromDisk.Checked = forceReload;

            // set output settings
            this.ux_outputPath.Text = ioSettings.BaseOutputPath;
            this.ux_compressModelOutputs.Checked = ioSettings.CompressModelOutputs;

            // set reporting & log levels
            bool isReportingLevelValid = this.SetInitialState_ReportingLevel(ioSettings);
            bool isLogLevelValid = this.SetInitialState_LogLevel(ioSettings);

            // call save in base to reset initial "changes"
            base.SaveChanges();
            //this.PostSave();

            // special case when reporting or log levels are "disabled" due to older sessions
            if (!isReportingLevelValid || !isLogLevelValid) {
                MessageBox.Show(
                    "The current session is using an outdated format that is not compatible with the current " +
                    "version of the CAFE Model. Some of the I/O settings were reset to default values.",
                    "CAFE Model Settings", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            if (!isReportingLevelValid) {
                this.ux_defaultReporting.Checked = true;
            }
            if (!isLogLevelValid) {
                this.ux_defaultLogging.Checked = true;
            }
        }
        bool SetInitialState_ReportingLevel(IOSettings ioSettings)
        {
            var reportingLevel = ioSettings.ReportingLevel;
            var maskedReportingLevel = reportingLevel & ~ReportingLevel.SplitLargeReports;
            if (maskedReportingLevel.IsMinimal()) {
                this.ux_minimalReporting.Checked = true;
            }
            else if (maskedReportingLevel.IsDiagnostic()) {
                this.ux_diagnosticReporting.Checked = true;
            }
            else {
                this.ux_defaultReporting.Checked = true;
            }

            if ((reportingLevel & ReportingLevel.SplitLargeReports) == ReportingLevel.SplitLargeReports) {
                this.ux_splitLargeReports.Checked = true;
            }

            return maskedReportingLevel != ReportingLevel.Disabled;
        }
        bool SetInitialState_LogLevel(IOSettings ioSettings)
        {
            switch (ioSettings.LogLevel) {
                case LogLevel.Minimal:
                    this.ux_minimalLogging.Checked = true;
                    return true;

                case LogLevel.Default:
                    this.ux_defaultLogging.Checked = true;
                    return true;

                case LogLevel.Debug:
                    this.ux_debugLogging.Checked = true;
                    return true;
            }

            return false;
        }
        void SetInitialState_ProcessFile(int fileID, ref bool forceReload)
        {
            string file;
            DateTime timestamp;
            TextBox fileTextBox;
            Label outdatedLbl;

            switch (fileID)
            {
                case MarketDataID:
                    file        = this._ioSettings.MarketDataFile;
                    timestamp   = this._ioSettings.MarketDataTimestamp;
                    fileTextBox = this.ux_marketDataFile;
                    outdatedLbl = this.ux_marketDataFileOutdatedLabel;
                    break;

                case TechnologiesID:
                    file        = this._ioSettings.TechnologiesFile;
                    timestamp   = this._ioSettings.TechnologiesTimestamp;
                    fileTextBox = this.ux_technologiesFile;
                    outdatedLbl = this.ux_technologiesFileOutdatedLabel;
                    break;

                case ParametersID:
                    file        = this._ioSettings.ParametersFile;
                    timestamp   = this._ioSettings.ParametersTimestamp;
                    fileTextBox = this.ux_parametersFile;
                    outdatedLbl = this.ux_parametersFileOutdatedLabel;
                    break;

                case ScenariosID:
                    file        = this._ioSettings.ScenariosFile;
                    timestamp   = this._ioSettings.ScenariosTimestamp;
                    fileTextBox = this.ux_scenariosFile;
                    outdatedLbl = this.ux_scenariosFileOutdatedLabel;
                    break;

                default:
                    return;
            }

            this.SetInitialState_ProcessFile(file, timestamp, fileTextBox, outdatedLbl, ref forceReload);
        }
        void SetInitialState_ProcessFile(string file, DateTime timestamp, TextBox tb, Label outdatedLbl, ref bool forceReload)
        {
            tb.Text = file;
            Application.DoEvents();

            // check timestamps -- if file on disk is newer than the one loaded in memory, generate warning message
            if (File.Exists(file))
            {
                DateTime dt = File.GetLastWriteTime(file);
                if (timestamp < dt)
                {
                    outdatedLbl.Visible = true;
                    forceReload = true;
                }
            }
        }

        /// <summary>
        /// Commits the most recently made changes to the internal <see cref="IOSettingsPanel"/> structure.
        /// </summary>
        public override void SaveChanges()
        {
            if (this.SettingsChanged) {   // some output changed -- continue with save
                this._ioSettings.BaseOutputPath = this.ux_outputPath.Text;
                this._ioSettings.CompressModelOutputs = this.ux_compressModelOutputs.Checked;
                this._ioSettings.ReportingLevel = GetReportingLevelFromSettings();
                this._ioSettings.LogLevel = GetLogLevelFromSettings();

                // load input files in a new thread
                this._saveThread = new Thread(this.SaveChangesThread) {
                    Name = "InputPanelSaveThread"
                };
                this._saveThread.Start();
            }

            ReportingLevel GetReportingLevelFromSettings()
            {
                var reportingLevel =
                      this.ux_minimalReporting.Checked ? ReportingLevel.Minimal
                    : this.ux_diagnosticReporting.Checked ? ReportingLevel.Diagnostic
                    : ReportingLevel.Default;

                reportingLevel |= this.ux_splitLargeReports.Checked 
                    ? ReportingLevel.SplitLargeReports 
                    : ReportingLevel.Disabled;

                return reportingLevel;
            }

            LogLevel GetLogLevelFromSettings()
            {
                return this.ux_minimalLogging.Checked ? LogLevel.Minimal
                    : this.ux_debugLogging.Checked ? LogLevel.Debug 
                    : LogLevel.Default;
            }
        }
        void SaveChangesThread()
        {
            this.LoadFiles();       // load input files
            base.SaveChanges();     // call "save-changes" from base class to reset "visual tracking changes"
            this.PostSave();        // additional processing for cetain text controls
        }
        /// <summary>
        /// Clears visual tracking for certain text controls.
        /// </summary>
        void PostSave()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action(this.PostSave));
            }
            else if (this.VisualTracking)
            {   // clear visual tracking
                if (!this._loadFailed[MarketDataID])
                {
                    this.ux_marketDataFile.ForeColor = Control.DefaultForeColor;
                    this.ux_marketDataFile.BackColor = Color  .Empty;
                }
                if (!this._loadFailed[TechnologiesID])
                {
                    this.ux_technologiesFile.ForeColor = Control.DefaultForeColor;
                    this.ux_technologiesFile.BackColor = Color  .Empty;
                }
                if (!this._loadFailed[ParametersID])
                {
                    this.ux_parametersFile.ForeColor = Control.DefaultForeColor;
                    this.ux_parametersFile.BackColor = Color  .Empty;
                }
                if (!this._loadFailed[ScenariosID])
                {
                    this.ux_scenariosFile.ForeColor = Control.DefaultForeColor;
                    this.ux_scenariosFile.BackColor = Color  .Empty;
                }

                //
                this.ux_marketDataFileOutdatedLabel  .Visible = false;
                this.ux_technologiesFileOutdatedLabel.Visible = false;
                this.ux_parametersFileOutdatedLabel  .Visible = false;
                this.ux_scenariosFileOutdatedLabel   .Visible = false;

                this.ux_outputPath.ForeColor = Control.DefaultForeColor;
                this.ux_outputPath.BackColor = Color.Empty;
            }
        }

        #endregion

        #region /*** Properties ***/

        /// <summary>Gets the updated data loaded from the market-data input file, or null, if nothing was loaded.</summary>
        [Browsable(false)]
        public IIndustry MarketData => (this._data[MarketDataID] == null) ? null : (IIndustry)this._data[MarketDataID];
        /// <summary>Gets the updated data loaded from the technologies input file, or null, if nothing was loaded.</summary>
        [Browsable(false)]
        public Technologies Technologies => (this._data[TechnologiesID] == null) ? null : (Technologies)this._data[TechnologiesID];
        /// <summary>Gets the updated data loaded from the parameters input file, or null, if nothing was loaded.</summary>
        [Browsable(false)]
        public Parameters Parameters => (this._data[ParametersID] == null) ? null : (Parameters)this._data[ParametersID];
        /// <summary>Gets the updated data loaded from the scenarios input file, or null, if nothing was loaded.</summary>
        [Browsable(false)]
        public Scenarios Scenarios => (this._data[ScenariosID] == null) ? null : (Scenarios)this._data[ScenariosID];

        /// <summary>Gets whether the <see cref="IOSettingsPanel"/> is currently parsing, verifying, or loading input files.</summary>
        [Browsable(false)]
        public bool Parsing => this._parsing > 0;

        /// <summary>Gets whether any of the settings have changed since the control has been loaded or since the changes have
        ///   been last saved.</summary>
        [Browsable(false)]
        public override bool SettingsChanged
        {
            get
            {
                if (this.Parsing) { return false; }     // cannot save if parsing in progress
                //
                // first, perform some error checking (e.g., missing or incorrect text fields)
                if ((!Directory.Exists(this.ux_outputPath      .Text)) ||
                    (!File     .Exists(this.ux_marketDataFile  .Text)) ||
                    (!File     .Exists(this.ux_technologiesFile.Text)) ||
                    (!File     .Exists(this.ux_parametersFile  .Text)) ||
                    (!File     .Exists(this.ux_scenariosFile   .Text))) { return false; }
                //
                // then, check to see if things actually changed compared to the input-settings struct
                return
                    base.SettingsChanged ||
                    this.ux_reloadAllFromDisk.Checked ||
                    (!this.CompareDirectories(this._ioSettings.BaseOutputPath  , this.ux_outputPath      .Text)) ||
                    (!this.CompareFilePaths  (this._ioSettings.MarketDataFile  , this.ux_marketDataFile  .Text)) ||
                    (!this.CompareFilePaths  (this._ioSettings.TechnologiesFile, this.ux_technologiesFile.Text)) ||
                    (!this.CompareFilePaths  (this._ioSettings.ParametersFile  , this.ux_parametersFile  .Text)) ||
                    (!this.CompareFilePaths  (this._ioSettings.ScenariosFile   , this.ux_scenariosFile   .Text));
            }
        }

        #endregion

        #region /*** Variables ***/

        #region /* UI Variables */

        //----- output settings -----
        Label       ux_outputPathLabel;
        TextBox     ux_outputPath;
        Button      ux_outputPathBrowse;
        CheckBox    ux_compressModelOutputs;

        #endregion

        static readonly string[] FriendlyTitles =
        {
            "Market-Data",
            "Technologies",
            "Parameters",
            "Scenarios"
        };

        const int FileCount      = 4;   // total number of files
        const int MarketDataID   = (int)InputFileType.MarketData;
        const int TechnologiesID = (int)InputFileType.Technologies;
        const int ParametersID   = (int)InputFileType.Parameters;
        const int ScenariosID    = (int)InputFileType.Scenarios;

        int    _parsing;                // indicates whether the panel is parsing, verifying, or loading input files
        bool   _finalizeParsing;
        Thread _saveThread;             // thread used for loading input data

        bool  [] _verifiedFiles;        // indicates that the path entered in the input text-box is valid
        bool  [] _loadFailed;           // indicates whether the file has failed to load (used for post-save when clearing visual tracking)
        object[] _data;                 // the input data objects loaded from file into memory (populated during save)
        private GroupBox ux_reportingGroupBox;
        private CheckBox ux_splitLargeReports;
        private RadioButton ux_minimalReporting;
        private RadioButton ux_defaultReporting;
        private RadioButton ux_diagnosticReporting;
        private GroupBox ux_loggingGroupBox;
        private RadioButton ux_debugLogging;
        private RadioButton ux_defaultLogging;
        private RadioButton ux_minimalLogging;
        private Label ux_marketDataFileLabel;
        private Label ux_scenariosFileLabel;
        private Label ux_parametersFileLabel;
        private Label ux_technologiesFileLabel;
        private Label ux_technologiesFileOutdatedLabel;
        private Label ux_parametersFileOutdatedLabel;
        private Label ux_scenariosFileOutdatedLabel;
        private TextBox ux_marketDataFile;
        private Button ux_marketDataFileBrowse;
        private TextBox ux_technologiesFile;
        private Button ux_technologiesFileBrowse;
        private Label ux_inputFilesLabel;
        private TextBox ux_parametersFile;
        private Button ux_parametersFileBrowse;
        private TextBox ux_scenariosFile;
        private Button ux_scenariosFileBrowse;
        private CheckBox ux_reloadAllFromDisk;
        private FlowLayoutPanel ux_inputFilesPanel;
        private FlowLayoutPanel ux_outputSettingsPanel;
        private Label ux_marketDataFileOutdatedLabel;


        /// <summary>Represents the input and output configuration settings for the modeling system.</summary>
        IOSettings _ioSettings;

        #endregion

    }
}
