using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace AdvertsingProfitControl
{
    public partial class NewModifyRecord : Form
    {
        //http://stackoverflow.com/questions/6219454/efficient-way-to-remove-all-whitespace-from-string
        private static readonly FrmLogConsole LogConsole = FrmLogConsole.GetStaticInstance;
        //Create an array that contains all the ad items from the database.
        private readonly List<string> _adItemCollection;
        //This object contains all the unused ad items.
        private AutoCompleteStringCollection _trimmedAdItemCollection = new AutoCompleteStringCollection();
        //Contains all the ad specials that are in the database (i.e. "Daily Coupons").
        private readonly AutoCompleteStringCollection _adSpecialList;
        //This object contains all the suppliers that were found in the database.
        private readonly AutoCompleteStringCollection _supplierCollection;
        //This array keeps track of the number of times an Ad Item is used, if used once then it may only be used AFTER an AdSpecialRow and only once after that.
        //Once an item has been used twice, it will not appear again in the AutoCompleteSuggestions.
        //The structure of the used ad item list is as follows:
        //List(0) is section one and List(1) is section two. Section one is not ad special and section two is.
        private readonly List<string>[] _usedAdItems = new List<string>[2];
        //This string keeps track of the last ad item used. This item can then be used to safely remove an ad item from the list of used items.
        //Used in the OnCellValidating event to store the last ad item used in the event that the user changes a row that already exists.
        private string _beginningCellValue = string.Empty;
        //Flag showing whether or not the AdSpecialRow has been made in this session.
        private int _adSpecialIndex = -1;
        private readonly AdvertisingProfitControlTableHelper _tableHelperFunctions = new AdvertisingProfitControlTableHelper();
        //Represents the date that is being modified, if this changes then the form needs to be cleared and refilled with the new date.
        private DateTime _currentActiveDate;
        //
        private bool _isFormDirty;
        //
        private FormRoll _formRoll;
        public NewModifyRecord(DateTime date)
        {
            InitializeComponent();           
            //Start by grabbing all the AdItems and putting them into memory.
            var databaseTracker = new DatabaseTracker();
            var databaseReader = new DatabaseReader();
            weekEndingCalendar.BoldedDates = databaseReader.RetrieveDates(databaseTracker.DatabaseConnectionString).ToArray();
            weekEndingCalendar.SelectionStart = date;
            _currentActiveDate = date;
            weekEndingCalendar.DateChanged += ValidateDateChanged;          
            //next pull all the ad items into memory.
            _adItemCollection = databaseReader.GetAdItemsSuggestionList(databaseTracker.DatabaseConnectionString);
            //Now pull all the suppliers and the ad special list into memory.
            _supplierCollection = databaseReader.GetSupplierSuggestionList(databaseTracker.DatabaseConnectionString);
            _adSpecialList = databaseReader.RetrieveAdSpecialList(databaseTracker.DatabaseConnectionString);
            //Initialize the used ad item collection.
            _usedAdItems[0] = new List<string>();
            _usedAdItems[1] = new List<string>();
            //Assign the events for the comments text box and display the remaining character count for the user.
            commentsTextBox.TextChanged += DisplayRemainingCommentCharacterCount;
            commentsGroupBox.Text = @"Comments (Characters Remaining: " + commentsTextBox.MaxLength + @")";
            //Setup the events for that the Projected and Actual Sales DataGridViews will share.
            //Events are assigned with regard to which event gets triggered first and so on..
            //Hook the row add event so we can paint a row number in the header cell of the row.
            projectionsDataGridView.RowsAdded += DisplayRowNumbers;
            inventoryDataGridView.RowsAdded += DisplayRowNumbers;
            actualSalesDataGridView.RowsAdded += DisplayRowNumbers;
            //Assign all the tables to store the cell's contents on enter so changes (if any) can be detected and flagged (marked as dirty).
            projectionsDataGridView.CellEnter += StoreBeginningCellValue;
            inventoryDataGridView.CellEnter += StoreBeginningCellValue;
            actualSalesDataGridView.CellEnter += StoreBeginningCellValue;
            //Update the contents of the used as item list on row leave.
            projectionsDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            inventoryDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            actualSalesDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            //Validate, clean and format the contents of the cell that fires the cell validating event.
            projectionsDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            inventoryDataGridView.CellValidating += ValidateInventoryCellContents;
            actualSalesDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            //Validate that the row the user is trying to leave is legal (has at least an ad item entered) and prevent the user from leaving the row is its not.
            projectionsDataGridView.RowValidating += ValidateProjectedRow;
            inventoryDataGridView.RowValidating += ValidateInventoryRow;
            actualSalesDataGridView.RowValidating += ValidateActualSalesRow;
            //Update the used ad item collection by removing the contents of the ad item column when a row is deleted.
            projectionsDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            inventoryDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            actualSalesDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            //Once a row has been removed update the other tables keep them uniform.
            projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;
            inventoryDataGridView.RowsRemoved += InventoryRowRemoved;
            actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;
            //Grab the underlying text box object in the ad item cell, and build an auto complete list for the user.
            projectionsDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
            inventoryDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
            actualSalesDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
            //After all events have been set, construct the DataGridVeiws for use.
            ConstructApcDataGridViews();
            //Set-up events for the Invoice table.
            invoicesDataGridView.CellEnter += StoreBeginningCellValue;
            //Validate that the row the user is trying to leave is legal (has at least an ad item entered) and prevent the user from leaving the row is its not.
            invoicesDataGridView.RowValidating += ValidateInvoiceRow;
            //Validate, clean and format the contents of the cell that fires the cell validating event.
            invoicesDataGridView.CellValidating += ValidateInvoicesCellContents;
            //Grab the underlying text box object in the ad item cell, and build an auto complete list for the user.
            invoicesDataGridView.EditingControlShowing += DisplaySupplierAutoComleteOnEditingShadowControl;
            //Subscribe the method to allow the user to delete saved rows from the Invoices table.
            invoicesDataGridView.UserDeletingRow += UpdateInvoicesOnRowDeleting;
            //Finally build the last DataGridView for the form.
            ConstructInvoicesDataGridView();//No weekly sales table is nice.
            //Subscribe the comments text box to check if changes have been made on leave.
            commentsTextBox.Enter += StoreBeginningTextBoxValue;
            commentsTextBox.KeyDown += CheckForKeyCommand;
            commentsTextBox.Leave += CheckForTextChangeOnLeave;
            //Subscribe the weekly sales text boxes to validation, update required checks and auto-complete methods.
            sundayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            sundayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            mondayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            mondayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            tuesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            tuesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            wednesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            wednesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            thursdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            thursdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            fridayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            fridayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            saturdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            saturdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            totalWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            totalWeeklySalesTextBox.Validating += ValidateWeeklySales;
            //Subscribe the taxable text boxes to validation and update events.
            sundayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            sundayTaxableTextBox.Validating += ValidateTaxableFields;
            mondayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            mondayTaxableTextBox.Validating += ValidateTaxableFields;
            tuesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            tuesdayTaxableTextBox.Validating += ValidateTaxableFields;
            wednesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            wednesdayTaxableTextBox.Validating += ValidateTaxableFields;
            thursdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            thursdayTaxableTextBox.Validating += ValidateTaxableFields;
            fridayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            fridayTaxableTextBox.Validating += ValidateTaxableFields;
            saturdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            saturdayTaxableTextBox.Validating += ValidateTaxableFields;
            totalTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            totalTaxableTextBox.Validating += ValidateTaxableFields;
            //Subscribe the Cost Analysis text boxes to the validation and update events.
            salesPerManHourTextBox.Enter += StoreBeginningTextBoxValue;
            salesPerManHourTextBox.Validating += ValidateCostAnalysisValues;
            salaryPercentageTextBox.Enter += StoreBeginningTextBoxValue;
            salaryPercentageTextBox.Validating += ValidateCostAnalysisValues;
            salaryDollarsTextBox.Enter += StoreBeginningTextBoxValue;
            salaryDollarsTextBox.Validating += ValidateCostAnalysisValues;
            suppliesTextBox.Enter += StoreBeginningTextBoxValue;
            suppliesTextBox.Validating += ValidateCostAnalysisValues;
            //
            //_debugTabPage = mainTabControl.TabPages[4];
            //mainTabControl.TabPages.Remove(mainTabControl.TabPages[4]);
            Text = @"Modify Record (Current Record: " + date.ToShortDateString() + @")";
            _formRoll = FormRoll.ModifyRecord;
            LoadDate(date);
        }

        public NewModifyRecord()
        {
            InitializeComponent();
            //Start by grabbing all the AdItems and putting them into memory.
            var databaseTracker = new DatabaseTracker();
            var databaseReader = new DatabaseReader();
            weekEndingCalendar.BoldedDates = databaseReader.RetrieveDates(databaseTracker.DatabaseConnectionString).ToArray();
            SetNewRecordDate(databaseReader.RetrieveMostRecentDate(databaseTracker.DatabaseConnectionString), 1);
            _currentActiveDate = weekEndingCalendar.SelectionStart;
            //next pull all the ad items into memory.
            _adItemCollection = databaseReader.GetAdItemsSuggestionList(databaseTracker.DatabaseConnectionString);
            //Now pull all the suppliers and the ad special list into memory.
            _supplierCollection = databaseReader.GetSupplierSuggestionList(databaseTracker.DatabaseConnectionString);
            _adSpecialList = databaseReader.RetrieveAdSpecialList(databaseTracker.DatabaseConnectionString);
            //Initialize the used ad item collection.
            _usedAdItems[0] = new List<string>();
            _usedAdItems[1] = new List<string>();
            //Assign the events for the comments text box and display the remaining character count for the user.
            commentsTextBox.TextChanged += DisplayRemainingCommentCharacterCount;
            commentsGroupBox.Text = @"Comments (Characters Remaining: " + commentsTextBox.MaxLength + @")";
            //Setup the events for that the Projected and Actual Sales DataGridViews will share.
            //Events are assigned with regard to which event gets triggered first and so on..
            //Hook the row add event so we can paint a row number in the header cell of the row.
            projectionsDataGridView.RowsAdded += DisplayRowNumbers;
            inventoryDataGridView.RowsAdded += DisplayRowNumbers;
            actualSalesDataGridView.RowsAdded += DisplayRowNumbers;
            //Assign all the tables to store the cell's contents on enter so changes (if any) can be detected and flagged (marked as dirty).
            projectionsDataGridView.CellEnter += StoreBeginningCellValue;
            inventoryDataGridView.CellEnter += StoreBeginningCellValue;
            actualSalesDataGridView.CellEnter += StoreBeginningCellValue;
            //Update the contents of the used as item list on row leave.
            projectionsDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            inventoryDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            actualSalesDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            //Validate, clean and format the contents of the cell that fires the cell validating event.
            projectionsDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            inventoryDataGridView.CellValidating += ValidateInventoryCellContents;
            actualSalesDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            //Validate that the row the user is trying to leave is legal (has at least an ad item entered) and prevent the user from leaving the row is its not.
            projectionsDataGridView.RowValidating += ValidateProjectedRow;
            inventoryDataGridView.RowValidating += ValidateInventoryRow;
            actualSalesDataGridView.RowValidating += ValidateActualSalesRow;
            //Update the used ad item collection by removing the contents of the ad item column when a row is deleted.
            projectionsDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            inventoryDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            actualSalesDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            //Once a row has been removed update the other tables keep them uniform.
            projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;
            inventoryDataGridView.RowsRemoved += InventoryRowRemoved;
            actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;
            //Grab the underlying text box object in the ad item cell, and build an auto complete list for the user.
            projectionsDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
            inventoryDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
            actualSalesDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
            //After all events have been set, construct the DataGridVeiws for use.
            ConstructApcDataGridViews();
            //Set-up events for the Invoice table.
            invoicesDataGridView.CellEnter += StoreBeginningCellValue;
            //Validate that the row the user is trying to leave is legal (has at least an ad item entered) and prevent the user from leaving the row is its not.
            invoicesDataGridView.RowValidating += ValidateInvoiceRow;
            //Validate, clean and format the contents of the cell that fires the cell validating event.
            invoicesDataGridView.CellValidating += ValidateInvoicesCellContents;
            //Grab the underlying text box object in the ad item cell, and build an auto complete list for the user.
            invoicesDataGridView.EditingControlShowing += DisplaySupplierAutoComleteOnEditingShadowControl;
            //Subscribe the method to allow the user to delete saved rows from the Invoices table.
            invoicesDataGridView.UserDeletingRow += UpdateInvoicesOnRowDeleting;
            //Finally build the last DataGridView for the form.
            ConstructInvoicesDataGridView();//No weekly sales table is nice.
            //Subscribe the comments text box to check if changes have been made on leave.
            commentsTextBox.Enter += StoreBeginningTextBoxValue;
            commentsTextBox.KeyDown += CheckForKeyCommand;
            commentsTextBox.Leave += CheckForTextChangeOnLeave;
            //Subscribe the weekly sales text boxes to validation, update required checks and auto-complete methods.
            sundayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            sundayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            mondayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            mondayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            tuesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            tuesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            wednesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            wednesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            thursdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            thursdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            fridayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            fridayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            saturdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            saturdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            totalWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            totalWeeklySalesTextBox.Validating += ValidateWeeklySales;
            //Subscribe the taxable text boxes to validation and update events.
            sundayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            sundayTaxableTextBox.Validating += ValidateTaxableFields;
            mondayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            mondayTaxableTextBox.Validating += ValidateTaxableFields;
            tuesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            tuesdayTaxableTextBox.Validating += ValidateTaxableFields;
            wednesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            wednesdayTaxableTextBox.Validating += ValidateTaxableFields;
            thursdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            thursdayTaxableTextBox.Validating += ValidateTaxableFields;
            fridayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            fridayTaxableTextBox.Validating += ValidateTaxableFields;
            saturdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            saturdayTaxableTextBox.Validating += ValidateTaxableFields;
            totalTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            totalTaxableTextBox.Validating += ValidateTaxableFields;
            //Subscribe the Cost Analysis text boxes to the validation and update events.
            salesPerManHourTextBox.Enter += StoreBeginningTextBoxValue;
            salesPerManHourTextBox.Validating += ValidateCostAnalysisValues;
            salaryPercentageTextBox.Enter += StoreBeginningTextBoxValue;
            salaryPercentageTextBox.Validating += ValidateCostAnalysisValues;
            salaryDollarsTextBox.Enter += StoreBeginningTextBoxValue;
            salaryDollarsTextBox.Validating += ValidateCostAnalysisValues;
            suppliesTextBox.Enter += StoreBeginningTextBoxValue;
            suppliesTextBox.Validating += ValidateCostAnalysisValues;
            //
            //_debugTabPage = mainTabControl.TabPages[4];
            //mainTabControl.TabPages.Remove(mainTabControl.TabPages[4]);
            Text = @"Add New Record (Current Record: " + _currentActiveDate.ToShortDateString() + @")";
            _formRoll = FormRoll.AddRecord;
            addRecordButton.Text = @"Add Record";
        }

        public sealed override string Text
        {
            get { return base.Text; }
            set { base.Text = value; }
        }

        #region Global Text Box Events

        /// <summary>
        /// Stores the beginning value in a text box into the _beginningCellValue field.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void StoreBeginningTextBoxValue(object sender, EventArgs e)
        {
            var textBox = (TextBox)sender;
            _beginningCellValue = textBox.Text.Trim();
        }

        #endregion

        #region Invoice Table Events

        /// <summary>
        /// Event Used: RowValidating
        /// Validates that the row has required information, namely an invoice date, number and supplier.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void ValidateInvoiceRow(object sender, DataGridViewCellCancelEventArgs e)
        {
            var dataGridView = (DataGridView)sender;
            if (dataGridView.Rows[e.RowIndex].IsNewRow) return;
            DateTime dateTime;
            //Check to make sure the Invoice Date, Invoice Number and the Supplier values are set.
            if (dataGridView.Rows[e.RowIndex].Cells[(int)InvoiceTableColumns.InvoiceDate].EditedFormattedValue.ToString().Length == 0 || !DateTime.TryParse(dataGridView.Rows[e.RowIndex].Cells[(int)InvoiceTableColumns.InvoiceDate].EditedFormattedValue.ToString(), out dateTime))
            {
                dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.RowError;
                MessageBox.Show(@"An invoice date must be specified.", @"Invalid Invoice Date", MessageBoxButtons.OK, MessageBoxIcon.Error);
                e.Cancel = true;
                dataGridView.CurrentCell = dataGridView.Rows[e.RowIndex].Cells[(int)InvoiceTableColumns.InvoiceDate];
                return;
            }

            if (dataGridView.Rows[e.RowIndex].Cells[(int)InvoiceTableColumns.Supplier].EditedFormattedValue.ToString().Trim().Length == 0)
            {
                dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.RowError;
                MessageBox.Show(@"A supplier must be specified.", @"Invalid Supplier", MessageBoxButtons.OK, MessageBoxIcon.Error);
                e.Cancel = true;
                dataGridView.CurrentCell = dataGridView.Rows[e.RowIndex].Cells[(int)InvoiceTableColumns.Supplier];
                return;
            }

            if (
                dataGridView.Rows[e.RowIndex].Cells[(int)InvoiceTableColumns.InvoiceNumber].EditedFormattedValue
                    .ToString().Trim().Length == 0)
            {
                dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.RowError;
                MessageBox.Show(@"An invoice number must be specified.", @"Invalid Invoice Number", MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                dataGridView.CurrentCell = dataGridView.Rows[e.RowIndex].Cells[(int)InvoiceTableColumns.InvoiceNumber];
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Event Used: CellValidating
        /// Verifies the contents of the invoice table's cells. Also applies formating where needed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateInvoicesCellContents(object sender, DataGridViewCellValidatingEventArgs e)
        {
            //Grab the DataGirdView that fired the event and make it into a local variable.
            var dataGridView = (DataGridView)sender;
            var userInput = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString().Trim();
            var textInfo = new CultureInfo("en-US", false).TextInfo;
            //Check for isNewRow if it is, return no need to check it for anything.
            if (dataGridView.Rows[e.RowIndex].IsNewRow)
            {
                return;
            }
            //Mark the row as dirty assuming the user made changes
            if (userInput != _beginningCellValue)
            {
                dataGridView.Rows[e.RowIndex].Cells[(int)InvoiceTableColumns.IsDirty].Value = true;
            }
            else
            {
                return;
            }
            //Check to see if we're in the invoice date column make sure the date is valid.
            switch (e.ColumnIndex)
            {
                case (int)InvoiceTableColumns.InvoiceDate:
                    //Clear any error text a cell has for this column.
                    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
                    if (userInput != string.Empty)
                    {
                        DateTime date;
                        //Try parsing the date to make sure its valid, otherwise clear it from the cell and inform the user.
                        if (DateTime.TryParse(userInput, out date))
                        {
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = date.ToString("d");
                            dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                            _isFormDirty = true;
                        }
                        else
                        {
                            MessageBox.Show(@"The date '" + userInput + @"' is not a valid date.", @"Invalid Date", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "Invoice Date Must be in a Valid Format";
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                        }
                    }
                    else
                    {
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "An Invoice Date is Required";
                    }
                    break;
                case (int)InvoiceTableColumns.InvoiceNumber:
                    //Clear any error text a cell has for this column.
                    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
                    if (userInput != string.Empty)
                    {
                        long parsedNumber;
                        if (long.TryParse(userInput, out parsedNumber))
                        {
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = parsedNumber;
                            dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                            _isFormDirty = true;
                        }
                        else
                        {
                            MessageBox.Show(@"The invoice number must be a numeric value.", @"Non Numeric Invoice Number", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "Invoice Number Must be Numeric";
                        }
                    }
                    else
                    {
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "An Invoice Number is Required";
                    }
                    break;
                case (int)InvoiceTableColumns.Supplier:
                    //Clear any error text a cell has for this column.
                    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
                    if (userInput != string.Empty)
                    {
                        //TODO: Create a custom engine to do this.
                        //Pretty up the entered text since there is something here.
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = textInfo.ToTitleCase(userInput);
                        dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                        _isFormDirty = true;
                    }
                    else
                    {
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "A Supplier is Required";
                    }
                    break;
                case (int)InvoiceTableColumns.InvoiceNote:
                    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = userInput;
                    dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                    _isFormDirty = true;
                    break;
                default:
                    //Should only process the InvoiceNetAmountAtCost and InvoiceNetAmount columns.
                    if (e.ColumnIndex != (int)InvoiceTableColumns.Id &&
                        e.ColumnIndex < (int)InvoiceTableColumns.InvoiceNote)
                    {
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
                        //Now check to make sure the user input isn't null
                        //Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
                        if (userInput != string.Empty)
                        {
                            double parsedNumber;
                            if (double.TryParse(userInput, out parsedNumber))
                            {
                                //Since the input is a number format it to show the cents and display it.
                                dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
                                dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                                _isFormDirty = true;
                            }
                            else
                            {
                                dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                                dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "Only Numeric Values Allowed.";
                            }
                        }
                    }
                    break;
            }
            dataGridView.RefreshEdit();
        }

        /// <summary>
        /// Event Used: EditingShadowControlShowing
        /// Adds an auto-complete list of suppliers to the Suppliers cell.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DisplaySupplierAutoComleteOnEditingShadowControl(object sender, DataGridViewEditingControlShowingEventArgs e)
        {
            var dataGridView = (DataGridView)sender;
            var autoText = e.Control as TextBox;
            if (autoText == null) return;
            if (!(e.Control is DataGridViewTextBoxEditingControl) || dataGridView.CurrentCell.ColumnIndex != (int)InvoiceTableColumns.Supplier) return;
            autoText.AutoCompleteMode = AutoCompleteMode.Suggest;
            autoText.AutoCompleteSource = AutoCompleteSource.CustomSource;
            autoText.AutoCompleteCustomSource = _supplierCollection;
        }

        /// <summary>
        /// Checks to see if the row that the user is attempting to delete has been saved to the database.
        /// If so, this method will try to delete the record, failing that it will cancel the row deletion.
        /// </summary>
        /// <param name="sender">Invoice table.</param>
        /// <param name="e"></param>
        private void UpdateInvoicesOnRowDeleting(object sender, DataGridViewRowCancelEventArgs e)
        {
            var rowIndex = e.Row.Index;
            //See if there is an ID number in the ID column.
            if (invoicesDataGridView.Rows[rowIndex].Cells[(int)InvoiceTableColumns.Id].EditedFormattedValue.ToString() == string.Empty) return;
            //Ask the user to make damn sure they want to remove this record.
            var result = MessageBox.Show(@"Deleting this row will remove it from the database permanently. Do you wish to continue?", @"Remove Invoice Number " + invoicesDataGridView.Rows[rowIndex].Cells[(int)InvoiceTableColumns.InvoiceNumber].EditedFormattedValue, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (result == DialogResult.Yes)
            {
                //If so create the database interaction objects.
                var dbTracker = new DatabaseTracker();
                var dbWriter = new DatabaseWriter(dbTracker.DatabaseConnectionString);
                var id =
                    int.Parse(
                        invoicesDataGridView.Rows[rowIndex].Cells[(int)InvoiceTableColumns.Id].EditedFormattedValue
                            .ToString());
                if (dbWriter.DeleteInvoiceRow(id))
                {
                    informationLabel.Text = @"Successfully removed row " + (rowIndex + 1) + @" from the database.";
                }
                else
                {
                    //The operation failed.
                    errorLabel.Text = @"Failed to delete row " + (rowIndex + 1) + @" from invoices.";
                    e.Cancel = true;
                }
            }
            else
            {
                e.Cancel = true;
            }
        }

        #endregion

        #region APC DataGridView Events

        /// <summary>
        /// Event Used: RowsAdded
        /// Draws the row number in the row's cell header whenever a row is added.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void DisplayRowNumbers(object sender, DataGridViewRowsAddedEventArgs e)
        {
            var table = ((DataGridView)sender);
            table.Rows[e.RowIndex].HeaderCell.Value = (e.RowIndex + 1).ToString();
        }

        /// <summary>
        /// Event Used: CellEnter
        /// Stores the initial contents of the cell being entered to be compared later 
        /// to see if the user has made any changes (IsDirty).
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void StoreBeginningCellValue(object sender, DataGridViewCellEventArgs e)
        {
            var dataGridView = (DataGridView)sender;
            _beginningCellValue = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
        }

        /// <summary>
        /// Event Used: OnRowLeave
        /// Adds the ad items to the used ad item collection if they are not already in the collection.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateUsedAdItemCollectionOnRowLeave(object sender, DataGridViewCellEventArgs e)
        {
            var dataGridView = ((DataGridView)sender);
            if (dataGridView.Rows[e.RowIndex].IsNewRow) { return; } //Return if the row is a new row as nothing needs to be done here.
            int adItemIndex;

            switch (dataGridView.Name)
            {
                case "projectionsDataGridView":
                    adItemIndex = (int)SalesTableColumns.AdItem;
                    break;
                case "actualSalesDataGridView":
                    adItemIndex = (int)SalesTableColumns.AdItem;
                    break;
                default:
                    adItemIndex = (int)InventoryTableColumns.AdItem;
                    break;
            }
            var userInput = dataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString().Trim();
            //Paint the rows to identify what group they belong to.
            _tableHelperFunctions.PaintRowGroupsFromIndex(e.RowIndex, dataGridView);
            //Add in used Ad Items to the list.
            if (userInput == string.Empty) return;
            //Clear the old ad item out of the used ad item collection.
            //Section one (1) detected.
            if (_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex)
            {
                if (_usedAdItems[0].Contains(userInput)) return;
                _usedAdItems[0].Add(userInput);
            }
            //Section two (2) detected.
            else if(e.RowIndex > _adSpecialIndex)
            {
                if (_usedAdItems[1].Contains(userInput)) return;
                _usedAdItems[1].Add(userInput);
            }
        }

        /// <summary>
        /// Event Used: OnEditingControlShowing
        /// Configures the auto complete collection and how it will be shown to the user. This method detects the section,
        /// either one (1) or two (2), based on the gAdSpecialIndex and removes items from the auto complete accordingly.
        /// Just a measure to help reduce redundancy in the tables.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DisplayAutoCompleteOnEditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
        {
            var dataGridView = ((DataGridView)sender);
            var autoText = e.Control as TextBox;
            //Get the index of the ad item column
            const int adItemIndex = (int)SalesTableColumns.AdItem;
            if (e.Control is DataGridViewTextBoxEditingControl && dataGridView.CurrentCell.ColumnIndex == adItemIndex)
            {
                //Create a copy of the main ad item list that can be freely manipulated.
                var customAutoComplete = new AutoCompleteStringCollection();
                var customList = _adItemCollection.ToList();

                //IF the current row is less than the AdSpecialRow, remove all used items with the section 1 attribute.
                if (_adSpecialIndex == -1 || dataGridView.CurrentCell.RowIndex < _adSpecialIndex)
                {
                    foreach (var adItem in _usedAdItems[0])
                    {
                        customList.RemoveAll(w => w.Equals(adItem, StringComparison.OrdinalIgnoreCase));
                    }
                }
                //Occurs AFTER the AdSpecial row.
                else if (dataGridView.CurrentCell.RowIndex > _adSpecialIndex)
                {
                    foreach (var adItem in _usedAdItems[1])
                    {
                        customList.RemoveAll(w => w.Equals(adItem, StringComparison.OrdinalIgnoreCase));
                    }
                }
                foreach (var item in customList)
                {
                    customAutoComplete.Add(item);
                }
                autoText.KeyDown += ChangeAutoCompleteListOnKeyCombo;
                _trimmedAdItemCollection = customAutoComplete; //Make a temporary copy of the list for use with the TextBox event handler.
                autoText.AutoCompleteMode = AutoCompleteMode.Suggest;
                autoText.AutoCompleteSource = AutoCompleteSource.CustomSource;
                autoText.AutoCompleteCustomSource = customAutoComplete;
            }
            else if (e.Control is DataGridViewTextBoxEditingControl && dataGridView.CurrentCell.ColumnIndex != adItemIndex)
            {
                autoText.AutoCompleteMode = AutoCompleteMode.None;
            }
        }

        /// <summary>
        /// Event Used: UserDeleteingRow
        /// This function is responsible for removing ad Items from the gUsedAdItem array; this must be done during the row removing
        /// event handler so the data in the row can be grabbed and used.
        /// </summary>
        /// <param name="sender">The DataGridView that fired the event.</param>
        /// <param name="e">Parameters, mainly allowing for canceling the event.</param>
        private void UpdateUsedAdItemCollectionOnRowRemoving(object sender, CancelEventArgs e)
        {
            //Create an object that represents the DataGridView that fired the event.
            var dataGridView = (DataGridView)sender;
            if (dataGridView.CurrentRow == null) return;
            //Create the database writer object so rows that are in the database can be deleted.
            var dbTracker = new DatabaseTracker();
            var dbWriter = new DatabaseWriter(dbTracker.DatabaseConnectionString);
            var currentRowIndex = dataGridView.CurrentRow.Index;
            //Remove the ad item from the gUsedAdItem collection, if it exists.
            if (_adSpecialIndex == -1)
            {
                //If the ID number is set, i.e. not equal to null then attempt to remove it from the database.
                if (dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() != string.Empty)
                {
                    var result = MessageBox.Show(@"Removing this row will permanently delete this record from the database. Do you wish to continue?", @"Remove " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue, MessageBoxButtons.YesNo);
                    if (result == DialogResult.Yes)
                    {
                        //Attempt to delete the row from the database by its ID number.
                        int projectionsRowId;
                        int inventoryRowId;
                        int actualSalesRowId;
                        //Attempt to delete the row from the database by its ID number.
                        int.TryParse(
                            projectionsDataGridView.Rows[currentRowIndex].Cells[(int) SalesTableColumns.Id]
                                .EditedFormattedValue.ToString(), out projectionsRowId);
                        int.TryParse(
                            inventoryDataGridView.Rows[currentRowIndex].Cells[(int) SalesTableColumns.Id]
                                .EditedFormattedValue.ToString(), out inventoryRowId);
                        int.TryParse(
                            actualSalesDataGridView.Rows[currentRowIndex].Cells[(int) SalesTableColumns.Id]
                                .EditedFormattedValue.ToString(), out actualSalesRowId);
                        if (dbWriter.DeleteApcRow(projectionsRowId, inventoryRowId, actualSalesRowId))
                        {
                            informationLabel.Text = @"Successfully removed row " + (currentRowIndex + 1) +
                                                    @" from the database.";
                        }
                        else
                        {
                            //If the removing failed cancel the row deletion in the DataGridView.
                            e.Cancel = true;
                            MessageBox.Show(
                                @"Failed to delete " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue +
                                @" from the database.", @"Failed to Update Database");
                            return;
                        }
                    }
                    else
                    {
                        e.Cancel = true;
                        return;
                    }
                }
                //If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
                _usedAdItems[0].Remove(dataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.AdItem].EditedFormattedValue.ToString());
                FlagRowsAsDirty(dataGridView, currentRowIndex);
            }
            else if (currentRowIndex < _adSpecialIndex)
            {
                //If the ID number is set, i.e. not equal to null then attempt to remove it from the database.
                if (dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() != string.Empty)
                {
                    var result = MessageBox.Show(@"Removing this row will permanently delete this record from the database. Do you wish to continue?", @"Remove " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue, MessageBoxButtons.YesNo);
                    if (result == DialogResult.Yes)
                    {
                        //Attempt to delete the row from the database by its ID number.
                        int projectionsRowId;
                        int inventoryRowId;
                        int actualSalesRowId;
                        //Attempt to delete the row from the database by its ID number.
                        int.TryParse(projectionsDataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString(), out projectionsRowId);
                        int.TryParse(inventoryDataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString(), out inventoryRowId);
                        int.TryParse(actualSalesDataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString(), out actualSalesRowId);
                        if (dbWriter.DeleteApcRow(projectionsRowId, inventoryRowId, actualSalesRowId))
                        {
                            informationLabel.Text = @"Successfully removed row " + (currentRowIndex + 1) + @" from the database.";
                        }
                        else
                        {
                            //If the removing failed cancel the row deletion in the DataGridView.
                            e.Cancel = true;
                            MessageBox.Show(@"Failed to delete " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue +
                                @" from the database.", @"Failed to Update Database");
                            return;
                        }
                    }
                    else
                    {
                        e.Cancel = true;
                        return;
                    }
                }
                //If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
                _usedAdItems[0].Remove(dataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.AdItem].EditedFormattedValue.ToString());
                //Mark the rows as dirty before decrementing the _adSpecialIndex since this wouldn't account for the fact that the current row hasn't been cleared yet.
                FlagRowsAsDirty(dataGridView, currentRowIndex, _adSpecialIndex);
                //Also decrement the _adSpecialIndex so that it points to the correct row.
                _adSpecialIndex--;
            }
            else if (currentRowIndex > _adSpecialIndex)
            {
                //If the ID number is set, i.e. not equal to null then attempt to remove it from the database.
                if (dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() != string.Empty)
                {
                    var result = MessageBox.Show(@"Removing this row will permanently delete this record from the database. Do you wish to continue?", @"Remove " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue, MessageBoxButtons.YesNo);
                    if (result == DialogResult.Yes)
                    {
                        //Attempt to delete the row from the database by its ID number.
                        var projectionsRowId = int.Parse(projectionsDataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString());
                        var inventoryRowId = int.Parse(inventoryDataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString());
                        var actualSalesRowId = int.Parse(actualSalesDataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString());
                        if (dbWriter.DeleteApcRow(projectionsRowId, inventoryRowId, actualSalesRowId))
                        {
                            informationLabel.Text = @"Successfully removed row " + (currentRowIndex + 1) + @" from the database.";
                        }
                        else
                        {
                            //If the removing failed cancel the row deletion in the DataGridView.
                            e.Cancel = true;
                            MessageBox.Show(@"Failed to delete " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue +
                                @" from the database.", @"Failed to Update Database");
                            return;
                        }
                    }
                    else
                    {
                        e.Cancel = true;
                        return;
                    }
                }
                //If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
                _usedAdItems[1].Remove(dataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.AdItem].EditedFormattedValue.ToString());
                FlagRowsAsDirty(dataGridView, currentRowIndex);
            }
            else if (currentRowIndex == _adSpecialIndex)
            {
                //Handle removing the AdSpecial row.
                var result = MessageBox.Show(@"Deleting the Ad Special row will remove all rows beneath it. Do you wish to continue?", @"Clear " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (result == DialogResult.Yes)
                {
                    //Clear all events that handle row removal from both DataGridViews.
                    //Projections table
                    projectionsDataGridView.RowsRemoved -= ProjectionRowRemoved;
                    projectionsDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
                    //Inventory table
                    inventoryDataGridView.RowsRemoved -= InventoryRowRemoved;
                    inventoryDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
                    //Actual Sales table
                    actualSalesDataGridView.RowsRemoved -= ActualSalesRowRemoved;
                    actualSalesDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
                    //Now for-each through each row that is underneath the Ad Special row.
                    for (var rowIndex = dataGridView.RowCount; currentRowIndex != rowIndex; rowIndex--)
                    {
                        if (projectionsDataGridView.RowCount == inventoryDataGridView.RowCount &&
                            projectionsDataGridView.RowCount == actualSalesDataGridView.RowCount)
                        {
                            if (dataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
                            {
                                //Attempt to delete the row from the database by its ID number.
                                int projectionsRowId;
                                int inventoryRowId;
                                int actualSalesRowId;
                                //Attempt to delete the row from the database by its ID number.
                                int.TryParse(projectionsDataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString(), out projectionsRowId);
                                int.TryParse(inventoryDataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString(), out inventoryRowId);
                                int.TryParse(actualSalesDataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.Id].EditedFormattedValue.ToString(), out actualSalesRowId);
                                if (dbWriter.DeleteApcRow(projectionsRowId, inventoryRowId, actualSalesRowId))
                                {
                                    informationLabel.Text = @"Successfully removed row " + (currentRowIndex + 1) + @" from the database.";
                                }
                                else
                                {
                                    //If the removing failed cancel the row deletion in the DataGridView.
                                    e.Cancel = true;
                                    MessageBox.Show(@"Failed to delete " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue +
                                        @" from the database.", @"Failed to Update Database");
                                    return;
                                }
                            }
                        }
                        if (projectionsDataGridView.Rows[currentRowIndex].IsNewRow != true)
                        {
                            projectionsDataGridView.Rows.RemoveAt(currentRowIndex);
                        }
                        if (inventoryDataGridView.Rows[currentRowIndex].IsNewRow != true)
                        {
                            inventoryDataGridView.Rows.RemoveAt(currentRowIndex);
                        }
                        if (actualSalesDataGridView.Rows[currentRowIndex].IsNewRow != true)
                        {
                            actualSalesDataGridView.Rows.RemoveAt(currentRowIndex);
                        }
                        //If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
                        _usedAdItems[1].Remove(dataGridView.Rows[currentRowIndex].Cells[(int)SalesTableColumns.AdItem].EditedFormattedValue.ToString());
                    }
                    //Re-enable all row removal events on both tables.
                    //Projections table
                    projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;
                    projectionsDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
                    //Inventory table
                    inventoryDataGridView.RowsRemoved += InventoryRowRemoved;
                    inventoryDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
                    //Actual Sales table
                    actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;
                    actualSalesDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
                    //Reset the gAdSpecialIndex to -1.
                    _adSpecialIndex = -1;
                    e.Cancel = true; //Prevent the new row from being removed.
                    return;
                }
                e.Cancel = true;
            }
        }

        private void FlagRowsAsDirty(DataGridView dataGridView, int currentRowIndex, int adSpecialIndex = -1)
        {
            //Check to see if the user started to add a row but then decided against it, does not mark the form dirty.
            if (dataGridView.Rows.Count != inventoryDataGridView.Rows.Count ||
                dataGridView.Rows.Count != actualSalesDataGridView.Rows.Count) return;
            //Mark all rows under the row that just got deleted as dirty.
            _isFormDirty = true;
            for (var index = currentRowIndex; index < dataGridView.Rows.Count; index++)
            {
                //Do not color code the NewRow just break, we're done.
                if (dataGridView.Rows[index].IsNewRow) break;
                //Do not color code the Ad Special Row, just for preference.
                if (index == adSpecialIndex)
                {
                    //Clear it's row color coding just in-case the user clears more than one row.
                    projectionsDataGridView.Rows[index].Cells[(int)SalesTableColumns.IsDirty].Value = false;
                    projectionsDataGridView.Rows[index].HeaderCell.Style.BackColor = DefaultBackColor;
                    inventoryDataGridView.Rows[index].Cells[(int)InventoryTableColumns.IsDirty].Value = false;
                    inventoryDataGridView.Rows[index].HeaderCell.Style.BackColor = DefaultBackColor;
                    actualSalesDataGridView.Rows[index].Cells[(int)SalesTableColumns.IsDirty].Value = false;
                    actualSalesDataGridView.Rows[index].HeaderCell.Style.BackColor = DefaultBackColor;
                    continue;
                }
                projectionsDataGridView.Rows[index].Cells[(int)SalesTableColumns.IsDirty].Value = true;
                projectionsDataGridView.Rows[index].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                inventoryDataGridView.Rows[index].Cells[(int)InventoryTableColumns.IsDirty].Value = true;
                inventoryDataGridView.Rows[index].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                actualSalesDataGridView.Rows[index].Cells[(int)SalesTableColumns.IsDirty].Value = true;
                actualSalesDataGridView.Rows[index].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
            }
        }

        private void ChangeAutoCompleteListOnKeyCombo(object sender, KeyEventArgs e)
        {
            var textBox = (TextBox)sender;
            informationLabel.Text = string.Empty;

            if (e.Modifiers == Keys.Alt && e.KeyCode == Keys.S)
            {
                if (_adSpecialIndex == -1)
                {
                    textBox.AutoCompleteCustomSource = _adSpecialList;
                    informationLabel.Text = @"Auto complete mode changed to Ad Special.";
                }
                else
                {
                    textBox.AutoCompleteCustomSource = _trimmedAdItemCollection;
                    informationLabel.Text = @"An Ad Special row already exists, auto complete mode\n can not be changed.";
                }
            }
            else if (e.Modifiers == Keys.Alt && e.KeyCode == Keys.A)
            {
                textBox.AutoCompleteCustomSource = _trimmedAdItemCollection;
                informationLabel.Text = @"Auto complete mode changed to Ad Items.";
            }
            e.Handled = false;
        }

        #endregion

        #region Sales DataGridView Events

        /// <summary>
        /// Event Used: CellValidating
        /// Validates the contents of a cell, before leaving it. If the contents
        /// are valid then the appropriate formatting is applied if needed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateSalesDataGridViewCellContents(object sender, DataGridViewCellValidatingEventArgs e)
        {
            //Grab the DataGirdView that fired the event and make it into a local variable.
            var dataGridView = (DataGridView)sender;
            var userInput = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
            //TODO: Write a custom parsing engine for detecting when bins are entered.
            var textInfo = new CultureInfo("en-US", false).TextInfo;
            //Check for isNewRow if it is, return no need to check it for anything.
            if (dataGridView.Rows[e.RowIndex].IsNewRow)
            {
                return;
            }

            if (userInput == _beginningCellValue)
            {
                return;
            }
            //Check to make sure we're not in the boolean fields or the ID field.
            if (e.ColumnIndex >= (int)SalesTableColumns.IsHeaderRow || e.ColumnIndex == (int)SalesTableColumns.Id)
            {
                return;
            }
            //Cell validating gets to handle updating the used ad item list since it handles cells on by one, instead by a whole row.
            if (userInput != _beginningCellValue && e.ColumnIndex == (int)SalesTableColumns.AdItem)
            {
                //The user is trying to change the ad special text to something else.
                if (e.RowIndex == _adSpecialIndex)
                {
                    var parser = new RowParsing();
                    if (parser.CheckForGroupKeyWord(userInput) == "NoGroupFound")
                    {
                        MessageBox.Show(
                            @"The Ad Special row's column one (1) cannot be changed. You must delete this row by clicking on the header cell and pressing 'Delete'.",
                            @"Invalid Operation on Ad Special Row", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _beginningCellValue;
                        dataGridView.RefreshEdit();
                        return;
                    }
                    //Mark all ad special members as dirty since the user changed the ad special.
                    for (var index = _adSpecialIndex + 1; index < dataGridView.RowCount; index++)
                    {
                        //Add overflow protection
                        if (index < projectionsDataGridView.RowCount)
                        {
                            if (!projectionsDataGridView.Rows[index].IsNewRow)
                            {
                                projectionsDataGridView.Rows[index].Cells[(int) SalesTableColumns.IsDirty].Value = true;
                                projectionsDataGridView.Rows[index].HeaderCell.Style.BackColor =
                                    ApplicationColors.PendingEdit;
                            }
                        }
                        if (index < actualSalesDataGridView.RowCount)
                        {
                            if (!actualSalesDataGridView.Rows[index].IsNewRow)
                            {
                                actualSalesDataGridView.Rows[index].Cells[(int) SalesTableColumns.IsDirty].Value = true;
                                actualSalesDataGridView.Rows[index].HeaderCell.Style.BackColor =
                                    ApplicationColors.PendingEdit;
                            }
                        }
                        if (index < inventoryDataGridView.RowCount)
                        {
                            if (!inventoryDataGridView.Rows[index].IsNewRow)
                            {
                                inventoryDataGridView.Rows[index].Cells[(int) InventoryTableColumns.IsDirty].Value =
                                    true;
                                inventoryDataGridView.Rows[index].HeaderCell.Style.BackColor =
                                    ApplicationColors.PendingEdit;
                            }
                        }
                    }
                }
                else
                {
                    _usedAdItems[_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex ? 0 : 1].Remove(_beginningCellValue);
                    dataGridView.Rows[e.RowIndex].Cells[(int)SalesTableColumns.IsDirty].Value = true;                    
                }
            }
            else if (userInput != _beginningCellValue)
            {
                dataGridView.Rows[e.RowIndex].Cells[(int)SalesTableColumns.IsDirty].Value = true;
                //Set the coloring for the header cell.
                dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                _isFormDirty = true;
            }
            double parsedNumber;
            //Check to see if the current column is the ad item column.
            switch (e.ColumnIndex)
            {
                case (int)SalesTableColumns.AdItem: //Ad Item
                    //If there is text after all the whitespace has been cleared, clear the error text property regardless of whether or not it is set.
                    if (!string.IsNullOrEmpty(Regex.Replace(userInput, @"\s+", string.Empty)))
                    {
                        //Clear the error text since there is in fact an item entered.
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
                        //Send the ad item text through the formatting engine and assign the new value to the cell.
                        userInput = TextFormat.FormatAdItemText(userInput);
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = userInput;
                        var parser = new RowParsing();
                        if (parser.CheckForGroupKeyWord(userInput) == "NoGroupFound")
                        {
                            dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                            _isFormDirty = true;
                        }
                        else
                        {
                            //Check to see if the ad special index has changed.
                            if (_adSpecialIndex == -1)
                            {
                                //Check to see if there is an ID in the row and warn the user about the row deletion.
                                if (
                                    dataGridView.Rows[e.RowIndex].Cells[(int) SalesTableColumns.Id].EditedFormattedValue
                                        .ToString() != string.Empty)
                                {
                                    var result =
                                        MessageBox.Show(
                                            @"Performing this operation will clear '" + _beginningCellValue +
                                            @"' from the database. Do you wish to continue?",
                                            @"Delete " + _beginningCellValue + @" Permanently", MessageBoxButtons.YesNo,
                                            MessageBoxIcon.Information);

                                    if (result == DialogResult.Yes)
                                    {
                                        var dbT = new DatabaseTracker();
                                        var dbW = new DatabaseWriter(dbT.DatabaseConnectionString);
                                        if (!dbW.DeleteApcRow(
                                            int.Parse(
                                                projectionsDataGridView.Rows[e.RowIndex].Cells[
                                                    (int) SalesTableColumns.Id].EditedFormattedValue.ToString()),
                                            int.Parse(
                                                inventoryDataGridView.Rows[e.RowIndex].Cells[
                                                    (int) InventoryTableColumns.Id].EditedFormattedValue.ToString()),
                                            int.Parse(
                                                actualSalesDataGridView.Rows[e.RowIndex].Cells[
                                                    (int) SalesTableColumns.Id].EditedFormattedValue.ToString())))
                                        {
                                            //Error out, it failed to be cleared.
                                            MessageBox.Show(
                                                @"Failed to delete '" + _beginningCellValue +
                                                @"' from the database, cannot replace ad item with " + userInput + @"'.",
                                                @"Failed to Delete Row", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                            e.Cancel = true;
                                            return;
                                        }
                                        //Update the color coding of the other tables.
                                        var helperClass = new AdvertisingProfitControlTableHelper();
                                        helperClass.PaintRowGroupsFromIndex(e.RowIndex - 1, projectionsDataGridView);
                                        if (inventoryDataGridView.RowCount > e.RowIndex)
                                        {
                                            inventoryDataGridView.Rows[e.RowIndex].Cells[(int) InventoryTableColumns.AdItem].Value = userInput;
                                        }
                                        helperClass.PaintRowGroupsFromIndex(e.RowIndex - 1, inventoryDataGridView);
                                        if (actualSalesDataGridView.RowCount > e.RowIndex)
                                        {
                                            actualSalesDataGridView.Rows[e.RowIndex].Cells[(int) SalesTableColumns.AdItem].Value = userInput;
                                        }
                                        helperClass.PaintRowGroupsFromIndex(e.RowIndex - 1, actualSalesDataGridView);
                                    }
                                    else
                                    {
                                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _beginningCellValue;
                                        dataGridView.RefreshEdit();
                                        e.Cancel = true;
                                        return;
                                    }
                                }
                                //Flag the rows under the changed row as dirty.
                                FlagRowsAsDirty(dataGridView, e.RowIndex + 1);
                                //_beginningCellValue has the ad item that was destroyed.
                                _usedAdItems[0].Remove(_beginningCellValue);
                                //Now update the UsedAdItemCollection
                                for (var i = e.RowIndex + 1; i < _usedAdItems[0].Count; i++)
                                {
                                    //Wow this seems to work... well one shot baby!
                                    _usedAdItems[1].Add(_usedAdItems[0][i]);
                                    _usedAdItems[0].RemoveAt(e.RowIndex + 1);
                                }
                                //Mark row as Ad Special.
                                projectionsDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor =
                                    ApplicationColors.AdSpecial;
                                projectionsDataGridView.Rows[e.RowIndex].Cells[(int) SalesTableColumns.IsDirty].Value =
                                    false;
                                inventoryDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor =
                                    ApplicationColors.AdSpecial;
                                inventoryDataGridView.Rows[e.RowIndex].Cells[(int) InventoryTableColumns.IsDirty].Value
                                    = false;
                                actualSalesDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor =
                                    ApplicationColors.AdSpecial;
                                actualSalesDataGridView.Rows[e.RowIndex].Cells[(int) SalesTableColumns.IsDirty].Value =
                                    false;
                                ClearRow(e.RowIndex);
                                _adSpecialIndex = e.RowIndex;
                                //Since this is the ad special row don't give it any color coding.
                                dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
                            }
                        }
                        //Force a refresh so the cell's text updates and displays for the user.
                        dataGridView.RefreshEdit();
                        return;
                    }
                    //Otherwise, show an error.
                    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "This column can't be blank!";
                    break;
                case (int)SalesTableColumns.Sold: //Sold
                    //This Reg-ex pattern will match any number followed by the word bin(s), to allow specifying the number of bins of product were ordered.
                    var inventoryStringCheck = new Regex(@"^[0-9]{1,2} \bbin(s){0,1}\b", RegexOptions.IgnoreCase);

                    if (
                        inventoryStringCheck.IsMatch(
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString()))
                    {
                        //IF a match has been found, then make sure the word 'bin(s)' is capitalized to keep things looking pretty.
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = textInfo.ToTitleCase(userInput);
                        dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                        _isFormDirty = true;
                        dataGridView.RefreshEdit();
                        return;
                    }
                    //Try parsing the text entered as a number and if that fails then break out and clear the value entered.
                    if (double.TryParse(userInput, out parsedNumber))
                    {
                        //Add the formatted value to the cell.
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
                        dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                        _isFormDirty = true;
                    }
                    else
                    {
                        if (userInput == string.Empty)
                        {
                            return;
                        }
                        MessageBox.Show(@"Non numeric characters in column " + (e.ColumnIndex + 1) + @" are not allowed.", @"Invalid Characters Detected");
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                        dataGridView.RefreshEdit();
                        e.Cancel = true;
                        return;
                    }
                    break;
                case (int)SalesTableColumns.SalePrice:
                    //If the column is the "Sale Price" column try to parse the contents to a Double and apply number formatting to the contents.
                    //Check for the cost column to see if there are any strings formatted like such:
                    var regExpression = new Regex(@"^\d+( *)?/( *)?\${0,1}?\d+(\.\d+)?", RegexOptions.IgnoreCase); // [0-9]/($)?[0-9]
                    //IF the current cell is in the sale price column, check for the string format above, else move to the default method.
                    if (regExpression.IsMatch(dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString()))
                    {
                        //Grab the input and split it at the forward slash (/) for formatting.
                        var input = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
                        input = input.Replace(" ", string.Empty);
                        //Remove any dollar signs as these cause errors.
                        input = input.Replace("$", string.Empty);
                        var stringArray = input.Split('/');
                        //Format the last number as Currency, and round it up if necessary.
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value =
                            $@"{stringArray[0]}/{Math.Round(decimal.Parse(stringArray[1]), 2):C}";
                        dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                        _isFormDirty = true;
                        //Always refresh edit so the new value shows up to the user.
                        dataGridView.RefreshEdit();
                        return; //And return, there is no need to go further.
                    }
                    //Try parsing the text entered as a number and if that fails then break out and clear the value entered.
                    if (double.TryParse(userInput, out parsedNumber))
                    {
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
                        dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                        _isFormDirty = true;
                    }
                    else
                    {
                        if (userInput == string.Empty)
                        {
                            return;
                        }
                        MessageBox.Show(@"Non numeric characters in column " + (e.ColumnIndex + 1) + @" are not allowed.", @"Invalid Characters Detected");
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                        dataGridView.RefreshEdit();
                        e.Cancel = true;
                        return;
                    }
                    break;
                default:
                    //Purely here for protection against parsing the Boolean columns by mistake.
                    if (e.ColumnIndex == (int)SalesTableColumns.Id || e.ColumnIndex >= (int)SalesTableColumns.IsHeaderRow) { return; }
                    //If the column is any other then check to see if the entered value can be parsed to a double (is a number), if not then throw an error to the user.
                    if (double.TryParse(userInput, out parsedNumber))
                    {
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
                        dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                        _isFormDirty = true;
                    }
                    else
                    {
                        if (userInput == string.Empty)
                        {
                            return;
                        }
                        MessageBox.Show(@"Non numeric characters in column " + (e.ColumnIndex + 1) + @" are not allowed.", @"Invalid Characters Detected");
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                        dataGridView.RefreshEdit();
                        e.Cancel = true;
                        return;
                    }
                    break;
            }
            //Always refresh edit so the new value shows up to the user.
            dataGridView.RefreshEdit();
        }

        #endregion

        #region Projected Sales DataGridView Events

        /// <summary>
        /// Event Used: RowValidating
        /// Checks to make sure the row is valid (has an ad item) and
        /// then copies the contents where possible over to the inventory
        /// and actual sales DataGridViews.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateProjectedRow(object sender, DataGridViewCellCancelEventArgs e)
        {
            //Grab the index of the ad item, assuming the Sales tables and the Inventory table stay in the same position.
            const int adItemIndex = (int)SalesTableColumns.AdItem;
            //Do not even attempt anything since this is a new row and nothing to worry about.
            if (projectionsDataGridView.Rows[e.RowIndex].IsNewRow)
            {
                return;
            }
            //Clear all whitespace and check for a null value in the ad item column.
            if (Regex.Replace(projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString(), @"\s+", string.Empty) == string.Empty)
            {
                projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.RowError;
                MessageBox.Show(@"An ad item is required.", @"No Ad Item Specified");
                projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Selected = true;
                e.Cancel = true;
            }
            //Check to see if the other tables have more then one additional row. If the tables have two or more rows than the Projections table then that means
            //the tables have become unbalanced and need to be repaired. If the difference is only one row than that means the user is simply adding this row as
            //a new entry to the Projections table.
            if (projectionsDataGridView.RowCount - actualSalesDataGridView.RowCount > 1 || projectionsDataGridView.RowCount - inventoryDataGridView.RowCount > 1)
            {
                NormalizeApcTables();
                return;
            }
            //Check to see if the user left a row that already exists and doesn't require being copied over.
            if (projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() == actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
            {
                return;
            }
            _isFormDirty = true;
            //Next check to see if the user changed the ad item is the corresponding row.
            if (projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() != actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
            {
                if (projectionsDataGridView.RowCount == actualSalesDataGridView.RowCount)
                {
                    var adItemText = projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString();
                    actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
                    if (e.RowIndex != _adSpecialIndex)
                    {
                        actualSalesDataGridView.Rows[e.RowIndex].Cells[(int) SalesTableColumns.IsDirty].Value = true;
                        actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;                        
                    }
                    inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
                    if (e.RowIndex != _adSpecialIndex)
                    {
                        inventoryDataGridView.Rows[e.RowIndex].Cells[(int) InventoryTableColumns.IsDirty].Value = true;
                        inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;                        
                    }
                    projectionsDataGridView.RefreshEdit();
                    //Remove the last ad item from the UsedAdItem array, the new one will be added in the OnRowLeave event handler.
                    if (_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex)
                    {
                        _usedAdItems[0].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
                    }
                    else
                    {
                        _usedAdItems[1].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
                    }

                    return;
                }
            }
            var rowContents = new object[projectionsDataGridView.ColumnCount];
            //Spin through the DataGridViewCells in the row and add their contents to an array.
            for (var i = 0; i < projectionsDataGridView.ColumnCount; i++)
            {
                //Grab the Ad Item in the first cell and add it into the array.
                switch (i)
                {
                    case (int)SalesTableColumns.AdItem:
                        rowContents[i] = projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.SalePrice:
                        rowContents[i] = projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.Cost:
                        rowContents[i] = projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.ProfitReturn:
                        rowContents[i] = projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.IsDirty:
                        rowContents[i] = true;
                        break;
                    case (int)SalesTableColumns.IsHeaderRow:
                        rowContents[i] = false;
                        break;
                    case (int)SalesTableColumns.IsMemberRow:
                        rowContents[i] = false;
                        break;
                    default:
                        rowContents[i] = string.Empty;
                        break;
                }
            }
            actualSalesDataGridView.Rows.Add(rowContents);
            actualSalesDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
            //Build a collection of objects for the inventory table to use.
            var inventoryNewRow = new object[inventoryDataGridView.ColumnCount];
            //Spin through the DataGridViewCells in the row and add their contents to an array.
            for (var i = 0; i < projectionsDataGridView.ColumnCount; i++)
            {
                //Grab the Ad Item in the first cell and add it into the array.
                switch (i)
                {
                    case (int)SalesTableColumns.Id:
                        inventoryNewRow[(int)InventoryTableColumns.Id] = string.Empty;
                        break;
                    case (int)SalesTableColumns.AdItem:
                        inventoryNewRow[(int)InventoryTableColumns.AdItem] =
                            projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)InventoryTableColumns.IsDirty:
                        inventoryNewRow[(int)InventoryTableColumns.IsDirty] = true;
                        break;
                    case (int)InventoryTableColumns.IsHeaderRow:
                        inventoryNewRow[(int)InventoryTableColumns.IsHeaderRow] = false;
                        break;
                    case (int)InventoryTableColumns.IsMemberRow:
                        inventoryNewRow[(int)InventoryTableColumns.IsMemberRow] = false;
                        break;
                    default:
                        if (i > (int)InventoryTableColumns.IsHeaderRow) continue;
                        inventoryNewRow[i] = string.Empty;
                        break;
                }
            }
            inventoryDataGridView.Rows.Add(inventoryNewRow);
            inventoryDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
            //Set the row headers of the other tables to show up as pending; this row is valid without a doubt.
            if (e.RowIndex != _adSpecialIndex)
            {
                inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
            }
            else
            {
                inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
                actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
            }
        }

        private void ProjectionRowRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            var dataGridView = ((DataGridView)sender);
            //Provide protection against overflows
            if ((e.RowIndex + 1) > dataGridView.Rows.Count)
            {
                return;
            }
            //Disable all row removing events from the other two tables to prevent interference.
            inventoryDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
            inventoryDataGridView.RowsRemoved -= InventoryRowRemoved;

            actualSalesDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
            actualSalesDataGridView.RowsRemoved -= ActualSalesRowRemoved;

            //IF the row count on the passed DataGridView is less then the other table's row count...
            if (projectionsDataGridView.RowCount <= actualSalesDataGridView.RowCount && projectionsDataGridView.RowCount <= actualSalesDataGridView.RowCount)
            {
                //... it is lower so its safe to assume that both tables have the same row that can be removed.
                if (!actualSalesDataGridView.Rows[e.RowIndex].IsNewRow)
                {
                    actualSalesDataGridView.Rows.RemoveAt(e.RowIndex);
                }

                if (!inventoryDataGridView.Rows[e.RowIndex].IsNewRow)
                {
                    inventoryDataGridView.Rows.RemoveAt(e.RowIndex);
                }
            }
            //ELSE IF the row count is larger then the other table's row count...
            else
            {
                //... Log the error and then what?
                //TODO: Figure out if this is an error condition.
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "The Projections table has more rows then the Actual Sales table.");
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Projections Row Count: " + projectionsDataGridView.RowCount);
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Inventory Row Count: " + inventoryDataGridView.RowCount);
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Actual Sales Row Count: " + actualSalesDataGridView.RowCount);
                errorLabel.Text = @"Error removing rows from Actual Sales and Inventory.";
                NormalizeApcTables();
                return;
            }
            //... After all the row removal has been finished re-enable the row removal events.
            inventoryDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            inventoryDataGridView.RowsRemoved += InventoryRowRemoved;

            actualSalesDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;

            //Reset the row numbers in the tables.
            for (var i = e.RowIndex; i < (dataGridView.RowCount); i++)
            {
                projectionsDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
                inventoryDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
                actualSalesDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
            }
            //Refresh the DataGridViews so the header cell's number get repainted properly and repaint the rows in case any groups get messed up during row removal.
            projectionsDataGridView.RefreshEdit();
            inventoryDataGridView.RefreshEdit();
            actualSalesDataGridView.RefreshEdit();
            _tableHelperFunctions.PaintRowGroups(dataGridView);
        }

        #endregion

        #region Inventory DataGridView Events

        /// <summary>
        /// Event Used: CellValidating
        /// Validates the contents of the cell that the user is attempting to leave. Applies formatting
        /// to text as needed and prevents the user from leaving invalid cells.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateInventoryCellContents(object sender, DataGridViewCellValidatingEventArgs e)
        {
            //Grab the DataGirdView that fired the event and make it into a local variable.
            var dataGridView = ((DataGridView)sender);
            var userInput = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
            //TODO: Write a custom parsing engine for detecting when bins are entered.
            var textInfo = new CultureInfo("en-US", false).TextInfo;
            //Check for isNewRow if it is, return no need to check it for anything.
            if (dataGridView.Rows[e.RowIndex].IsNewRow)
            {
                return;
            }

            if (userInput == _beginningCellValue)
            {
                return;
            }
            //Check to make sure we're not in the boolean fields or the ID field.
            if (e.ColumnIndex >= (int)InventoryTableColumns.IsHeaderRow || e.ColumnIndex == (int)InventoryTableColumns.Id)
            {
                return;
            }
            //Cell validating gets to handle updating the used ad item list since it handles cells on by one, instead by a whole row.
            if (userInput != _beginningCellValue && e.ColumnIndex == (int)InventoryTableColumns.AdItem)
            {
                //The user is trying to change the ad special text to something else.
                if (e.RowIndex == _adSpecialIndex)
                {
                    var parser = new RowParsing();
                    if (parser.CheckForGroupKeyWord(userInput) == "NoGroupFound")
                    {
                        MessageBox.Show(
                            @"The Ad Special row's column one (1) cannot be changed. You must delete this row by clicking on the header cell and pressing 'Delete'.",
                            @"Invalid Operation on Ad Special Row", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _beginningCellValue;
                        dataGridView.RefreshEdit();
                        return;
                    }
                    //Mark all ad special members as dirty since the user changed the ad special.
                    for (var index = _adSpecialIndex + 1; index < dataGridView.RowCount; index++)
                    {
                        //Add overflow protection
                        if (index < projectionsDataGridView.RowCount)
                        {
                            if (!projectionsDataGridView.Rows[index].IsNewRow)
                            {
                                projectionsDataGridView.Rows[index].Cells[(int) SalesTableColumns.IsDirty].Value = true;
                                projectionsDataGridView.Rows[index].HeaderCell.Style.BackColor =
                                    ApplicationColors.PendingEdit;
                            }
                        }
                        if (index < actualSalesDataGridView.RowCount)
                        {
                            if (!actualSalesDataGridView.Rows[index].IsNewRow)
                            {
                                actualSalesDataGridView.Rows[index].Cells[(int) SalesTableColumns.IsDirty].Value = true;
                                actualSalesDataGridView.Rows[index].HeaderCell.Style.BackColor =
                                    ApplicationColors.PendingEdit;
                            }
                        }
                        if (index < inventoryDataGridView.RowCount)
                        {
                            if (!inventoryDataGridView.Rows[index].IsNewRow)
                            {
                                inventoryDataGridView.Rows[index].Cells[(int) InventoryTableColumns.IsDirty].Value =
                                    true;
                                inventoryDataGridView.Rows[index].HeaderCell.Style.BackColor =
                                    ApplicationColors.PendingEdit;
                            }
                        }
                    }
                }
                else
                {
                    _usedAdItems[_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex ? 0 : 1].Remove(_beginningCellValue);
                    dataGridView.Rows[e.RowIndex].Cells[(int)InventoryTableColumns.IsDirty].Value = true;
                }
            }
            else if (userInput != _beginningCellValue)
            {
                dataGridView.Rows[e.RowIndex].Cells[(int)InventoryTableColumns.IsDirty].Value = true;
            }
            //Check to see if the current column is the ad item column.
            switch (e.ColumnIndex)
            {
                case (int)InventoryTableColumns.AdItem:
                    //If there is text after all the whitespace has been cleared, clear the error text property regardless of whether or not it is set.
                    if (!string.IsNullOrEmpty(Regex.Replace(userInput, @"\s+", string.Empty)))
                    {
                        //Clear the error text since there is in fact an item entered.
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
                        //Send the ad item text through the formatting engine and assign the new value to the cell.
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = TextFormat.FormatAdItemText(userInput);
                        var parser = new RowParsing();
                        if (parser.CheckForGroupKeyWord(userInput) == "NoGroupFound")
                        {
                            dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                            _isFormDirty = true;
                        }
                        else
                        {
                            //Check to see if the ad special index has changed.
                            if (_adSpecialIndex == -1)
                            {
                                //Check to see if there is an ID in the row and warn the user about the row deletion.
                                if (dataGridView.Rows[e.RowIndex].Cells[(int)InventoryTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
                                {
                                    var result = MessageBox.Show(@"Performing this operation will clear '" + _beginningCellValue + @"' from the database. Do you wish to continue?", @"Delete " + _beginningCellValue + @" Permanently", MessageBoxButtons.YesNo, MessageBoxIcon.Information);

                                    if (result == DialogResult.Yes)
                                    {
                                        var dbT = new DatabaseTracker();
                                        var dbW = new DatabaseWriter(dbT.DatabaseConnectionString);
                                        if (!dbW.DeleteApcRow(
                                            int.Parse(
                                                projectionsDataGridView.Rows[e.RowIndex].Cells[
                                                    (int)SalesTableColumns.Id].EditedFormattedValue.ToString()),
                                            int.Parse(
                                                inventoryDataGridView.Rows[e.RowIndex].Cells[
                                                    (int)InventoryTableColumns.Id].EditedFormattedValue.ToString()),
                                            int.Parse(
                                                actualSalesDataGridView.Rows[e.RowIndex].Cells[
                                                    (int)SalesTableColumns.Id].EditedFormattedValue.ToString())))
                                        {
                                            //Error out, it failed to be cleared.
                                            MessageBox.Show(@"Failed to delete '" + _beginningCellValue + @"' from the database, cannot replace ad item with " + userInput + @"'.", @"Failed to Delete Row", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                            e.Cancel = true;
                                            return;
                                        }
                                        //Update the color coding of the other tables.
                                        var helperClass = new AdvertisingProfitControlTableHelper();
                                        helperClass.PaintRowGroupsFromIndex(e.RowIndex - 1, projectionsDataGridView);
                                        if (inventoryDataGridView.RowCount > e.RowIndex)
                                        {
                                            inventoryDataGridView.Rows[e.RowIndex].Cells[(int)InventoryTableColumns.AdItem].Value = userInput;
                                        }
                                        helperClass.PaintRowGroupsFromIndex(e.RowIndex - 1, inventoryDataGridView);
                                        if (actualSalesDataGridView.RowCount > e.RowIndex)
                                        {
                                            actualSalesDataGridView.Rows[e.RowIndex].Cells[(int)SalesTableColumns.AdItem].Value = userInput;
                                        }
                                        helperClass.PaintRowGroupsFromIndex(e.RowIndex - 1, actualSalesDataGridView);
                                    }
                                    else
                                    {
                                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _beginningCellValue;
                                        dataGridView.RefreshEdit();
                                        e.Cancel = true;
                                        return;
                                    }
                                }
                                //Flag the rows under the changed row as dirty.
                                FlagRowsAsDirty(dataGridView, e.RowIndex + 1);
                                //_beginningCellValue has the ad item that was destroyed.
                                _usedAdItems[0].Remove(_beginningCellValue);
                                //Now update the UsedAdItemCollection
                                for (var i = e.RowIndex + 1; i < _usedAdItems[0].Count; i++)
                                {
                                    //Wow this seems to work... well one shot baby!
                                    _usedAdItems[1].Add(_usedAdItems[0][i]);
                                    _usedAdItems[0].RemoveAt(e.RowIndex + 1);
                                }
                                //Mark row as Ad Special.
                                projectionsDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor = ApplicationColors.AdSpecial;
                                projectionsDataGridView.Rows[e.RowIndex].Cells[(int)SalesTableColumns.IsDirty].Value = false;
                                inventoryDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor = ApplicationColors.AdSpecial;
                                inventoryDataGridView.Rows[e.RowIndex].Cells[(int)InventoryTableColumns.IsDirty].Value = false;
                                actualSalesDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor = ApplicationColors.AdSpecial;
                                actualSalesDataGridView.Rows[e.RowIndex].Cells[(int)SalesTableColumns.IsDirty].Value = false;
                                ClearRow(e.RowIndex);
                                _adSpecialIndex = e.RowIndex;
                                //Since this is the ad special row don't give it any color coding.
                                dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
                            }
                        }
                        //Force a refresh so the cell's text updates and displays for the user.
                        dataGridView.RefreshEdit();
                        return;
                    }
                    //If column one (1) is blank then cancel cell validating.
                    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "This column can't be blank!";
                    break;
                default:
                    //Purely here for protection against parsing the Boolean columns by mistake.
                    if (e.ColumnIndex == (int)InventoryTableColumns.Id || e.ColumnIndex >= (int)InventoryTableColumns.IsHeaderRow) { return; }
                    if (userInput == string.Empty)
                    {
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = userInput;
                        return;
                    }
                    //This Reg-ex pattern will match any number followed by the word bin(s), to allow specifying the number of bins of product were ordered.
                    var inventoryStringCheck = new Regex(@"^[0-9]{1,2} \bbin(s){0,1}\b", RegexOptions.IgnoreCase);

                    if (
                        inventoryStringCheck.IsMatch(
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString()))
                    {
                        //IF a match has been found, then make sure the word 'bin(s)' is capitalized to keep things looking pretty.
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = textInfo.ToTitleCase(userInput);
                        dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                        _isFormDirty = true;
                        dataGridView.RefreshEdit();
                        return;
                    }
                    double parsedNumber;
                    //Try parsing the text entered as a number and if that fails then break out and clear the value entered.
                    if (userInput != string.Empty && double.TryParse(userInput, out parsedNumber))
                    {
                        //Add the value to the cell.
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = userInput;
                        dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                        _isFormDirty = true;
                        return;
                    }
                    //Prevent the user from being bombarded by message boxes. All validation has been completed at this point so there's nothing to worry about.
                    if (userInput != string.Empty)
                    {
                        MessageBox.Show(@"Non numeric characters in column " + (e.ColumnIndex + 1) + @" are not allowed.", @"Invalid Characters Detected");
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
                        dataGridView.RefreshEdit();
                        e.Cancel = true;
                    }
                    break;
            }
            //Always refresh edit so the new value shows up to the user.
            dataGridView.RefreshEdit();
        }

        /// <summary>
        /// Event Used: RowValidating
        /// Checks to make sure the row is valid (has an ad item) and
        /// then copies the contents where possible over to the projections
        /// and actual sales DataGridViews.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateInventoryRow(object sender, DataGridViewCellCancelEventArgs e)
        {
            //Grab the index of the ad item, assuming the Sales tables and the Inventory table stay in the same position.
            const int adItemIndex = (int)InventoryTableColumns.AdItem;
            //Do not even attempt anything since this is a new row and nothing to worry about.
            if (inventoryDataGridView.Rows[e.RowIndex].IsNewRow)
            {
                return;
            }
            //Clear all whitespace and check for a null value in the ad item column.
            if (Regex.Replace(inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString(), @"\s+", string.Empty) == string.Empty)
            {
                inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.RowError;
                MessageBox.Show(@"An ad item is required.", @"No Ad Item Specified");
                inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Selected = true;
                e.Cancel = true;
            }
            //Check to see if the other tables have more then one additional row. If the tables have two or more rows than the Inventory table then that means
            //the tables have become unbalanced and need to be repaired. If the difference is only one row than that means the user is simply adding this row as
            //a new entry to the Inventory table.
            if (inventoryDataGridView.RowCount - projectionsDataGridView.RowCount > 1 || inventoryDataGridView.RowCount - actualSalesDataGridView.RowCount > 1)
            {
                NormalizeApcTables();
                return;
            }
            //Check to see if the user left a row that already exists and doesn't require being copied over.
            if (inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() == actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
            {
                return;
            }
            _isFormDirty = true;
            //Next check to see if the user changed the ad item is the corresponding row.
            if (inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() != actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
            {
                if (inventoryDataGridView.RowCount == actualSalesDataGridView.RowCount)
                {
                    var adItemText = inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString();
                    projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
                    if (e.RowIndex != _adSpecialIndex)
                    {
                        projectionsDataGridView.Rows[e.RowIndex].Cells[(int) SalesTableColumns.IsDirty].Value = true;
                        projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;                        
                    }
                    actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
                    if (e.RowIndex != _adSpecialIndex)
                    {
                        actualSalesDataGridView.Rows[e.RowIndex].Cells[(int) SalesTableColumns.IsDirty].Value = true;
                        actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;                        
                    }
                    //Remove the last ad item from the UsedAdItem array, the new one will be added in the OnRowLeave event handler.
                    if (_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex)
                    {
                        _usedAdItems[0].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
                    }
                    else
                    {
                        _usedAdItems[1].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
                    }

                    return;
                }
            }
            var rowContents = new object[actualSalesDataGridView.ColumnCount];
            //Spin through the DataGridViewCells in the row and add their contents to an array.
            for (var i = 0; i < actualSalesDataGridView.ColumnCount; i++)
            {
                //Grab the Ad Item in the first cell and add it into the array.
                switch (i)
                {
                    case (int)SalesTableColumns.AdItem:
                        rowContents[i] = inventoryDataGridView.Rows[e.RowIndex].Cells[(int)InventoryTableColumns.AdItem].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.IsDirty:
                        rowContents[i] = true;
                        break;
                    case (int)SalesTableColumns.IsHeaderRow:
                        rowContents[i] = false;
                        break;
                    case (int)SalesTableColumns.IsMemberRow:
                        rowContents[i] = false;
                        break;
                    default:
                        rowContents[i] = string.Empty;
                        break;
                }
            }
            projectionsDataGridView.Rows.Add(rowContents);
            projectionsDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
            actualSalesDataGridView.Rows.Add(rowContents);
            actualSalesDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
            if (e.RowIndex != _adSpecialIndex)
            {
                //Apply color coding to the respective row headers on the other tables.
                projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
            }
            else
            {
                //Apply color coding to the respective row headers on the other tables.
                projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
                actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
            }
        }

        private void InventoryRowRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            var dataGridView = ((DataGridView)sender);
            //Provide protection against overflows
            if ((e.RowIndex + 1) > dataGridView.Rows.Count)
            {
                return;
            }
            //Disable all row removing events from the other two tables to prevent interference.
            projectionsDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
            projectionsDataGridView.RowsRemoved -= ProjectionRowRemoved;

            actualSalesDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
            actualSalesDataGridView.RowsRemoved -= ActualSalesRowRemoved;

            //IF the row count on the passed DataGridView is less then the other table's row count...
            if (inventoryDataGridView.RowCount <= projectionsDataGridView.RowCount && inventoryDataGridView.RowCount <= actualSalesDataGridView.RowCount)
            {
                //... it is lower so its safe to assume that both tables have the same row that can be removed.
                if (!projectionsDataGridView.Rows[e.RowIndex].IsNewRow)
                {
                    projectionsDataGridView.Rows.RemoveAt(e.RowIndex);
                }

                if (!actualSalesDataGridView.Rows[e.RowIndex].IsNewRow)
                {
                    actualSalesDataGridView.Rows.RemoveAt(e.RowIndex);
                }
            }
            //ELSE IF the row count is larger then the other table's row count...
            else
            {
                //... Log the error and then what?
                //TODO: Figure out if this is an error condition.
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "The Inventory table has more rows then the Actual Sales table.");
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Projections Row Count: " + projectionsDataGridView.RowCount);
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Inventory Row Count: " + inventoryDataGridView.RowCount);
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Actual Sales Row Count: " + actualSalesDataGridView.RowCount);
                errorLabel.Text = @"Error removing rows from Actual Sales and Inventory.";
                NormalizeApcTables();
                return;
            }
            //... After all the row removal has been finished re-enable the row removal events.
            projectionsDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;

            actualSalesDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;

            //Reset the row numbers in each table.
            for (var i = e.RowIndex; i < (dataGridView.RowCount); i++)
            {
                projectionsDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
                inventoryDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
                actualSalesDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
            }
            //Refresh the DataGridViews so the header cell's number get repainted properly and repaint the rows in case any groups get messed up during row removal.
            projectionsDataGridView.RefreshEdit();
            inventoryDataGridView.RefreshEdit();
            actualSalesDataGridView.RefreshEdit();
            _tableHelperFunctions.PaintRowGroups(dataGridView);
        }

        #endregion

        #region Actual Sales DataGridView Events

        /// <summary>
        /// Event Used: RowValidating
        /// Checks to make sure the row is valid (has an ad item) and
        /// then copies the contents where possible over to the inventory
        /// and actual sales DataGridViews.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateActualSalesRow(object sender, DataGridViewCellCancelEventArgs e)
        {
            //Grab the index of the ad item, assuming the Sales tables and the Inventory table stay in the same position.
            const int adItemIndex = (int)SalesTableColumns.AdItem;
            //Do not even attempt anything since this is a new row and nothing to worry about.
            if (actualSalesDataGridView.Rows[e.RowIndex].IsNewRow)
            {
                return;
            }
            //Clear all whitespace and check for a null value in the ad item column.
            if (Regex.Replace(actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString(), @"\s+", string.Empty) == string.Empty)
            {
                actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.RowError;
                MessageBox.Show(@"An ad item is required.", @"No Ad Item Specified");
                actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Selected = true;
                e.Cancel = true;
            }
            //Check to see if the other tables have more then one additional row. If the tables have two or more rows than the Actual Sales table then that means
            //the tables have become unbalanced and need to be repaired. If the difference is only one row than that means the user is simply adding this row as
            //a new entry to the Actual Sales table.
            if (actualSalesDataGridView.RowCount - projectionsDataGridView.RowCount > 1 || actualSalesDataGridView.RowCount - inventoryDataGridView.RowCount > 1)
            {
                NormalizeApcTables();
                return;
            }
            //Check to see if the user left a row that already exists and doesn't require being copied over.
            if (actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() == projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
            {
                return;
            }
            _isFormDirty = true;
            //Next check to see if the user changed the ad item is the corresponding row.
            if (actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() != projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
            {
                if (actualSalesDataGridView.RowCount == projectionsDataGridView.RowCount)
                {
                    var adItemText = actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString();
                    projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
                    if (e.RowIndex != _adSpecialIndex)
                    {
                        projectionsDataGridView.Rows[e.RowIndex].Cells[(int) SalesTableColumns.IsDirty].Value = true;
                        projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;                        
                    }
                    inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
                    if (e.RowIndex != _adSpecialIndex)
                    {
                        inventoryDataGridView.Rows[e.RowIndex].Cells[(int) InventoryTableColumns.IsDirty].Value = true;
                        inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;                        
                    }
                    //projectionsDataGridView.RefreshEdit();
                    //Remove the last ad item from the UsedAdItem array, the new one will be added in the OnRowLeave event handler.
                    if (_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex)
                    {
                        _usedAdItems[0].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
                    }
                    else
                    {
                        _usedAdItems[1].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
                    }

                    return;
                }
            }
            var rowContents = new object[actualSalesDataGridView.ColumnCount];
            //Spin through the DataGridViewCells in the row and add their contents to an array.
            for (var i = 0; i < actualSalesDataGridView.ColumnCount; i++)
            {
                //Grab the Ad Item in the first cell and add it into the array.
                switch (i)
                {
                    case (int)SalesTableColumns.AdItem:
                        rowContents[i] = actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.SalePrice:
                        rowContents[i] = actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.Cost:
                        rowContents[i] = actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.ProfitReturn:
                        rowContents[i] = actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.IsDirty:
                        rowContents[i] = true;
                        break;
                    case (int)SalesTableColumns.IsHeaderRow:
                        rowContents[i] = false;
                        break;
                    case (int)SalesTableColumns.IsMemberRow:
                        rowContents[i] = false;
                        break;
                    default:
                        rowContents[i] = string.Empty;
                        break;
                }
            }
            projectionsDataGridView.Rows.Add(rowContents);
            projectionsDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
            //Build a collection of objects for the inventory table to use.
            var inventoryNewRow = new object[inventoryDataGridView.ColumnCount];
            //Spin through the DataGridViewCells in the row and add their contents to an array.
            for (var i = 0; i < actualSalesDataGridView.ColumnCount; i++)
            {
                //Grab the Ad Item in the first cell and add it into the array.
                switch (i)
                {
                    case (int)SalesTableColumns.AdItem:
                        inventoryNewRow[(int)InventoryTableColumns.AdItem] =
                            actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
                        break;
                    case (int)SalesTableColumns.IsDirty:
                        inventoryNewRow[(int)InventoryTableColumns.IsDirty] = true;
                        break;
                    case (int)InventoryTableColumns.IsHeaderRow:
                        inventoryNewRow[(int)InventoryTableColumns.IsHeaderRow] = false;
                        break;
                    case (int)InventoryTableColumns.IsMemberRow:
                        inventoryNewRow[(int)InventoryTableColumns.IsMemberRow] = false;
                        break;
                    default:
                        if (i > (int)InventoryTableColumns.IsHeaderRow) continue;
                        inventoryNewRow[i] = string.Empty;
                        break;
                }
            }
            inventoryDataGridView.Rows.Add(inventoryNewRow);
            inventoryDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
            if (e.RowIndex != _adSpecialIndex)
            {
                //Apply color coding to the respective row headers on the other tables.
                projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
            }
            else
            {
                //Apply color coding to the respective row headers on the other tables.
                projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
                inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
            }
        }

        private void ActualSalesRowRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            var dataGridView = ((DataGridView)sender);
            //Provide protection against overflows
            if ((e.RowIndex + 1) > dataGridView.Rows.Count)
            {
                return;
            }
            //Disable all row removing events from the other two tables to prevent interference.
            projectionsDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
            projectionsDataGridView.RowsRemoved -= ProjectionRowRemoved;

            inventoryDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
            inventoryDataGridView.RowsRemoved -= InventoryRowRemoved;

            //IF the row count on the passed DataGridView is less then the other table's row count...
            if (actualSalesDataGridView.RowCount <= projectionsDataGridView.RowCount && actualSalesDataGridView.RowCount <= inventoryDataGridView.RowCount)
            {
                //... it is lower so its safe to assume that both tables have the same row that can be removed.
                if (!projectionsDataGridView.Rows[e.RowIndex].IsNewRow)
                {
                    projectionsDataGridView.Rows.RemoveAt(e.RowIndex);
                }

                if (!inventoryDataGridView.Rows[e.RowIndex].IsNewRow)
                {
                    inventoryDataGridView.Rows.RemoveAt(e.RowIndex);
                }
            }
            //ELSE IF the row count is larger then the other table's row count...
            else
            {
                //... Log the error and then what?
                //TODO: Figure out if this is an error condition.
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "The Actual Sales table has more rows then the Projections table.");
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Projections Row Count: " + projectionsDataGridView.RowCount);
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Inventory Row Count: " + inventoryDataGridView.RowCount);
                LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Actual Sales Row Count: " + actualSalesDataGridView.RowCount);
                errorLabel.Text = @"Error removing rows from Actual Sales and Inventory.";
                NormalizeApcTables();
                return;
            }
            //... After all the row removal has been finished re-enable the row removal events.
            projectionsDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;

            inventoryDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
            inventoryDataGridView.RowsRemoved += InventoryRowRemoved;

            //Reset the row numbers in the tables.
            for (var i = e.RowIndex; i < (dataGridView.RowCount); i++)
            {
                projectionsDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
                inventoryDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
                actualSalesDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
            }
            //Refresh the DataGridViews so the header cell's number get repainted properly and repaint the rows in case any groups get messed up during row removal.
            projectionsDataGridView.RefreshEdit();
            inventoryDataGridView.RefreshEdit();
            actualSalesDataGridView.RefreshEdit();
            _tableHelperFunctions.PaintRowGroups(dataGridView);
        }

        #endregion

        #region DataGridView Construction Functions

        /// <summary>
        /// Fills the APC DataGridViews with the appropriate columns and starting row for the user to start
        /// entering data.
        /// </summary>
        private void ConstructApcDataGridViews()
        {
            //Construct a list of column names for the projections/actual sales DataGridViews and the inventory DataGirdView.
            string[] saleColumnNames =
            {
                "ID", "AdItem", "Sold", "SalePrice", "TotalSales", "Cost", "ProfitReturn",
                "TotalProfitReturn", "IsHeader", "IsMember", "IsDirty"
            };
            string[] inventoryColumnNames =
            {
                "ID", "AdItem", "BeginningInventory", "Received", "Total", "EndingInventory", "IsHeader", "IsMember", "IsDirty"
            };

            foreach (var name in saleColumnNames)
            {
                if (!name.StartsWith("Is"))
                {
                    var column = new DataGridViewTextBoxColumn
                    {
                        Name = name,
                        HeaderText = TextFormat.AddSpacesToSentence(name, false),
                        ValueType = typeof(string),
                        SortMode = DataGridViewColumnSortMode.NotSortable,
                        MaxInputLength = 32
                    };
                    if (name.Contains("ID"))
                    {
                        column.Visible = false;
                    }
                    if (name == "AdItem")
                    {
                        column.MaxInputLength = 64;
                    }
                    projectionsDataGridView.Columns.Add(column);
                }
                else
                {
                    var column = new DataGridViewCheckBoxColumn
                    {
                        Name = name,
                        HeaderText = TextFormat.AddSpacesToSentence(name, false),
                        ValueType = typeof(bool),
                        Visible = false,
                        SortMode = DataGridViewColumnSortMode.NotSortable
                    };
                    projectionsDataGridView.Columns.Add(column);
                }
            }
            //Add the columns into the inventory DataGridView after setting their types.
            foreach (var name in inventoryColumnNames)
            {
                if (!name.StartsWith("Is"))
                {
                    var column = new DataGridViewTextBoxColumn
                    {
                        Name = name,
                        HeaderText = TextFormat.AddSpacesToSentence(name, false),
                        ValueType = typeof(string),
                        SortMode = DataGridViewColumnSortMode.NotSortable,
                        MaxInputLength = 32
                    };
                    if (name.Contains("ID"))
                    {
                        column.Visible = false;
                    }
                    if (name == "AdItem")
                    {
                        column.MaxInputLength = 64;
                    }
                    inventoryDataGridView.Columns.Add(column);
                }
                else
                {
                    var column = new DataGridViewCheckBoxColumn
                    {
                        Name = name,
                        HeaderText = TextFormat.AddSpacesToSentence(name, false),
                        ValueType = typeof(bool),
                        Visible = false,
                        SortMode = DataGridViewColumnSortMode.NotSortable
                    };
                    inventoryDataGridView.Columns.Add(column);
                }
            }
            //
            foreach (var name in saleColumnNames)
            {
                if (!name.StartsWith("Is"))
                {
                    var column = new DataGridViewTextBoxColumn
                    {
                        Name = name,
                        HeaderText = TextFormat.AddSpacesToSentence(name, false),
                        ValueType = typeof(string),
                        SortMode = DataGridViewColumnSortMode.NotSortable,
                        MaxInputLength = 32
                    };
                    if (name.Contains("ID"))
                    {
                        column.Visible = false;
                    }
                    if (name == "AdItem")
                    {
                        column.MaxInputLength = 64;
                    }
                    actualSalesDataGridView.Columns.Add(column);
                }
                else
                {
                    var column = new DataGridViewCheckBoxColumn
                    {
                        Name = name,
                        HeaderText = TextFormat.AddSpacesToSentence(name, false),
                        ValueType = typeof(bool),
                        Visible = false,
                        SortMode = DataGridViewColumnSortMode.NotSortable
                    };
                    actualSalesDataGridView.Columns.Add(column);
                }
            }
        }

        /// <summary>
        /// Constructs the invoices DataGridView.
        /// </summary>
        private void ConstructInvoicesDataGridView()
        {
            string[] invoicesColumnNames = { "ID", "InvoiceDate", "Supplier", "InvoiceNumber", "InvoiceNetAmountAtCost", "InvoiceNetAmountExtendedRetail", "InvoiceNote", "IsDirty" };

            foreach (var name in invoicesColumnNames)
            {
                if (!name.StartsWith("Is"))
                {
                    var column = new DataGridViewTextBoxColumn
                    {
                        Name = name,
                        HeaderText = TextFormat.AddSpacesToSentence(name, false),
                        ValueType = typeof(string),
                        SortMode = DataGridViewColumnSortMode.NotSortable,
                        MaxInputLength = 32
                    };
                    if (name.Contains("ID"))
                    {
                        column.Visible = false;
                    }
                    if (name == "Supplier")
                    {
                        column.MaxInputLength = 64;
                    }
                    invoicesDataGridView.Columns.Add(column);
                }
                else
                {
                    var column = new DataGridViewCheckBoxColumn
                    {
                        Name = name,
                        ValueType = typeof(bool),
                        Visible = false,
                        SortMode = DataGridViewColumnSortMode.NotSortable
                    };
                    invoicesDataGridView.Columns.Add(column);
                }
            }
        }

        #endregion

        #region Comments TextBox Events

        /// <summary>
        /// Allows the user to select all the text in the comments text box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CheckForKeyCommand(object sender, KeyEventArgs e)
        {
            if (!e.Control || e.KeyCode != Keys.A) return;
            commentsTextBox.SelectionStart = 0;
            commentsTextBox.SelectionLength = commentsTextBox.Text.Length;
            e.Handled = true;
            e.SuppressKeyPress = true;
        }

        /// <summary>
        /// Event Used: TextChanged
        /// Calculates and displays the remaining number of characters available for the user to enter
        /// and changes the color of the label displaying the character count to red when 20% or less characters remain.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DisplayRemainingCommentCharacterCount(object sender, EventArgs e)
        {
            //If the amount of characters left is less then 20% (or 80% or more characters have been used) then color the label red, otherwise color it its default color.
            commentsGroupBox.ForeColor = (double)commentsTextBox.TextLength / commentsTextBox.MaxLength < .8 ? default(Color) : Color.DarkRed;
            commentsGroupBox.Text = @"Comments (Characters Remaining: " + (commentsTextBox.MaxLength - commentsTextBox.TextLength) + @")";
        }

        private void CheckForTextChangeOnLeave(object sender, EventArgs e)
        {
            if (commentsTextBox.Text == _beginningCellValue) return;
            //Clear white spaces and check if the string is null.
            if (commentsTextBox.Text.Trim() == string.Empty && isCommentDirtyCheckBox.Tag == null)
            {
                //The user cleared the comment(s) they were making but the comments were never committed to the database.
                //So the comments are no longer dirty.
                isCommentDirtyCheckBox.Checked = false;
            }
            else
            {
                //Otherwise the comment(s) were committed to the database and will need updating.
                //If its empty then the record will be cleared from the database.
                isCommentDirtyCheckBox.Checked = true;
                _isFormDirty = true;
            }
        }

        #endregion

        #region Weekly Sales Events

        /// <summary>
        /// Event Used: Validating
        /// Validates the contents of the weekly sales text boxes and adds up the total.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateWeeklySales(object sender, CancelEventArgs e)
        {
            var textBox = (TextBox)sender;
            if (_beginningCellValue == textBox.Text) return;
            //There was a change to the starting value of the text box if we've made it this far.
            if (textBox.Text != string.Empty)
            {
                double dollarValue;
                if (double.TryParse(textBox.Text.Trim(), out dollarValue))
                {
                    //Input is valid so mark the section as dirty.
                    isWeeklySalesDirtyCheckBox.Checked = true;
                    _isFormDirty = true;
                    //And apply formatting.
                    textBox.Text = Math.Round(dollarValue, 2).ToString("N", new CultureInfo("en-US"));
                    //Check to see if the number is equal to zero (0) i.e. "0.00".
                    if (textBox.Text == @"0.00") textBox.Text = string.Empty;
                }
                else
                {
                    MessageBox.Show(@"The value must be numeric.", @"Input Must be Numeric");
                    textBox.Text = string.Empty;
                }
            }
            if (isWeeklySalesDirtyCheckBox.Tag != null)
            {
                isWeeklySalesDirtyCheckBox.Checked = true;
                _isFormDirty = true;
            }
            //Update the total sales text box before returning.
            var totalWeeklySales = 0.00;
            if (sundayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(sundayWeeklySalesTextBox.Text);
            if (mondayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(mondayWeeklySalesTextBox.Text);
            if (tuesdayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(tuesdayWeeklySalesTextBox.Text);
            if (wednesdayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(wednesdayWeeklySalesTextBox.Text);
            if (thursdayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(thursdayWeeklySalesTextBox.Text);
            if (fridayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(fridayWeeklySalesTextBox.Text);
            if (saturdayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(saturdayWeeklySalesTextBox.Text);
            if (Math.Abs(totalWeeklySales) > 0)
            {
                totalWeeklySalesTextBox.Text = totalWeeklySales.ToString("N", new CultureInfo("en-US"));
            }
            else
            {
                //Check to see if the weekly sales have been added to the database.
                if (isWeeklySalesDirtyCheckBox.Tag == null)
                {
                    //If the tag doesn't have an ID in it then we're clear to simply clear the is dirty flag.
                    isWeeklySalesDirtyCheckBox.Checked = false;
                }
                else if (isWeeklySalesDirtyCheckBox.Tag != null && textBox.Text == string.Empty)
                {
                    //If the user has already added the fields to the database but has removed a value
                    //update that accordingly.
                    isWeeklySalesDirtyCheckBox.Checked = true;
                    _isFormDirty = true;
                }
                totalWeeklySalesTextBox.Text = string.Empty;
            }
        }

        #endregion

        #region Taxable Events

        /// <summary>
        /// Validates the contents of the Taxable text boxes and adds up the total.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateTaxableFields(object sender, CancelEventArgs e)
        {
            var textBox = (TextBox)sender;
            if (_beginningCellValue == textBox.Text) return;
            //There was a change to the starting value of the text box if we've made it this far.
            if (textBox.Text != string.Empty)
            {
                double dollarValue;
                if (double.TryParse(textBox.Text.Trim(), out dollarValue))
                {
                    //Input is valid so mark the section as dirty.
                    isTaxableDirtyCheckBox.Checked = true;
                    _isFormDirty = true;
                    //And apply formatting.
                    textBox.Text = Math.Round(dollarValue, 2).ToString("N", new CultureInfo("en-US"));
                    //Check to see if the number is equal to zero (0) i.e. "0.00".
                    if (textBox.Text == @"0.00") textBox.Text = string.Empty;
                }
                else
                {
                    MessageBox.Show(@"The value must be numeric.", @"Input Must be Numeric");
                    textBox.Text = string.Empty;
                }
            }
            if (isTaxableDirtyCheckBox.Tag != null)
            {
                isTaxableDirtyCheckBox.Checked = true;
                _isFormDirty = true;
            }
            //Update the total sales text box before returning.
            var totalTaxable = 0.00;
            if (sundayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(sundayTaxableTextBox.Text);
            if (mondayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(mondayTaxableTextBox.Text);
            if (tuesdayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(tuesdayTaxableTextBox.Text);
            if (wednesdayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(wednesdayTaxableTextBox.Text);
            if (thursdayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(thursdayTaxableTextBox.Text);
            if (fridayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(fridayTaxableTextBox.Text);
            if (saturdayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(saturdayTaxableTextBox.Text);
            if (Math.Abs(totalTaxable) > 0)
            {
                totalTaxableTextBox.Text = totalTaxable.ToString("N", new CultureInfo("en-US"));
            }
            else
            {
                //Check to see if the weekly sales have been added to the database.
                if (isTaxableDirtyCheckBox.Tag == null)
                {
                    //If the tag doesn't have an ID in it then we're clear to simply clear the is dirty flag.
                    isTaxableDirtyCheckBox.Checked = false;
                }
                else if (isTaxableDirtyCheckBox.Tag != null && textBox.Text == string.Empty)
                {
                    //If the user has already added the fields to the database but has removed a value
                    //update that accordingly.
                    isTaxableDirtyCheckBox.Checked = true;
                    _isFormDirty = true;
                }
                totalTaxableTextBox.Text = string.Empty;
            }
        }

        #endregion

        #region Cost Analysis Events

        /// <summary>
        /// Validates the cost analysis text boxes.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateCostAnalysisValues(object sender, CancelEventArgs e)
        {
            var textBox = (TextBox)sender;
            if (_beginningCellValue == textBox.Text) return;
            //There was a change to the starting value of the text box if we've made it this far.
            if (textBox.Text != string.Empty)
            {
                double value;
                if (double.TryParse(textBox.Text.Trim(), out value))
                {
                    //Input is valid so mark the section as dirty.
                    isCostAnalysisDirtyCheckBox.Checked = true;
                    _isFormDirty = true;
                    //And apply formatting.
                    textBox.Text = Math.Round(value, 2).ToString("N", new CultureInfo("en-US"));
                    //Check to see if the number is equal to zero (0) i.e. "0.00".
                    if (textBox.Text == @"0.00") textBox.Text = string.Empty;
                }
                else
                {
                    MessageBox.Show(@"The value must be numeric.", @"Input Must be Numeric");
                    textBox.Text = string.Empty;
                }
            }
            else
            {
                //Since the text box that fired this event is empty check to see if this group has been committed to the database.
                if (isCostAnalysisDirtyCheckBox.Tag == null)
                {
                    //Since it hasn't check to see if the other text boxes are empty.
                    var isDirty = salesPerManHourTextBox.Text.Trim() == string.Empty;
                    if (isDirty)
                    {
                        isCostAnalysisDirtyCheckBox.Checked = false;
                        return;
                    }
                    isDirty = salaryPercentageTextBox.Text.Trim() == string.Empty;
                    if (isDirty)
                    {
                        isCostAnalysisDirtyCheckBox.Checked = false;
                        return;
                    }
                    isDirty = salaryDollarsTextBox.Text.Trim() == string.Empty;
                    if (isDirty)
                    {
                        isCostAnalysisDirtyCheckBox.Checked = false;
                        return;
                    }
                    isDirty = suppliesTextBox.Text.Trim() == string.Empty;
                    if (isDirty)
                    {
                        isCostAnalysisDirtyCheckBox.Checked = false;
                    }
                }
                else if (isCostAnalysisDirtyCheckBox.Tag != null && textBox.Text == string.Empty)
                {
                    //If the user has already added the fields to the database but has removed a value
                    //update that accordingly.
                    isCostAnalysisDirtyCheckBox.Checked = true;
                    _isFormDirty = true;
                }
            }
        }

        #endregion

        #region Date Time Events

        private void ValidateDateChanged(object sender, DateRangeEventArgs e)
        {
            if (e.Start == _currentActiveDate) return;
            if (!weekEndingCalendar.BoldedDates.Contains(e.Start))
            {
                if (_formRoll == FormRoll.AddRecord)
                {
                    //If the form is dirty then prompt the user to save changes.
                    if (_isFormDirty)
                    {
                        var result = MessageBox.Show(
                            @"Would you like to save the changes you have made to this record?", @"Changes Detected",
                            MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                        if (result == DialogResult.Yes)
                        {
                            //Save the changes made to the database then clear and load the date picked by the user.
                            if (SaveRecords(false))
                            {
                                informationLabel.Text = @"Saved all changes successfully." + Environment.NewLine;
                            }
                            else
                            {
                                errorLabel.Text = @"Failed to save all changes." + Environment.NewLine;
                            }
                        }
                        else if (result == DialogResult.Cancel)
                        {
                            //Cancel this method, select the active current date and return.
                            weekEndingCalendar.SelectionStart = _currentActiveDate;
                        }
                        //The "No" button doesn't need to be processed since it just means continue on with the method.
                    }
                    else
                    {
                        //Change the active date of the form.
                        _currentActiveDate = e.Start;
                        Text = Text = @"Add New Record (Current Record: " + e.Start.ToString("d") + @")";
                    }
                }
            }
            else
            {
                //If the form is dirty then prompt the user to save changes.
                if (_isFormDirty)
                {
                    var result = MessageBox.Show(@"Would you like to save the changes you have made to this record?", @"Changes Detected", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                    if (result == DialogResult.Yes)
                    {
                        //Save the changes made to the database then clear and load the date picked by the user.
                        if (SaveRecords(false))
                        {
                            informationLabel.Text = @"Saved all changes successfully." + Environment.NewLine;
                        }
                        else
                        {
                            errorLabel.Text = @"Failed to save all changes." + Environment.NewLine;
                            return;
                        }
                    }
                    else if (result == DialogResult.Cancel)
                    {
                        //Cancel this method, select the active current date and return.
                        weekEndingCalendar.SelectionStart = _currentActiveDate;
                        return;
                    }
                    //The "No" button doesn't need to be processed since it just means continue on with the method.
                }
                _currentActiveDate = e.Start;
                Text = @"Modify Record (Current Record: " + e.Start.ToString("d") + @")";
                addRecordButton.Text = @"Update Record";
                //Clear the form state.
                ClearFormState();
                //Load the specified date from the database.
                LoadDate(e.Start);
            }              
        }

        private void SetNewRecordDate(DateTime date, int weeksAhead = 0)
        {
            weekEndingCalendar.DateChanged -= ValidateDateChanged;
            switch (date.DayOfWeek)
            {
                case DayOfWeek.Sunday:
                    weekEndingCalendar.SelectionStart = date.AddDays(6 + (weeksAhead * 7));
                    break;
                case DayOfWeek.Monday:
                    weekEndingCalendar.SelectionStart = date.AddDays(5 + (weeksAhead * 7));
                    break;
                case DayOfWeek.Tuesday:
                    weekEndingCalendar.SelectionStart = date.AddDays(4 + (weeksAhead * 7));
                    break;
                case DayOfWeek.Wednesday:
                    weekEndingCalendar.SelectionStart = date.AddDays(3 + (weeksAhead * 7));
                    break;
                case DayOfWeek.Thursday:
                    weekEndingCalendar.SelectionStart = date.AddDays(2 + (weeksAhead * 7));
                    break;
                case DayOfWeek.Friday:
                    weekEndingCalendar.SelectionStart = date.AddDays(1 + (weeksAhead * 7));
                    break;
                case DayOfWeek.Saturday:
                    weekEndingCalendar.SelectionStart = date.AddDays((weeksAhead * 7));
                    break;
                default:
                    return;
            }
            weekEndingCalendar.DateChanged += ValidateDateChanged;
        }

        #endregion

        private void ClearFormState()
        {
            projectionsDataGridView.CellEnter -= StoreBeginningCellValue;
            projectionsDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
            projectionsDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
            projectionsDataGridView.RowValidating -= ValidateProjectedRow;
            projectionsDataGridView.RowsRemoved -= ProjectionRowRemoved;
            inventoryDataGridView.CellEnter -= StoreBeginningCellValue;
            inventoryDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
            inventoryDataGridView.CellValidating -= ValidateInventoryCellContents;
            inventoryDataGridView.RowValidating -= ValidateInventoryRow;
            inventoryDataGridView.RowsRemoved -= InventoryRowRemoved;
            actualSalesDataGridView.CellEnter -= StoreBeginningCellValue;
            actualSalesDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
            actualSalesDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
            actualSalesDataGridView.RowValidating -= ValidateActualSalesRow;
            actualSalesDataGridView.RowsRemoved -= ActualSalesRowRemoved;
            invoicesDataGridView.CellEnter -= StoreBeginningCellValue;
            invoicesDataGridView.RowValidating -= ValidateInvoiceRow;
            invoicesDataGridView.CellValidating -= ValidateInvoicesCellContents;
            commentsTextBox.Enter -= StoreBeginningTextBoxValue;
            commentsTextBox.KeyDown -= CheckForKeyCommand;
            commentsTextBox.Leave -= CheckForTextChangeOnLeave;
            sundayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            sundayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            mondayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            mondayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            tuesdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            tuesdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            wednesdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            wednesdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            thursdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            thursdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            fridayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            fridayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            saturdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            saturdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            totalWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            totalWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            sundayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            sundayTaxableTextBox.Validating -= ValidateTaxableFields;
            mondayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            mondayTaxableTextBox.Validating -= ValidateTaxableFields;
            tuesdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            tuesdayTaxableTextBox.Validating -= ValidateTaxableFields;
            wednesdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            wednesdayTaxableTextBox.Validating -= ValidateTaxableFields;
            thursdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            thursdayTaxableTextBox.Validating -= ValidateTaxableFields;
            fridayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            fridayTaxableTextBox.Validating -= ValidateTaxableFields;
            saturdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            saturdayTaxableTextBox.Validating -= ValidateTaxableFields;
            totalTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            totalTaxableTextBox.Validating -= ValidateTaxableFields;
            salesPerManHourTextBox.Enter -= StoreBeginningTextBoxValue;
            salesPerManHourTextBox.Validating -= ValidateCostAnalysisValues;
            salaryPercentageTextBox.Enter -= StoreBeginningTextBoxValue;
            salaryPercentageTextBox.Validating -= ValidateCostAnalysisValues;
            salaryDollarsTextBox.Enter -= StoreBeginningTextBoxValue;
            salaryDollarsTextBox.Validating -= ValidateCostAnalysisValues;
            suppliesTextBox.Enter -= StoreBeginningTextBoxValue;
            suppliesTextBox.Validating -= ValidateCostAnalysisValues;
            //Clear projections
            projectionsDataGridView.Rows.Clear();
            //Clear inventory
            inventoryDataGridView.Rows.Clear();
            //Clear actual sales
            actualSalesDataGridView.Rows.Clear();
            //Reset the ad special index.
            _adSpecialIndex = -1;
            //Clear the beginning cell value variable.
            _beginningCellValue = string.Empty;
            //Reset the form's dirty flag
            _isFormDirty = false;
            //Clear the used ad items
            _usedAdItems[0].Clear(); //regular ad items (section 1)
            _usedAdItems[1].Clear(); //ad special items (section 2)
            //Clear invoices
            invoicesDataGridView.Rows.Clear();
            //Clear comments
            isCommentDirtyCheckBox.Checked = false;
            isCommentDirtyCheckBox.Text = @"IsCommentDirty";
            isCommentDirtyCheckBox.Tag = null;
            commentsTextBox.Text = string.Empty;
            commentsGroupBox.Text = @"Comments (Characters Remaining: " + commentsTextBox.MaxLength + @")";
            //Clear weekly sales.
            isWeeklySalesDirtyCheckBox.Checked = false;
            isWeeklySalesDirtyCheckBox.Text = @"IsWeeklySalesDirty";
            isWeeklySalesDirtyCheckBox.Tag = null;
            sundayWeeklySalesTextBox.Text = string.Empty;
            mondayWeeklySalesTextBox.Text = string.Empty;
            tuesdayWeeklySalesTextBox.Text = string.Empty;
            wednesdayWeeklySalesTextBox.Text = string.Empty;
            thursdayWeeklySalesTextBox.Text = string.Empty;
            fridayWeeklySalesTextBox.Text = string.Empty;
            saturdayWeeklySalesTextBox.Text = string.Empty;
            totalWeeklySalesTextBox.Text = string.Empty;
            //Clear taxable.
            isTaxableDirtyCheckBox.Checked = false;
            isTaxableDirtyCheckBox.Text = @"IsTaxableDirty";
            isTaxableDirtyCheckBox.Tag = null;
            sundayTaxableTextBox.Text = string.Empty;
            mondayTaxableTextBox.Text = string.Empty;
            tuesdayTaxableTextBox.Text = string.Empty;
            wednesdayTaxableTextBox.Text = string.Empty;
            thursdayTaxableTextBox.Text = string.Empty;
            fridayTaxableTextBox.Text = string.Empty;
            saturdayTaxableTextBox.Text = string.Empty;
            totalTaxableTextBox.Text = string.Empty;
            //Clear cost analysis
            isCostAnalysisDirtyCheckBox.Checked = false;
            isCostAnalysisDirtyCheckBox.Text = @"IsCostAnalysisDirty";
            isCostAnalysisDirtyCheckBox.Tag = null;
            salesPerManHourTextBox.Text = string.Empty;
            salaryPercentageTextBox.Text = string.Empty;
            salaryDollarsTextBox.Text = string.Empty;
            suppliesTextBox.Text = string.Empty;
            //Re-enable the events for all the controls.
            projectionsDataGridView.CellEnter += StoreBeginningCellValue;
            projectionsDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            projectionsDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            projectionsDataGridView.RowValidating += ValidateProjectedRow;
            projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;
            inventoryDataGridView.CellEnter += StoreBeginningCellValue;
            inventoryDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            inventoryDataGridView.CellValidating += ValidateInventoryCellContents;
            inventoryDataGridView.RowValidating += ValidateInventoryRow;
            inventoryDataGridView.RowsRemoved += InventoryRowRemoved;
            actualSalesDataGridView.CellEnter += StoreBeginningCellValue;
            actualSalesDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            actualSalesDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            actualSalesDataGridView.RowValidating += ValidateActualSalesRow;
            actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;

            invoicesDataGridView.CellEnter += StoreBeginningCellValue;
            invoicesDataGridView.RowValidating += ValidateInvoiceRow;
            invoicesDataGridView.CellValidating += ValidateInvoicesCellContents;

            commentsTextBox.Enter += StoreBeginningTextBoxValue;
            commentsTextBox.KeyDown += CheckForKeyCommand;
            commentsTextBox.Leave += CheckForTextChangeOnLeave;

            sundayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            sundayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            mondayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            mondayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            tuesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            tuesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            wednesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            wednesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            thursdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            thursdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            fridayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            fridayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            saturdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            saturdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            totalWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            totalWeeklySalesTextBox.Validating += ValidateWeeklySales;

            sundayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            sundayTaxableTextBox.Validating += ValidateTaxableFields;
            mondayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            mondayTaxableTextBox.Validating += ValidateTaxableFields;
            tuesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            tuesdayTaxableTextBox.Validating += ValidateTaxableFields;
            wednesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            wednesdayTaxableTextBox.Validating += ValidateTaxableFields;
            thursdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            thursdayTaxableTextBox.Validating += ValidateTaxableFields;
            fridayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            fridayTaxableTextBox.Validating += ValidateTaxableFields;
            saturdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            saturdayTaxableTextBox.Validating += ValidateTaxableFields;
            totalTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            totalTaxableTextBox.Validating += ValidateTaxableFields;

            salesPerManHourTextBox.Enter += StoreBeginningTextBoxValue;
            salesPerManHourTextBox.Validating += ValidateCostAnalysisValues;
            salaryPercentageTextBox.Enter += StoreBeginningTextBoxValue;
            salaryPercentageTextBox.Validating += ValidateCostAnalysisValues;
            salaryDollarsTextBox.Enter += StoreBeginningTextBoxValue;
            salaryDollarsTextBox.Validating += ValidateCostAnalysisValues;
            suppliesTextBox.Enter += StoreBeginningTextBoxValue;
            suppliesTextBox.Validating += ValidateCostAnalysisValues;
        }

        #region Data Loading Methods

        private void LoadDate(DateTime date)
        {
            var databaseTracker = new DatabaseTracker();
            var databaseReader = new DatabaseReader();
            var dateId = databaseReader.RetrieveDateIdByDateString(date.ToString("d"), databaseTracker.DatabaseConnectionString);
            if (dateId == 0)
            {
                errorLabel.Text = @"Failed to get the date ID number, aborting load operation." + Environment.NewLine;
                return;
            }
            informationLabel.Text = @"Loading data for " + _currentActiveDate.ToString("d") + @"." + Environment.NewLine;
            var projections = databaseReader.ReturnProjectionsTable(dateId, databaseTracker.DatabaseConnectionString);
            var inventory = databaseReader.ReturnInventoryTable(dateId, databaseTracker.DatabaseConnectionString);
            var actualSales = databaseReader.ReturnActualSales(dateId, databaseTracker.DatabaseConnectionString);
            LoadProjectionsTable(projections);
            LoadInventory(inventory);
            LoadActualSales(actualSales);
            var invoices = databaseReader.ReturnInvoiceTable(dateId, databaseTracker.DatabaseConnectionString);
            var comments = databaseReader.RetrieveComments(int.Parse(dateId.ToString()), databaseTracker.DatabaseConnectionString);
            if (comments.Count == 2)
            {
                LoadComments(int.Parse(comments[0]), comments[1]);
            }
            else
            {
                informationLabel.Text += @"No comments to display." + Environment.NewLine;
            }
            var weeklySales = databaseReader.ReturnWeeklySalesFromDateId(dateId, databaseTracker.DatabaseConnectionString);
            if (weeklySales.Rows.Count == 1)
            {
                LoadWeeklySales(weeklySales);
            }
            else
            {
                informationLabel.Text += @"No sales to display." + Environment.NewLine;
            }

            var taxable = databaseReader.ReturnTaxableFromDateId(dateId, databaseTracker.DatabaseConnectionString);
            if (taxable.Rows.Count == 1)
            {
                LoadTaxable(taxable);
            }
            else
            {
                informationLabel.Text += @"No taxable data to display." + Environment.NewLine;
            }
            LoadInvoices(invoices);
            var costAnalysis = databaseReader.ReturnCostAnalysis(dateId, databaseTracker.DatabaseConnectionString);
            if (costAnalysis.Rows.Count == 1)
            {
                LoadCostAnalysis(costAnalysis);
            }
            else
            {
                informationLabel.Text += @"No cost analysis data to display." + Environment.NewLine;
            }
            _formRoll = FormRoll.ModifyRecord;
        }

        private void LoadProjectionsTable(DataTable projections)
        {
            //Assuming all the tables are correctly aligned.
            //Disable the relevant events in the DataGridViews
            projectionsDataGridView.CellEnter -= StoreBeginningCellValue;
            projectionsDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
            projectionsDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
            projectionsDataGridView.RowValidating -= ValidateProjectedRow;

            for (var rowIndex = 0; rowIndex < projections.Rows.Count; rowIndex++)
            {
                var newRow = new DataGridViewRow();
                var isMemberRow = false;
                for (var cellIndex = 0; cellIndex < projections.Rows[rowIndex].ItemArray.Length; cellIndex++)
                {
                    //ID and Ad Item.
                    if (cellIndex <= 1)
                    {
                        var cell = new DataGridViewTextBoxCell
                        {
                            Value = projections.Rows[rowIndex].ItemArray[cellIndex].ToString()
                        };
                        newRow.Cells.Add(cell);
                        continue;
                    }
                    //Everything in between the ad item cell and the row attribute cells.
                    if (cellIndex > 1 && cellIndex <= 7)
                    {
                        //Only allow zeros in the Sold columns otherwise just leave them blank.
                        if (cellIndex == (int) SalesTableColumns.Sold)
                        {
                            var cell = new DataGridViewTextBoxCell { Value = projections.Rows[rowIndex].ItemArray[cellIndex].ToString() };
                            newRow.Cells.Add(cell);
                        }
                        else if (projections.Rows[rowIndex].ItemArray[cellIndex].ToString() == "0.0000" || projections.Rows[rowIndex].ItemArray[cellIndex].ToString() == "0")
                        {
                            var cell = new DataGridViewTextBoxCell {Value = string.Empty};
                            newRow.Cells.Add(cell);
                        }
                        else
                        {
                            var cell = new DataGridViewTextBoxCell {Value = projections.Rows[rowIndex].ItemArray[cellIndex].ToString()};
                            newRow.Cells.Add(cell);
                        }
                        continue;
                    }
                    //Check the attribute cell.
                    if (cellIndex == 8)
                    {
                        var isHeaderCell = new DataGridViewCheckBoxCell();
                        var isMemberCell = new DataGridViewCheckBoxCell();
                        var rowAttribute = int.Parse(projections.Rows[rowIndex].ItemArray[cellIndex].ToString());
                        switch (rowAttribute)
                        {
                            case 1:
                                //Header row
                                isHeaderCell.Value = true;
                                newRow.DefaultCellStyle.BackColor = ApplicationColors.HeaderRow;
                                break;
                            case 2:
                                //Member Row
                                isMemberCell.Value = true;
                                isMemberRow = true;
                                newRow.DefaultCellStyle.BackColor = ApplicationColors.MemberRow;
                                break;
                            default:
                                isHeaderCell.Value = false;
                                isMemberCell.Value = false;
                                break;
                        }
                        newRow.Cells.Add(isHeaderCell);
                        newRow.Cells.Add(isMemberCell);
                        continue;
                    }
                    //Check the group it is part of if any.
                    if (cellIndex == 9)
                    {
                        //Check to see if this row belongs to an ad special group.
                        if (projections.Rows[rowIndex].ItemArray[cellIndex].ToString() != "0")
                        {
                            //If so add the ad item in it to section two (2) of the used ad item array.
                            _usedAdItems[1].Add(projections.Rows[rowIndex].ItemArray[1].ToString());
                            //If the ad special index is not set, then create the ad special row with the human friendly group name.
                            if (_adSpecialIndex == -1)
                            {
                                var databaseTracker = new DatabaseTracker();
                                var databaseReader = new DatabaseReader();
                                var groupName = databaseReader.ReturnGroupNameFromGroupId(projections.Rows[rowIndex].ItemArray[cellIndex].ToString(), databaseTracker.DatabaseConnectionString);
                                var adSpecialRow = new DataGridViewRow();
                                projectionsDataGridView.Rows.Add(adSpecialRow);
                                projectionsDataGridView.Rows[rowIndex].Cells[1].Value = groupName;
                                projectionsDataGridView.Rows[rowIndex].DefaultCellStyle.BackColor = ApplicationColors.AdSpecial;
                                _adSpecialIndex = rowIndex;
                            }
                        }
                        else
                        {
                            //The ad item belongs in section one (1) of the used ad item array.
                            _usedAdItems[0].Add(projections.Rows[rowIndex].ItemArray[1].ToString());
                        }
                    }

                    if (!isMemberRow) continue;
                    //Clear all values from the member row, they are not needed.
                    newRow.Cells[2].Value = string.Empty;
                    newRow.Cells[3].Value = string.Empty;
                    newRow.Cells[4].Value = string.Empty;
                    newRow.Cells[5].Value = string.Empty;
                    newRow.Cells[6].Value = string.Empty;
                    newRow.Cells[7].Value = string.Empty;
                }
                //Set the is dirty cell to false.
                var isDirtyCell = new DataGridViewCheckBoxCell
                {
                    Value = false
                };
                newRow.Cells.Add(isDirtyCell);
                projectionsDataGridView.Rows.Add(newRow);
            }
            //Manually write the row number to the new row.
            projectionsDataGridView.Rows[projectionsDataGridView.Rows.Count - 1].HeaderCell.Value = projectionsDataGridView.Rows.Count.ToString();
            //Re-enable the events
            projectionsDataGridView.CellEnter += StoreBeginningCellValue;
            projectionsDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            projectionsDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            projectionsDataGridView.RowValidating += ValidateProjectedRow;
        }

        private void LoadInventory(DataTable inventoryTable)
        {
            //Assuming all the tables are correctly aligned.
            //Disable the relevant events in the DataGridViews
            inventoryDataGridView.CellEnter -= StoreBeginningCellValue;
            inventoryDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
            inventoryDataGridView.CellValidating -= ValidateInventoryCellContents;
            inventoryDataGridView.RowValidating -= ValidateInventoryRow;
            //Create a local ad special index since Projections should have filled the ad special index in for the class.
            var adSpecialIndex = -1;

            for (var rowIndex = 0; rowIndex < inventoryTable.Rows.Count; rowIndex++)
            {
                var newRow = new DataGridViewRow();
                var isMemberRow = false;
                for (var cellIndex = 0; cellIndex < inventoryTable.Rows[rowIndex].ItemArray.Length; cellIndex++)
                {
                    //ID and Ad Item.
                    if (cellIndex <= 1)
                    {
                        var cell = new DataGridViewTextBoxCell
                        {
                            Value = inventoryTable.Rows[rowIndex].ItemArray[cellIndex].ToString()
                        };
                        newRow.Cells.Add(cell);
                        continue;
                    }
                    //Everything in between the ad item cell and the row attribute cells.
                    if (cellIndex > 1 && cellIndex <= 5)
                    {
                        var cell = new DataGridViewTextBoxCell {Value = inventoryTable.Rows[rowIndex].ItemArray[cellIndex].ToString()};
                        newRow.Cells.Add(cell);             
                        continue;
                    }
                    //Check the attribute cell.
                    if (cellIndex == 6)
                    {
                        var isHeaderCell = new DataGridViewCheckBoxCell();
                        var isMemberCell = new DataGridViewCheckBoxCell();
                        var rowAttribute = int.Parse(inventoryTable.Rows[rowIndex].ItemArray[cellIndex].ToString());
                        switch (rowAttribute)
                        {
                            case 1:
                                //Header row
                                isHeaderCell.Value = true;
                                newRow.DefaultCellStyle.BackColor = ApplicationColors.HeaderRow;
                                break;
                            case 2:
                                //Member Row
                                isMemberCell.Value = true;
                                isMemberRow = true;
                                newRow.DefaultCellStyle.BackColor = ApplicationColors.MemberRow;
                                break;
                            default:
                                isHeaderCell.Value = false;
                                isMemberCell.Value = false;
                                break;
                        }
                        newRow.Cells.Add(isHeaderCell);
                        newRow.Cells.Add(isMemberCell);
                        continue;
                    }
                    //Check the group it is part of if any.
                    if (cellIndex == 7)
                    {
                        if (inventoryTable.Rows[rowIndex].ItemArray[cellIndex].ToString() != "0" && adSpecialIndex == -1)
                        {                            
                            var databaseTracker = new DatabaseTracker();
                            var databaseReader = new DatabaseReader();
                            var groupName = databaseReader.ReturnGroupNameFromGroupId(inventoryTable.Rows[rowIndex].ItemArray[cellIndex].ToString(), databaseTracker.DatabaseConnectionString);
                            var adSpecialRow = new DataGridViewRow();
                            inventoryDataGridView.Rows.Add(adSpecialRow);
                            inventoryDataGridView.Rows[rowIndex].Cells[1].Value = groupName;
                            inventoryDataGridView.Rows[rowIndex].DefaultCellStyle.BackColor = ApplicationColors.AdSpecial;
                            adSpecialIndex = rowIndex;
                        }
                    }

                    if (!isMemberRow) continue;
                    //Clear all values from the member row, they are not needed.
                    newRow.Cells[2].Value = string.Empty;
                    newRow.Cells[3].Value = string.Empty;
                    newRow.Cells[4].Value = string.Empty;
                    newRow.Cells[5].Value = string.Empty;
                }
                //Set the is dirty cell to false.
                var isDirtyCell = new DataGridViewCheckBoxCell
                {
                    Value = false
                };
                newRow.Cells.Add(isDirtyCell);
                inventoryDataGridView.Rows.Add(newRow);
            }
            //Manually write the row number to the new row.
            inventoryDataGridView.Rows[inventoryDataGridView.Rows.Count - 1].HeaderCell.Value = inventoryDataGridView.Rows.Count.ToString();
            //Re-enable the events
            inventoryDataGridView.CellEnter += StoreBeginningCellValue;
            inventoryDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            inventoryDataGridView.CellValidating += ValidateInventoryCellContents;
            inventoryDataGridView.RowValidating += ValidateInventoryRow;
        }

        private void LoadActualSales(DataTable actualSales)
        {
            //Assuming all the tables are correctly aligned.
            //Disable the relevant events in the DataGridViews
            actualSalesDataGridView.CellEnter -= StoreBeginningCellValue;
            actualSalesDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
            actualSalesDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
            actualSalesDataGridView.RowValidating -= ValidateActualSalesRow;
            //Create a local ad special index since Projections should have filled the ad special index in for the class.
            var adSpecialIndex = -1;

            for (var rowIndex = 0; rowIndex < actualSales.Rows.Count; rowIndex++)
            {
                var newRow = new DataGridViewRow();
                var isMemberRow = false;
                for (var cellIndex = 0; cellIndex < actualSales.Rows[rowIndex].ItemArray.Length; cellIndex++)
                {
                    //ID and Ad Item.
                    if (cellIndex <= 1)
                    {
                        var cell = new DataGridViewTextBoxCell
                        {
                            Value = actualSales.Rows[rowIndex].ItemArray[cellIndex].ToString()
                        };
                        newRow.Cells.Add(cell);
                        continue;
                    }
                    //Everything in between the ad item cell and the row attribute cells.
                    if (cellIndex > 1 && cellIndex <= 7)
                    {
                        //Only allow zeros in the Sold columns otherwise just leave them blank.
                        if (cellIndex == (int)SalesTableColumns.Sold)
                        {
                            var cell = new DataGridViewTextBoxCell { Value = actualSales.Rows[rowIndex].ItemArray[cellIndex].ToString() };
                            newRow.Cells.Add(cell);
                        }
                        else if (actualSales.Rows[rowIndex].ItemArray[cellIndex].ToString() == "0.0000" || actualSales.Rows[rowIndex].ItemArray[cellIndex].ToString() == "0")
                        {
                            var cell = new DataGridViewTextBoxCell { Value = string.Empty };
                            newRow.Cells.Add(cell);
                        }
                        else
                        {
                            var cell = new DataGridViewTextBoxCell {Value = actualSales.Rows[rowIndex].ItemArray[cellIndex].ToString()};
                            newRow.Cells.Add(cell);
                        }
                        continue;
                    }
                    //Check the attribute cell.
                    if (cellIndex == 8)
                    {
                        var isHeaderCell = new DataGridViewCheckBoxCell();
                        var isMemberCell = new DataGridViewCheckBoxCell();
                        var rowAttribute = int.Parse(actualSales.Rows[rowIndex].ItemArray[cellIndex].ToString());
                        switch (rowAttribute)
                        {
                            case 1:
                                //Header row
                                isHeaderCell.Value = true;
                                newRow.DefaultCellStyle.BackColor = ApplicationColors.HeaderRow;
                                break;
                            case 2:
                                //Member Row
                                isMemberCell.Value = true;
                                isMemberRow = true;
                                newRow.DefaultCellStyle.BackColor = ApplicationColors.MemberRow;
                                break;
                            default:
                                isHeaderCell.Value = false;
                                isMemberCell.Value = false;
                                break;
                        }
                        newRow.Cells.Add(isHeaderCell);
                        newRow.Cells.Add(isMemberCell);
                        continue;
                    }
                    //Check the group it is part of if any.
                    if (cellIndex == 9)
                    {
                        if (actualSales.Rows[rowIndex].ItemArray[cellIndex].ToString() != "0" && adSpecialIndex == -1)
                        {
                            var databaseTracker = new DatabaseTracker();
                            var databaseReader = new DatabaseReader();
                            var groupName = databaseReader.ReturnGroupNameFromGroupId(actualSales.Rows[rowIndex].ItemArray[cellIndex].ToString(), databaseTracker.DatabaseConnectionString);
                            var adSpecialRow = new DataGridViewRow();
                            actualSalesDataGridView.Rows.Add(adSpecialRow);
                            actualSalesDataGridView.Rows[rowIndex].Cells[1].Value = groupName;
                            actualSalesDataGridView.Rows[rowIndex].DefaultCellStyle.BackColor = ApplicationColors.AdSpecial;
                            adSpecialIndex = rowIndex;
                        }
                    }

                    if (!isMemberRow) continue;
                    //Clear all values from the member row, they are not needed.
                    newRow.Cells[2].Value = string.Empty;
                    newRow.Cells[3].Value = string.Empty;
                    newRow.Cells[4].Value = string.Empty;
                    newRow.Cells[5].Value = string.Empty;
                    newRow.Cells[6].Value = string.Empty;
                    newRow.Cells[7].Value = string.Empty;
                }
                //Set the is dirty cell to false.
                var isDirtyCell = new DataGridViewCheckBoxCell
                {
                    Value = false
                };
                newRow.Cells.Add(isDirtyCell);
                actualSalesDataGridView.Rows.Add(newRow);
            }
            //Manually write the row number to the new row.
            actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].HeaderCell.Value = actualSalesDataGridView.Rows.Count.ToString();
            //Re-enable the events
            actualSalesDataGridView.CellEnter += StoreBeginningCellValue;
            actualSalesDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            actualSalesDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            actualSalesDataGridView.RowValidating += ValidateActualSalesRow;
        }

        private void LoadInvoices(DataTable invoices)
        {
            for (var rowIndex = 0; rowIndex < invoices.Rows.Count; rowIndex++)
            {
                var row = new DataGridViewRow();
                for (var cellIndex = 0; cellIndex < invoices.Rows[rowIndex].ItemArray.Length; cellIndex++)
                {
                    var cell = new DataGridViewTextBoxCell();
                    //Format the invoice date.
                    if (cellIndex == 1)
                    {
                        var date = DateTime.Parse(invoices.Rows[rowIndex].ItemArray[cellIndex].ToString());
                        cell.Value = date.ToString("d");
                        row.Cells.Add(cell);
                        continue;
                    }
                    //Apply formatting to the only cells that will have currency values in them.
                    if (cellIndex == 4 || cellIndex == 5)
                    {
                        var formattedNumber = Math.Round(double.Parse(invoices.Rows[rowIndex].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            cell.Value = formattedNumber;
                        }
                        row.Cells.Add(cell);
                        continue;
                    }
                    //No special formatting rules here so just put the value in and move on.     
                    cell.Value = invoices.Rows[rowIndex].ItemArray[cellIndex].ToString();
                    row.Cells.Add(cell);
                }
                //Set the is dirty cell to false.
                var isDirtyCell = new DataGridViewCheckBoxCell
                {
                    Value = false
                };
                row.Cells.Add(isDirtyCell);
                invoicesDataGridView.Rows.Add(row);
            }
        }

        private void LoadComments(int id, string comments)
        {
            //commentsTextBox.TextChanged -= DisplayRemainingCommentCharacterCount;
            commentsTextBox.Enter -= StoreBeginningTextBoxValue;
            commentsTextBox.KeyDown -= CheckForKeyCommand;
            commentsTextBox.Leave -= CheckForTextChangeOnLeave;

            isCommentDirtyCheckBox.Text = @"isCommentsDirty (" + id + @")";
            isCommentDirtyCheckBox.Tag = id;

            commentsTextBox.Text = comments;

            //commentsGroupBox.Text = @"Comments (Characters Remaining: " + commentsTextBox.MaxLength + @")";
            //commentsTextBox.TextChanged += DisplayRemainingCommentCharacterCount;
            commentsTextBox.Enter += StoreBeginningTextBoxValue;
            commentsTextBox.KeyDown += CheckForKeyCommand;
            commentsTextBox.Leave += CheckForTextChangeOnLeave;
        }

        private void LoadWeeklySales(DataTable weeklySales)
        {
            sundayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            sundayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            mondayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            mondayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            tuesdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            tuesdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            wednesdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            wednesdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            thursdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            thursdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            fridayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            fridayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            saturdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            saturdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
            totalWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
            totalWeeklySalesTextBox.Validating -= ValidateWeeklySales;

            //Spin through the only row in the weekly sales table. Item in item 
            //array index zero (0) is the ID number of the weekly sales.
            for (var cellIndex = 0; cellIndex < weeklySales.Rows[0].ItemArray.Length; cellIndex++)
            {
                if (cellIndex == 0)
                {
                    //ID number
                    var id = int.Parse(weeklySales.Rows[0].ItemArray[cellIndex].ToString());
                    isWeeklySalesDirtyCheckBox.Tag = id;
                    isWeeklySalesDirtyCheckBox.Text = @"IsWeeklySalesDirty (" + id + @")";
                    continue;
                }
                string formattedNumber;
                switch (cellIndex)
                {
                    case 1: //Sunday
                        formattedNumber = Math.Round(double.Parse(weeklySales.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            sundayWeeklySalesTextBox.Text = formattedNumber;
                        }
                        break;
                    case 2: //Monday
                        formattedNumber = Math.Round(double.Parse(weeklySales.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            mondayWeeklySalesTextBox.Text = formattedNumber;
                        }
                        break;
                    case 3: //Tuesday
                        formattedNumber = Math.Round(double.Parse(weeklySales.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            tuesdayWeeklySalesTextBox.Text = formattedNumber;
                        }
                        break;
                    case 4: //Wednesday
                        formattedNumber = Math.Round(double.Parse(weeklySales.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            wednesdayWeeklySalesTextBox.Text = formattedNumber;
                        }
                        break;
                    case 5: //Thursday
                        formattedNumber = Math.Round(double.Parse(weeklySales.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            thursdayWeeklySalesTextBox.Text = formattedNumber;
                        }
                        break;
                    case 6: //Friday
                        formattedNumber = Math.Round(double.Parse(weeklySales.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            fridayWeeklySalesTextBox.Text = formattedNumber;
                        }
                        break;
                    case 7: //Saturday
                        formattedNumber = Math.Round(double.Parse(weeklySales.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            saturdayWeeklySalesTextBox.Text = formattedNumber;
                        }
                        break;
                    case 8: //Total Sales
                        formattedNumber = Math.Round(double.Parse(weeklySales.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            totalWeeklySalesTextBox.Text = formattedNumber;
                        }
                        break;
                }
            }

            sundayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            sundayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            mondayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            mondayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            tuesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            tuesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            wednesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            wednesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            thursdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            thursdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            fridayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            fridayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            saturdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            saturdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
            totalWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
            totalWeeklySalesTextBox.Validating += ValidateWeeklySales;
        }

        private void LoadTaxable(DataTable taxable)
        {
            sundayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            sundayTaxableTextBox.Validating -= ValidateTaxableFields;
            mondayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            mondayTaxableTextBox.Validating -= ValidateTaxableFields;
            tuesdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            tuesdayTaxableTextBox.Validating -= ValidateTaxableFields;
            wednesdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            wednesdayTaxableTextBox.Validating -= ValidateTaxableFields;
            thursdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            thursdayTaxableTextBox.Validating -= ValidateTaxableFields;
            fridayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            fridayTaxableTextBox.Validating -= ValidateTaxableFields;
            saturdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            saturdayTaxableTextBox.Validating -= ValidateTaxableFields;
            totalTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
            totalTaxableTextBox.Validating -= ValidateTaxableFields;
            //Spin through the only row in the taxable table. Item in item 
            //array index zero (0) is the ID number of the weekly sales.
            for (var cellIndex = 0; cellIndex < taxable.Rows[0].ItemArray.Length; cellIndex++)
            {
                if (cellIndex == 0)
                {
                    //ID number
                    var id = int.Parse(taxable.Rows[0].ItemArray[cellIndex].ToString());
                    isTaxableDirtyCheckBox.Tag = id;
                    isTaxableDirtyCheckBox.Text = @"IsTaxableDirty (" + id + @")";
                    continue;
                }
                string formattedNumber;
                switch (cellIndex)
                {
                    case 1: //Sunday
                        formattedNumber = Math.Round(double.Parse(taxable.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            sundayTaxableTextBox.Text = formattedNumber;
                        }
                        break;
                    case 2: //Monday
                        formattedNumber = Math.Round(double.Parse(taxable.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            mondayTaxableTextBox.Text = formattedNumber;
                        }
                        break;
                    case 3: //Tuesday
                        formattedNumber = Math.Round(double.Parse(taxable.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            tuesdayTaxableTextBox.Text = formattedNumber;
                        }
                        break;
                    case 4: //Wednesday
                        formattedNumber = Math.Round(double.Parse(taxable.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            wednesdayTaxableTextBox.Text = formattedNumber;
                        }
                        break;
                    case 5: //Thursday
                        formattedNumber = Math.Round(double.Parse(taxable.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            thursdayTaxableTextBox.Text = formattedNumber;
                        }
                        break;
                    case 6: //Friday
                        formattedNumber = Math.Round(double.Parse(taxable.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            fridayTaxableTextBox.Text = formattedNumber;
                        }
                        break;
                    case 7: //Saturday
                        formattedNumber = Math.Round(double.Parse(taxable.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            saturdayTaxableTextBox.Text = formattedNumber;
                        }
                        break;
                    case 8: //Total Taxable
                        formattedNumber = Math.Round(double.Parse(taxable.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            totalTaxableTextBox.Text = formattedNumber;
                        }
                        break;
                }
            }
            sundayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            sundayTaxableTextBox.Validating += ValidateTaxableFields;
            mondayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            mondayTaxableTextBox.Validating += ValidateTaxableFields;
            tuesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            tuesdayTaxableTextBox.Validating += ValidateTaxableFields;
            wednesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            wednesdayTaxableTextBox.Validating += ValidateTaxableFields;
            thursdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            thursdayTaxableTextBox.Validating += ValidateTaxableFields;
            fridayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            fridayTaxableTextBox.Validating += ValidateTaxableFields;
            saturdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            saturdayTaxableTextBox.Validating += ValidateTaxableFields;
            totalTaxableTextBox.Enter += StoreBeginningTextBoxValue;
            totalTaxableTextBox.Validating += ValidateTaxableFields;
        }

        private void LoadCostAnalysis(DataTable costAnalysis)
        {
            salesPerManHourTextBox.Enter -= StoreBeginningTextBoxValue;
            salesPerManHourTextBox.Validating -= ValidateCostAnalysisValues;
            salaryPercentageTextBox.Enter -= StoreBeginningTextBoxValue;
            salaryPercentageTextBox.Validating -= ValidateCostAnalysisValues;
            salaryDollarsTextBox.Enter -= StoreBeginningTextBoxValue;
            salaryDollarsTextBox.Validating -= ValidateCostAnalysisValues;
            suppliesTextBox.Enter -= StoreBeginningTextBoxValue;
            suppliesTextBox.Validating -= ValidateCostAnalysisValues;
            //Spin through the only row in the taxable table. Item in item 
            //array index zero (0) is the ID number of the weekly sales.
            for (var cellIndex = 0; cellIndex < costAnalysis.Rows[0].ItemArray.Length; cellIndex++)
            {
                if (cellIndex == 0)
                {
                    //ID number
                    var id = int.Parse(costAnalysis.Rows[0].ItemArray[cellIndex].ToString());
                    isCostAnalysisDirtyCheckBox.Tag = id;
                    isCostAnalysisDirtyCheckBox.Text = @"IsCostAnalysisDirty (" + id + @")";
                    continue;
                }
                string formattedNumber;
                switch (cellIndex)
                {
                    case 1: //Sales per man hour
                        formattedNumber = Math.Round(double.Parse(costAnalysis.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            salesPerManHourTextBox.Text = formattedNumber;
                        }
                        break;
                    case 2: //Salary Percentage
                        formattedNumber = Math.Round(double.Parse(costAnalysis.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            salaryPercentageTextBox.Text = formattedNumber;
                        }
                        break;
                    case 3: //Salary Dollars
                        formattedNumber = Math.Round(double.Parse(costAnalysis.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            salaryDollarsTextBox.Text = formattedNumber;
                        }
                        break;
                    case 4: //Supplies
                        formattedNumber = Math.Round(double.Parse(costAnalysis.Rows[0].ItemArray[cellIndex].ToString()), 2).ToString("N", new CultureInfo("en-US"));
                        if (formattedNumber != "0.00")
                        {
                            suppliesTextBox.Text = formattedNumber;
                        }
                        break;
                }
            }
            salesPerManHourTextBox.Enter += StoreBeginningTextBoxValue;
            salesPerManHourTextBox.Validating += ValidateCostAnalysisValues;
            salaryPercentageTextBox.Enter += StoreBeginningTextBoxValue;
            salaryPercentageTextBox.Validating += ValidateCostAnalysisValues;
            salaryDollarsTextBox.Enter += StoreBeginningTextBoxValue;
            salaryDollarsTextBox.Validating += ValidateCostAnalysisValues;
            suppliesTextBox.Enter += StoreBeginningTextBoxValue;
            suppliesTextBox.Validating += ValidateCostAnalysisValues;
        }

        #endregion

        #region Database Update/Insertion Methods

        /// <summary>
        /// Saves all changes made to the form to the database.
        /// </summary>
        /// <param name="displayInformation">Whether or not to update the information label with the status.</param>
        /// <returns>True on success otherwise, false.</returns>
        private bool SaveRecords(bool displayInformation = true)
        {
            var success = true;
            //Get the date ID for the current active date.
            if (_currentActiveDate.DayOfWeek != DayOfWeek.Saturday)
            {
                SetNewRecordDate(_currentActiveDate);
                _currentActiveDate = weekEndingCalendar.SelectionStart;
            }
            var dateId = GetDateId(_currentActiveDate.ToString("d"));
            if (dateId == 0)
            {
                MessageBox.Show(@"Failed to get the date ID number.", @"Invalid ID Number", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            if (displayInformation) informationLabel.Text = "";
            //Create the database interaction objects.
            var dbT = new DatabaseTracker();
            var dbW = new DatabaseWriter(dbT.DatabaseConnectionString);
            DataTable newInsertProjectionsTable;
            DataTable updateProjectionsTable;
            var projectionsTrimmingStatus = ConstructCleanedSalesTable("Projections", dateId, out newInsertProjectionsTable, out updateProjectionsTable);
            if (projectionsTrimmingStatus != TrimmingOperationResult.NoChangesRequired && projectionsTrimmingStatus != TrimmingOperationResult.FailedToTrim)
            {
                //Check to see if the insert table has any items.
                if (newInsertProjectionsTable.Rows.Count > 0)
                {
                    var writerResult = dbW.InsertIntoSalesTable(newInsertProjectionsTable, dbT.DatabaseConnectionString);
                    if (writerResult.GetWritingOperationStatus() != WritingOperationStatus.Failed)
                    {
                        //Spin through the collection and update the affected rows.
                        foreach (var rowIndex in writerResult.GetRowCollection())
                        {
                            projectionsDataGridView.Rows[rowIndex.Key - 1].Cells[(int) SalesTableColumns.Id].Value = rowIndex.Value;
                            projectionsDataGridView.Rows[rowIndex.Key - 1].Cells[(int) SalesTableColumns.IsDirty].Value = false;
                            projectionsDataGridView.Rows[rowIndex.Key - 1].HeaderCell.Style.BackColor = ApplicationColors.EditingSaved;
                        }
                    }
                    else
                    {
                        success = false;
                    }
                }
                //Next check to see if the update table has anything.
                if (updateProjectionsTable.Rows.Count > 0)
                {
                    var writerResult = dbW.UpdateSalesTable(updateProjectionsTable, dbT.DatabaseConnectionString);
                    if (writerResult.GetWritingOperationStatus() != WritingOperationStatus.Failed)
                    {
                        //Spin through the collection and update the affected rows.
                        foreach (var rowIndex in writerResult.GetRowCollection())
                        {
                            //Only reset the IsDirty value to false since the updates when through.
                            projectionsDataGridView.Rows[rowIndex.Key - 1].Cells[(int) SalesTableColumns.IsDirty].Value = false;
                            projectionsDataGridView.Rows[rowIndex.Key - 1].HeaderCell.Style.BackColor = ApplicationColors.EditingSaved;
                        }
                    }
                    else
                    {
                        success = false;
                    }
                }
                if (displayInformation) informationLabel.Text += @"Successfully saved projections." + Environment.NewLine;
            }
            else if (projectionsTrimmingStatus == TrimmingOperationResult.NoChangesRequired)
            {
                if (displayInformation) informationLabel.Text += @"No changes to projections detected." + Environment.NewLine;
            }
            else
            {
                errorLabel.Text += @"Failed to process projections." + Environment.NewLine;
            }
            DataTable inventoryNewTable;
            DataTable inventoryUpdateTable;
            var inventoryTrimmingStatus = ConstructCleanedInventoryTable(dateId, out inventoryNewTable, out inventoryUpdateTable);
            if (inventoryTrimmingStatus != TrimmingOperationResult.NoChangesRequired && inventoryTrimmingStatus != TrimmingOperationResult.FailedToTrim)
            {
                if (inventoryNewTable.Rows.Count > 0)
                {
                    var writerResult = dbW.InsertIntoInventoryTable(inventoryNewTable, dbT.DatabaseConnectionString);
                    if (writerResult.GetWritingOperationStatus() != WritingOperationStatus.Failed)
                    {
                        //Spin through the collection and update the affected rows.
                        foreach (var rowIndex in writerResult.GetRowCollection())
                        {
                            //Only reset the IsDirty value to false since the updates when through.
                            inventoryDataGridView.Rows[rowIndex.Key - 1].Cells[(int) InventoryTableColumns.Id].Value = rowIndex.Value;
                            inventoryDataGridView.Rows[rowIndex.Key - 1].Cells[(int) InventoryTableColumns.IsDirty].Value = false;
                            inventoryDataGridView.Rows[rowIndex.Key - 1].HeaderCell.Style.BackColor = ApplicationColors.EditingSaved;
                        }
                    }
                    else
                    {
                        success = false;
                    }
                }
                //Next check to see if the update table has anything.
                if (inventoryUpdateTable.Rows.Count > 0)
                {
                    var writerResult = dbW.UpdateInventoryTable(inventoryUpdateTable, dbT.DatabaseConnectionString);
                    if (writerResult.GetWritingOperationStatus() != WritingOperationStatus.Failed)
                    {
                        //Spin through the collection and update the affected rows.
                        foreach (var rowIndex in writerResult.GetRowCollection())
                        {
                            //Only reset the IsDirty value to false since the updates when through.
                            inventoryDataGridView.Rows[rowIndex.Key - 1].Cells[(int) InventoryTableColumns.IsDirty].Value = false;
                            inventoryDataGridView.Rows[rowIndex.Key - 1].HeaderCell.Style.BackColor = ApplicationColors.EditingSaved;
                        }
                    }
                    else
                    {
                        success = false;
                    }
                }
                if (displayInformation) informationLabel.Text += @"Successfully saved inventory." + Environment.NewLine;
            }
            else if (inventoryTrimmingStatus == TrimmingOperationResult.NoChangesRequired)
            {
                if (displayInformation) informationLabel.Text += @"No changes to inventory detected." + Environment.NewLine;
            }
            else
            {
                errorLabel.Text += @"Failed to process inventory." + Environment.NewLine;
            }
            DataTable actualSalesNewTable;
            DataTable actualSalesUpdateTable;
            var actualSalesTrimmingStatus = ConstructCleanedSalesTable("ActualSales", dateId, out actualSalesNewTable, out actualSalesUpdateTable);
            if (actualSalesTrimmingStatus != TrimmingOperationResult.NoChangesRequired && actualSalesTrimmingStatus != TrimmingOperationResult.FailedToTrim)
            {
                //Check to see if the insert table has any items.
                if (actualSalesNewTable.Rows.Count > 0)
                {
                    var writerResult = dbW.InsertIntoSalesTable(actualSalesNewTable, dbT.DatabaseConnectionString);
                    if (writerResult.GetWritingOperationStatus() != WritingOperationStatus.Failed)
                    {
                        //Spin through the collection and update the affected rows.
                        foreach (var rowIndex in writerResult.GetRowCollection())
                        {
                            actualSalesDataGridView.Rows[rowIndex.Key - 1].Cells[(int) SalesTableColumns.Id].Value = rowIndex.Value;
                            actualSalesDataGridView.Rows[rowIndex.Key - 1].Cells[(int) SalesTableColumns.IsDirty].Value = false;
                            actualSalesDataGridView.Rows[rowIndex.Key - 1].HeaderCell.Style.BackColor = ApplicationColors.EditingSaved;
                        }
                    }
                    else
                    {
                        success = false;
                    }
                }
                //Next check to see if the update table has anything.
                if (actualSalesUpdateTable.Rows.Count > 0)
                {
                    var writerResult = dbW.UpdateSalesTable(actualSalesUpdateTable, dbT.DatabaseConnectionString);
                    if (writerResult.GetWritingOperationStatus() != WritingOperationStatus.Failed)
                    {
                        //Spin through the collection and update the affected rows.
                        foreach (var rowIndex in writerResult.GetRowCollection())
                        {
                            //Only reset the IsDirty value to false since the updates when through.
                            actualSalesDataGridView.Rows[rowIndex.Key - 1].Cells[(int) SalesTableColumns.IsDirty].Value = false;
                            actualSalesDataGridView.Rows[rowIndex.Key - 1].HeaderCell.Style.BackColor = ApplicationColors.EditingSaved;
                        }
                    }
                    else
                    {
                        success = false;
                    }
                }
                if (displayInformation) informationLabel.Text += @"Successfully saved actual sales." + Environment.NewLine;
            }
            else if (actualSalesTrimmingStatus == TrimmingOperationResult.NoChangesRequired)
            {
                if (displayInformation) informationLabel.Text += @"No changes to actual sales detected." + Environment.NewLine;
            }
            else
            {
                errorLabel.Text += @"Failed to process actual sales." + Environment.NewLine;
            }
            if (!SaveInvoices(dateId, displayInformation))
            {
                success = false;
            }
            if (!SaveComments(dateId, displayInformation))
            {
                success = false;
            }
            if (!SaveWeeklySales(dateId, displayInformation))
            {
                success = false;
            }
            if (!SaveTaxable(dateId, displayInformation))
            {
                success = false;
            }
            if (!SaveCostAnalysis(dateId, displayInformation))
            {
                success = false;
            }
            //If everything went through properly then set the form as not dirty since all changes are saved.
            if (success)
            {
                _isFormDirty = false;
                addRecordButton.Text = @"Update Record";
                Text = @"Modify Record (Current Record: " + _currentActiveDate.ToShortDateString() + @")";
            }
            _formRoll = FormRoll.ModifyRecord;
            weekEndingCalendar.AddBoldedDate(_currentActiveDate);
            weekEndingCalendar.Invalidate();
            return success;
        }

        /// <summary>
        /// Attempts to get the date ID for the date supplied (short date format required, 'Merica!).
        /// Failing that, it will insert the date into the database and return the date ID.
        /// </summary>
        /// <param name="date">The date string.</param>
        /// <returns>The date ID, otherwise zero (0) on fail.</returns>
        private int GetDateId(string date)
        {
            var dbT = new DatabaseTracker();
            var dbW = new DatabaseWriter(dbT.DatabaseConnectionString);
            var dbR = new DatabaseReader();
            var dateId = dbR.RetrieveDateIdByDateString(date, dbT.DatabaseConnectionString);
            //If the ID is zero (0) that means the date isn't in the database so simply insert it.
            if (dateId != 0) return dateId;
            //Try inserting the date string.
            if (dbW.InsertIntoWeekEnding(date))
            {
                dateId = dbR.RetrieveDateIdByDateString(date, dbT.DatabaseConnectionString);
            }
            else
            {
                //The above method reports that it failed to insert the date into the database.
                errorLabel.Text = @"Failed to insert the date '" + date + @"' into the database.";
            }
            return dateId;
        }

        private bool SaveInvoices(int dateId, bool displayInformation = true)
        {
            var dbT = new DatabaseTracker();
            var dbW = new DatabaseWriter(dbT.DatabaseConnectionString);
            //Commit the Invoice table to the database.
            var writerStatus = dbW.ProccessInvoiceTable(invoicesDataGridView, dateId, dbT.DatabaseConnectionString);
            if (writerStatus.GetWritingOperationStatus() == WritingOperationStatus.Failed)
            {
                errorLabel.Text = writerStatus.GetErrorMessage();
                return false;
            }
            if (displayInformation) informationLabel.Text += writerStatus.GetErrorMessage() + Environment.NewLine;
            return true;
        }

        private bool SaveComments(int dateId, bool displayInformation = true)
        {
            var dbT = new DatabaseTracker();
            var dbW = new DatabaseWriter(dbT.DatabaseConnectionString);
            //TODO: If the comments are null but there is a comment ID, delete the comments from the database.
            if (isCommentDirtyCheckBox.Checked)
            {
                if (isCommentDirtyCheckBox.Tag == null)
                {
                    var status = dbW.ProcessComments(commentsTextBox.Text, dateId, dbT.DatabaseConnectionString);
                    if (status.Status == WritingOperationStatus.Failed)
                    {
                        errorLabel.Text = status.ErrorMessage;
                        return false;
                    }
                    if (displayInformation) informationLabel.Text += @"Comment(s) processed successfully." + Environment.NewLine;
                    var id = status.Id;
                    isCommentDirtyCheckBox.Tag = id;
                    isCommentDirtyCheckBox.Text = @"IsCommentDirty (" + id + @")";
                    isCommentDirtyCheckBox.Checked = false;
                    return true;
                }
                else
                {
                    //Check for a null comment box and delete the comment ID from the database.
                    //Then if all is successful clear the Tag in the isCommentsDirty check box.
                    var status = dbW.ProcessComments(commentsTextBox.Text, dateId, dbT.DatabaseConnectionString, int.Parse(isCommentDirtyCheckBox.Tag.ToString()));
                    if (status.Status == WritingOperationStatus.Failed)
                    {
                        errorLabel.Text = status.ErrorMessage;
                        return false;
                    }
                    isCommentDirtyCheckBox.Checked = false;
                    if (displayInformation) informationLabel.Text += @"Comment(s) processed successfully." + Environment.NewLine;
                    return true;
                }
            }
            if (displayInformation) informationLabel.Text += @"No changes detected for the comment(s)" + Environment.NewLine;
            return true;
        }

        private bool SaveWeeklySales(int dateId, bool displayInformation = true)
        {
            var dbT = new DatabaseTracker();
            var dbW = new DatabaseWriter(dbT.DatabaseConnectionString);
            //Begin checking the Weekly Sales.
            if (isWeeklySalesDirtyCheckBox.Checked)
            {
                //Parse out all the text boxes' values in the weekly sales group.
                var weeklySales = new double[8];
                weeklySales[0] = sundayWeeklySalesTextBox.Text == "" ? 0 : double.Parse(sundayWeeklySalesTextBox.Text);
                weeklySales[1] = mondayWeeklySalesTextBox.Text == "" ? 0 : double.Parse(mondayWeeklySalesTextBox.Text);
                weeklySales[2] = tuesdayWeeklySalesTextBox.Text == "" ? 0 : double.Parse(tuesdayWeeklySalesTextBox.Text);
                weeklySales[3] = wednesdayWeeklySalesTextBox.Text == "" ? 0 : double.Parse(wednesdayWeeklySalesTextBox.Text);
                weeklySales[4] = thursdayWeeklySalesTextBox.Text == "" ? 0 : double.Parse(thursdayWeeklySalesTextBox.Text);
                weeklySales[5] = fridayWeeklySalesTextBox.Text == "" ? 0 : double.Parse(fridayWeeklySalesTextBox.Text);
                weeklySales[6] = saturdayWeeklySalesTextBox.Text == "" ? 0 : double.Parse(saturdayWeeklySalesTextBox.Text);
                weeklySales[7] = totalWeeklySalesTextBox.Text == "" ? 0 : double.Parse(totalWeeklySalesTextBox.Text);

                if (isWeeklySalesDirtyCheckBox.Tag == null)
                {
                    //Weekly sales is not in the database.
                    var status = dbW.ProcessWeeklySales(weeklySales, dateId, dbT.DatabaseConnectionString);
                    if (status.Status == WritingOperationStatus.Failed)
                    {
                        errorLabel.Text = status.ErrorMessage;
                        return false;
                    }
                    if (displayInformation) informationLabel.Text += @"Weekly Sales processed successfully." + Environment.NewLine;
                    isWeeklySalesDirtyCheckBox.Tag = status.Id;
                    isWeeklySalesDirtyCheckBox.Text = @"IsWeeklySalesDirty (" + status.Id + @")";
                    isWeeklySalesDirtyCheckBox.Checked = false;
                    return true;
                }
                else
                {
                    var status = dbW.ProcessWeeklySales(weeklySales, dateId, dbT.DatabaseConnectionString, int.Parse(isWeeklySalesDirtyCheckBox.Tag.ToString()));
                    if (status.Status == WritingOperationStatus.Failed)
                    {
                        errorLabel.Text = status.ErrorMessage;
                        return false;
                    }
                    isWeeklySalesDirtyCheckBox.Checked = false;
                    if (displayInformation) informationLabel.Text += @"Weekly Sales updated successfully." + Environment.NewLine;
                    return true;
                }
            }
            if (displayInformation) informationLabel.Text += @"No changes detected for Weekly Sales." + Environment.NewLine;
            return true;
        }

        private bool SaveTaxable(int dateId, bool displayInformation = true)
        {
            var dbT = new DatabaseTracker();
            var dbW = new DatabaseWriter(dbT.DatabaseConnectionString);
            //Begin checking the Taxable.
            if (isTaxableDirtyCheckBox.Checked)
            {
                //Parse out all the text boxes' values in the taxable group.
                var taxable = new double[8];
                taxable[0] = sundayTaxableTextBox.Text == "" ? 0 : double.Parse(sundayTaxableTextBox.Text);
                taxable[1] = mondayTaxableTextBox.Text == "" ? 0 : double.Parse(mondayTaxableTextBox.Text);
                taxable[2] = tuesdayTaxableTextBox.Text == "" ? 0 : double.Parse(tuesdayTaxableTextBox.Text);
                taxable[3] = wednesdayTaxableTextBox.Text == "" ? 0 : double.Parse(wednesdayTaxableTextBox.Text);
                taxable[4] = thursdayTaxableTextBox.Text == "" ? 0 : double.Parse(thursdayTaxableTextBox.Text);
                taxable[5] = fridayTaxableTextBox.Text == "" ? 0 : double.Parse(fridayTaxableTextBox.Text);
                taxable[6] = saturdayTaxableTextBox.Text == "" ? 0 : double.Parse(saturdayTaxableTextBox.Text);
                taxable[7] = totalTaxableTextBox.Text == "" ? 0 : double.Parse(totalTaxableTextBox.Text);

                if (isTaxableDirtyCheckBox.Tag == null)
                {
                    //Taxable is not in the database.
                    var status = dbW.ProcessTaxable(taxable, dateId, dbT.DatabaseConnectionString);
                    if (status.Status == WritingOperationStatus.Failed)
                    {
                        errorLabel.Text = status.ErrorMessage;
                        return false;
                    }
                    if (displayInformation) informationLabel.Text += @"Taxable processed successfully." + Environment.NewLine;
                    isTaxableDirtyCheckBox.Tag = status.Id;
                    isTaxableDirtyCheckBox.Text = @"IsTaxableDirty (" + status.Id + @")";
                    return true;
                }
                else
                {
                    var status = dbW.ProcessTaxable(taxable, dateId, dbT.DatabaseConnectionString, int.Parse(isTaxableDirtyCheckBox.Tag.ToString()));
                    if (status.Status == WritingOperationStatus.Failed)
                    {
                        errorLabel.Text = status.ErrorMessage;
                        return false;
                    }
                    isTaxableDirtyCheckBox.Checked = false;
                    if (displayInformation) informationLabel.Text += @"Taxable updated successfully." + Environment.NewLine;
                    return true;
                }
            }
            if (displayInformation) informationLabel.Text += @"No changes detected for Taxable." + Environment.NewLine;
            return true;
        }

        private bool SaveCostAnalysis(int dateId, bool displayInformation = true)
        {
            var dbT = new DatabaseTracker();
            var dbW = new DatabaseWriter(dbT.DatabaseConnectionString);
            //Begin checking cost analysis.
            if (isCostAnalysisDirtyCheckBox.Checked)
            {
                //Parse out the cost analysis group values.
                var costAnalysis = new double[4];
                costAnalysis[0] = salesPerManHourTextBox.Text == "" ? 0 : double.Parse(salesPerManHourTextBox.Text);
                costAnalysis[1] = salaryPercentageTextBox.Text == "" ? 0 : double.Parse(salaryPercentageTextBox.Text);
                costAnalysis[2] = salaryDollarsTextBox.Text == "" ? 0 : double.Parse(salaryDollarsTextBox.Text);
                costAnalysis[3] = suppliesTextBox.Text == "" ? 0 : double.Parse(suppliesTextBox.Text);

                if (isCostAnalysisDirtyCheckBox.Tag == null)
                {
                    //Weekly sales is not in the database.
                    var status = dbW.ProcessCostAnalysis(costAnalysis, dateId, dbT.DatabaseConnectionString);
                    if (status.Status == WritingOperationStatus.Failed)
                    {
                        errorLabel.Text = status.ErrorMessage;
                        return false;
                    }
                    if (displayInformation) informationLabel.Text += @"Costs Analysis processed successfully." + Environment.NewLine;
                    isCostAnalysisDirtyCheckBox.Tag = status.Id;
                    isCostAnalysisDirtyCheckBox.Text = @"IsCostAnalysisDirty (" + status.Id + @")";
                    isCostAnalysisDirtyCheckBox.Checked = false;
                    return true;
                }
                else
                {
                    var status = dbW.ProcessCostAnalysis(costAnalysis, dateId, dbT.DatabaseConnectionString, int.Parse(isCostAnalysisDirtyCheckBox.Tag.ToString()));
                    if (status.Status == WritingOperationStatus.Failed)
                    {
                        errorLabel.Text = status.ErrorMessage;
                        return false;
                    }
                    isCostAnalysisDirtyCheckBox.Checked = false;
                    if (displayInformation) informationLabel.Text += @"Cost Analysis updated successfully." + Environment.NewLine;
                    return true;
                }
            }
            if (displayInformation) informationLabel.Text += @"No changes made to Cost Analysis." + Environment.NewLine;
            return true;
        }

        #endregion

        #region Table Trimming Operations

        private TrimmingOperationResult ConstructCleanedSalesTable(string tableName, int dateId, out DataTable newInsertTable, out DataTable updateTable)
        {
            //Create two DataTables one for the new items to be added to the database
            //and one for items that have to be updated.
            //New Sales Table Layout (Based on the Database's Physical Layout)
            //0:Sold 1:SalePrice 2:TotalSales 3:cost 4:ProfitReturn 5:TotalProfitReturn
            //6:FK_AdItemID 7:RowAttribute 8:FK_AdSpecialGroupName (ID) 9:RowPosition (not index based)
            //10:FK_DateID
            newInsertTable = new DataTable(tableName);
            //Update Sales Table Layout (Based on the Database's Physical Layout)
            //0:ID 1:Sold 2:SalePrice 3:TotalSales 4:cost 5:ProfitReturn 6:TotalProfitReturn
            //7:FK_AdItemID 8:RowAttribute 9:FK_AdSpecialGroupName (ID) 10:RowPosition (not index based)
            //11:FK_DateID
            updateTable = new DataTable(tableName);
            //Create a reference to the DataGridView that will be used (either Projections or Actual Sales).
            var dataGridView = tableName == "Projections" ? projectionsDataGridView : actualSalesDataGridView;
            //Construct a list of column names for the projections/actual sales DataGridViews and the inventory DataGirdView.
            string[] saleColumnNames =
            {
                "ID", "Sold", "SalePrice", "TotalSales", "Cost", "ProfitReturn", "TotalProfitReturn", "AdItemID", "RowAttribute", "AdSpecialID", "RowPosition", "DateID"
            };
            foreach (var columnName in saleColumnNames)
            {
                if (columnName == "ID") continue;
                var column = new DataColumn(columnName);
                newInsertTable.Columns.Add(column);
            }
            foreach (var columnName in saleColumnNames)
            {
                var column = new DataColumn(columnName);
                updateTable.Columns.Add(column);
            }
            //Create the database interaction objects.
            var databaseTracker = new DatabaseTracker();
            var databaseReader = new DatabaseReader();
            var databaseWriter = new DatabaseWriter(databaseTracker.DatabaseConnectionString);
            var adSpecialId = 0; //Entries in the database are not allowed to be zero (unique ID wise that is).
            //Grab the ad special ID, assuming there is one.
            if (_adSpecialIndex != -1)
            {
                //An ad special does exist so grab its ID from the database.
                int.TryParse(databaseReader.RetrieveGroupIdByString(dataGridView.Rows[_adSpecialIndex].Cells[(int) SalesTableColumns.AdItem].EditedFormattedValue.ToString(), databaseTracker.DatabaseConnectionString), out adSpecialId);
            }
            //Begin spinning through all the rows in the projections table.
            foreach (DataGridViewRow row in dataGridView.Rows)
            {
                //Always check for new row.
                if (row.IsNewRow) break;
                //Check to see if the current row is the ad special row.
                if (row.Index == _adSpecialIndex)
                {
                    continue;
                }
                //Check to see if the row is dirty.
                if (!(bool) row.Cells[(int) SalesTableColumns.IsDirty].EditedFormattedValue)
                {
                    //If it is not then continue on to the next row.
                    continue;
                }
                //Try grabbing the row's ID number (the number that it is in the database).
                var rowIdNumber = row.Cells[(int) SalesTableColumns.Id].EditedFormattedValue.ToString() == "" ? 0 : int.Parse(row.Cells[(int) SalesTableColumns.Id].EditedFormattedValue.ToString());
                //Grab the ad item ID.
                var adItemId = int.Parse(databaseReader.RetrieveAdItemId(row.Cells[(int) SalesTableColumns.AdItem].EditedFormattedValue.ToString(), databaseTracker.DatabaseConnectionString));
                //If the return value is zero (0) then the ad item is not in the database so try to add it.
                if (adItemId == 0)
                {
                    //Add the item to the database.
                    adItemId = databaseWriter.InsertNewAdItem(row.Cells[(int) SalesTableColumns.AdItem].EditedFormattedValue.ToString());
                    if (adItemId == 0)
                    {
                        //TODO: Throw an exception, this can not be allowed.
                        //AdItemInsertionFailedException
                        newInsertTable.Rows.Clear(); //Work around for now
                        updateTable.Rows.Clear();
                        errorLabel.Text = @"Failed to ad item to database.";
                        return TrimmingOperationResult.FailedToTrim;
                    }
                }
                //Determine the row's attribute.
                var rowAttribute = 0; //Zero (0) means no grouping, its not a header nor a member.
                if ((bool) row.Cells[(int) SalesTableColumns.IsHeaderRow].EditedFormattedValue && !(bool) row.Cells[(int) SalesTableColumns.IsMemberRow].EditedFormattedValue)
                {
                    rowAttribute = 1;
                }
                else if ((bool) row.Cells[(int) SalesTableColumns.IsMemberRow].EditedFormattedValue && !(bool) row.Cells[(int) SalesTableColumns.IsHeaderRow].EditedFormattedValue)
                {
                    rowAttribute = 2;
                }
                //Add the values to their respective data table.
                if (rowIdNumber == 0)
                {
                    //This table is full of data not in the database so the ID column isn't needed.
                    var newRow = new object[11];
                    newRow[0] = row.Cells[(int) SalesTableColumns.Sold].EditedFormattedValue.ToString(); //Sold, is string
                    newRow[1] = row.Cells[(int) SalesTableColumns.SalePrice].EditedFormattedValue.ToString(); //SalePrice, is string
                    newRow[2] = row.Cells[(int) SalesTableColumns.TotalSales].EditedFormattedValue.ToString() == "" ? 0 : double.Parse(row.Cells[(int) SalesTableColumns.TotalSales].EditedFormattedValue.ToString()); //TotalSales, must be a number
                    newRow[3] = row.Cells[(int) SalesTableColumns.Cost].EditedFormattedValue.ToString() == "" ? 0 : double.Parse(row.Cells[(int) SalesTableColumns.Cost].EditedFormattedValue.ToString()); //Cost, must be a number
                    newRow[4] = row.Cells[(int) SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString() == "" ? 0 : double.Parse(row.Cells[(int) SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString()); //ProfitReturn, must be a number
                    newRow[5] = row.Cells[(int) SalesTableColumns.TotalProfitReturn].EditedFormattedValue.ToString() == "" ? 0 : double.Parse(row.Cells[(int) SalesTableColumns.TotalProfitReturn].EditedFormattedValue.ToString()); //TotalProfitReturn, must be a number
                    newRow[6] = adItemId;
                    newRow[7] = rowAttribute;
                    if (_adSpecialIndex != -1 && row.Index > _adSpecialIndex)
                    {
                        newRow[8] = adSpecialId;
                        newRow[9] = row.Index; //"Subtract" one since we don't need to acknowledge the Ad Special Row's existence.
                    }
                    else
                    {
                        newRow[8] = 0;
                        newRow[9] = row.Index + 1;
                    }
                    newRow[10] = dateId;
                    newInsertTable.Rows.Add(newRow);
                }
                else
                {
                    //This table is full of data that is already in the database.
                    var newRow = new object[12];
                    newRow[0] = rowIdNumber;
                    newRow[1] = row.Cells[(int) SalesTableColumns.Sold].EditedFormattedValue.ToString(); //Sold, is string
                    newRow[2] = row.Cells[(int) SalesTableColumns.SalePrice].EditedFormattedValue.ToString(); //SalePrice, is string
                    newRow[3] = row.Cells[(int) SalesTableColumns.TotalSales].EditedFormattedValue.ToString() == "" ? 0 : double.Parse(row.Cells[(int) SalesTableColumns.TotalSales].EditedFormattedValue.ToString()); //TotalSales, must be a number
                    newRow[4] = row.Cells[(int) SalesTableColumns.Cost].EditedFormattedValue.ToString() == "" ? 0 : double.Parse(row.Cells[(int) SalesTableColumns.Cost].EditedFormattedValue.ToString()); //Cost, must be a number
                    newRow[5] = row.Cells[(int) SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString() == "" ? 0 : double.Parse(row.Cells[(int) SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString()); //ProfitReturn, must be a number
                    newRow[6] = row.Cells[(int) SalesTableColumns.TotalProfitReturn].EditedFormattedValue.ToString() == "" ? 0 : double.Parse(row.Cells[(int) SalesTableColumns.TotalProfitReturn].EditedFormattedValue.ToString()); //TotalProfitReturn, must be a number
                    newRow[7] = adItemId;
                    newRow[8] = rowAttribute;
                    if (_adSpecialIndex != -1 && row.Index > _adSpecialIndex)
                    {
                        newRow[9] = adSpecialId;
                        newRow[10] = row.Index; //"Subtract" one since we don't need to acknowledge the Ad Special Row's existence.
                    }
                    else
                    {
                        newRow[9] = 0;
                        newRow[10] = row.Index + 1;
                    }
                    newRow[11] = dateId;
                    updateTable.Rows.Add(newRow);
                }
            }

            if (newInsertTable.Rows.Count == 0 && updateTable.Rows.Count == 0)
            {
                return TrimmingOperationResult.NoChangesRequired;
            }
            if (newInsertTable.Rows.Count > 0 && updateTable.Rows.Count == 0)
            {
                return TrimmingOperationResult.CreatedNewInsertionTable;
            }
            if (newInsertTable.Rows.Count == 0 && updateTable.Rows.Count > 0)
            {
                return TrimmingOperationResult.CreatedUpdateTable;
            }
            return TrimmingOperationResult.CreatedNewInsertionAndUpdateTables;
        }

        private TrimmingOperationResult ConstructCleanedInventoryTable(int dateId, out DataTable newInsertTable, out DataTable updateTable)
        {
            //Create two DataTables one for the new items to be added to the database
            //and one for items that have to be updated.
            //New inventory Table Layout (Based on the Database's Physical Layout)
            //0:Beginning Inventory 1:Received 2:Total Inventory 3:Ending Inventory
            //4:AdItemID 5:RowAttribute 6:GroupID 7:RowPosition 8:DateID
            newInsertTable = new DataTable("Inventory");
            //Update Sales Table Layout (Based on the Database's Physical Layout)
            //0:ID 1:BeginningInventory 2:Received 3:TotalInventory 4:EndingInventory
            //5:AdItemID 6:RowAttribute 7:GroupID 8:RowPosition 9:DateID
            updateTable = new DataTable("Inventory");
            //Construct a list of column names for the projections/actual sales DataGridViews and the inventory DataGirdView.
            string[] saleColumnNames =
            {
                "ID", "Sold", "SalePrice", "TotalSales", "Cost", "ProfitReturn", "TotalProfitReturn", "AdItemID", "RowAttribute", "AdSpecialID", "RowPosition", "DateID"
            };
            foreach (var columnName in saleColumnNames)
            {
                if (columnName == "ID") continue;
                var column = new DataColumn(columnName);
                newInsertTable.Columns.Add(column);
            }
            foreach (var columnName in saleColumnNames)
            {
                var column = new DataColumn(columnName);
                updateTable.Columns.Add(column);
            }
            //Create the database interaction objects.
            var databaseTracker = new DatabaseTracker();
            var databaseReader = new DatabaseReader();
            var databaseWriter = new DatabaseWriter(databaseTracker.DatabaseConnectionString);
            var adSpecialId = 0; //Entries in the database are not allowed to be zero (unique ID wise that is).
            //Grab the ad special ID, assuming there is one.
            if (_adSpecialIndex != -1)
            {
                //An ad special does exist so grab its ID from the database.
                int.TryParse(databaseReader.RetrieveGroupIdByString(inventoryDataGridView.Rows[_adSpecialIndex].Cells[(int) InventoryTableColumns.AdItem].EditedFormattedValue.ToString(), databaseTracker.DatabaseConnectionString), out adSpecialId);
            }
            //Begin spinning through all the rows in the projections table.
            foreach (DataGridViewRow row in inventoryDataGridView.Rows)
            {
                //Always check for new row.
                if (row.IsNewRow) break;
                //Check to see if the current row is the ad special row.
                if (row.Index == _adSpecialIndex)
                {
                    //LogConsole.WriteToLog(FrmLogConsole.Level.Verbose, "Ad Special Row found at index " + row.Index + ".");
                    continue;
                }
                //Check to see if the row is dirty.
                if (!(bool) row.Cells[(int) InventoryTableColumns.IsDirty].EditedFormattedValue)
                {
                    //If it is not then continue on to the next row.
                    continue;
                }
                //Try grabbing the row's ID number (the number that it is in the database).
                var rowIdNumber = row.Cells[(int) InventoryTableColumns.Id].EditedFormattedValue.ToString() == "" ? 0 : int.Parse(row.Cells[(int) InventoryTableColumns.Id].EditedFormattedValue.ToString());
                //Grab the ad item ID.
                var adItemId = int.Parse(databaseReader.RetrieveAdItemId(row.Cells[(int) InventoryTableColumns.AdItem].EditedFormattedValue.ToString(), databaseTracker.DatabaseConnectionString));
                //If the return value is zero (0) then the ad item is not in the database so try to add it.
                if (adItemId == 0)
                {
                    //Add the item to the database.
                    adItemId = databaseWriter.InsertNewAdItem(row.Cells[(int) InventoryTableColumns.AdItem].EditedFormattedValue.ToString());
                    if (adItemId == 0)
                    {
                        //TODO: Throw an exception, this can not be allowed.
                        //AdItemInsertionFailedException
                        newInsertTable.Rows.Clear(); //Work around for now
                        updateTable.Rows.Clear();
                        return TrimmingOperationResult.FailedToTrim;
                    }
                }
                //Determine the row's attribute.
                var rowAttribute = 0; //Zero (0) means no grouping, its not a header nor a member.
                if ((bool) row.Cells[(int) InventoryTableColumns.IsHeaderRow].EditedFormattedValue && !(bool) row.Cells[(int) InventoryTableColumns.IsMemberRow].EditedFormattedValue)
                {
                    rowAttribute = 1;
                }
                else if ((bool) row.Cells[(int) InventoryTableColumns.IsMemberRow].EditedFormattedValue && !(bool) row.Cells[(int) InventoryTableColumns.IsHeaderRow].EditedFormattedValue)
                {
                    rowAttribute = 2;
                }
                //Add the values to their respective data table.
                if (rowIdNumber == 0)
                {
                    //This table is full of data not in the database so the ID column isn't needed.
                    var newRow = new object[9];
                    newRow[0] = row.Cells[(int) InventoryTableColumns.BeginningInventory].EditedFormattedValue.ToString(); //Beginning inventory, is string
                    newRow[1] = row.Cells[(int) InventoryTableColumns.Recieved].EditedFormattedValue.ToString(); //Received, is string
                    newRow[2] = row.Cells[(int) InventoryTableColumns.Total].EditedFormattedValue.ToString(); //Total inventory. is string
                    newRow[3] = row.Cells[(int) InventoryTableColumns.EndingInventory].EditedFormattedValue.ToString(); //Ending inventory, is string
                    newRow[4] = adItemId;
                    newRow[5] = rowAttribute;
                    if (_adSpecialIndex != -1 && row.Index > _adSpecialIndex)
                    {
                        newRow[6] = adSpecialId;
                        newRow[7] = row.Index; //"Subtract" one since we don't need to acknowledge the Ad Special Row's existence.
                    }
                    else
                    {
                        newRow[6] = 0;
                        newRow[7] = row.Index + 1;
                    }
                    newRow[8] = dateId;
                    newInsertTable.Rows.Add(newRow);
                }
                else
                {
                    //This table is full of data that is already in the database.
                    var newRow = new object[10];
                    newRow[0] = rowIdNumber;
                    newRow[1] = row.Cells[(int) InventoryTableColumns.BeginningInventory].EditedFormattedValue.ToString(); //Beginning inventory, is string
                    newRow[2] = row.Cells[(int) InventoryTableColumns.Recieved].EditedFormattedValue.ToString(); //Received, is string
                    newRow[3] = row.Cells[(int) InventoryTableColumns.Total].EditedFormattedValue.ToString(); //Total inventory. is string
                    newRow[4] = row.Cells[(int) InventoryTableColumns.EndingInventory].EditedFormattedValue.ToString(); //Ending inventory, is string
                    newRow[5] = adItemId;
                    newRow[6] = rowAttribute;
                    if (_adSpecialIndex != -1 && row.Index > _adSpecialIndex)
                    {
                        newRow[7] = adSpecialId;
                        newRow[8] = row.Index; //"Subtract" one since we don't need to acknowledge the Ad Special Row's existence.
                    }
                    else
                    {
                        newRow[7] = 0;
                        newRow[8] = row.Index + 1;
                    }
                    newRow[9] = dateId;
                    updateTable.Rows.Add(newRow);
                }
            }

            if (newInsertTable.Rows.Count == 0 && updateTable.Rows.Count == 0)
            {
                return TrimmingOperationResult.NoChangesRequired;
            }
            if (newInsertTable.Rows.Count > 0 && updateTable.Rows.Count == 0)
            {
                return TrimmingOperationResult.CreatedNewInsertionTable;
            }
            if (newInsertTable.Rows.Count == 0 && updateTable.Rows.Count > 0)
            {
                return TrimmingOperationResult.CreatedUpdateTable;
            }
            return TrimmingOperationResult.CreatedNewInsertionAndUpdateTables;
        }

        #endregion

        private void addRecordButton_Click(object sender, EventArgs e)
        {
            SaveRecords();
        }

        private void NewModifyRecord_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!_isFormDirty) return;
            var result = MessageBox.Show(@"Would you like to save the changes you have made?", @"Save Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
            switch (result)
            {
                case DialogResult.Yes:
                    SaveRecords(false);
                    break;
                case DialogResult.Cancel:
                    e.Cancel = true;
                    break;
            }
        }

        private void createNewRecordEditMainMenu_Click(object sender, EventArgs e)
        {
            if (_isFormDirty)
            {
                var result = MessageBox.Show(@"Do you wish to save the changes you have made before creating a new record?", @"Save Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

                if (result == DialogResult.Yes)
                {
                    SaveRecords();
                }
                else if (result == DialogResult.Cancel)
                {
                    return;
                }
            }

            ClearFormState();
            addRecordButton.Text = @"Add Record";
            var databaseTracker = new DatabaseTracker();
            var databaseReader = new DatabaseReader();
            _currentActiveDate = databaseReader.RetrieveMostRecentDate(databaseTracker.DatabaseConnectionString).AddDays(7);
            weekEndingCalendar.SelectionStart = _currentActiveDate;
            Text = @"Add New Record (Current Date: " + _currentActiveDate.ToShortDateString() + @")";
            informationLabel.Text = string.Empty;
            errorLabel.Text = string.Empty;
            _formRoll = FormRoll.AddRecord;
        }

        private void NormalizeApcTables()
        {
            //Disable validation events in the APC tables.
            projectionsDataGridView.CellEnter -= StoreBeginningCellValue;
            inventoryDataGridView.CellEnter -= StoreBeginningCellValue;
            actualSalesDataGridView.CellEnter -= StoreBeginningCellValue;
            projectionsDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
            inventoryDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
            actualSalesDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
            projectionsDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
            inventoryDataGridView.CellValidating -= ValidateInventoryCellContents;
            actualSalesDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
            projectionsDataGridView.RowValidating -= ValidateProjectedRow;
            inventoryDataGridView.RowValidating -= ValidateInventoryRow;
            actualSalesDataGridView.RowValidating -= ValidateActualSalesRow;
            //END DISABLE EVENTS
            informationLabel.Text = string.Empty;
            errorLabel.Text = @"Tables unbalanced, attempting repairs.";
            var dataGridView = projectionsDataGridView.RowCount > inventoryDataGridView.RowCount
                ? projectionsDataGridView
                : inventoryDataGridView;
            dataGridView = dataGridView.RowCount > actualSalesDataGridView.RowCount
                ? dataGridView
                : actualSalesDataGridView;
            var maxRowCount = dataGridView.RowCount;
            //MessageBox.Show(dataGridView.Name);
            var rowParser = new RowParsing();
            for (var i = 0; i < maxRowCount; i++)
            {
                if (rowParser.GetRowAttribute(dataGridView.Rows[i]) == RowAttribute.AdSpecialRow)
                {
                    _adSpecialIndex = i;
                }

                if (maxRowCount != projectionsDataGridView.RowCount)
                {
                    if (i < projectionsDataGridView.RowCount)
                    {
                        //Force update the existing row with whatever the fuller table has.
                        if (
                            projectionsDataGridView.Rows[i].Cells[(int) SalesTableColumns.AdItem].EditedFormattedValue !=
                            dataGridView.Rows[i].Cells[(int) SalesTableColumns.AdItem].EditedFormattedValue)
                        {
                            //Force the creation of the NewRow in the DataGridView.
                            projectionsDataGridView.Rows.Add();
                        }
                    }
                    else
                    {
                        //Add the new row to the projections DataGridView
                        projectionsDataGridView.Rows.Insert(i - 1);
                    }

                    projectionsDataGridView.Rows[i].Cells[(int)SalesTableColumns.AdItem].Value = dataGridView.Rows[i].Cells[(int)SalesTableColumns.AdItem].EditedFormattedValue;
                    projectionsDataGridView.Rows[i].Cells[(int)SalesTableColumns.IsDirty].Value = true;
                    if (i != _adSpecialIndex)
                    {
                        projectionsDataGridView.Rows[i].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                    }                 

                    if (dataGridView == actualSalesDataGridView)
                    {
                        projectionsDataGridView.Rows[i].Cells[(int) SalesTableColumns.SalePrice].Value =
                            dataGridView.Rows[i].Cells[(int) SalesTableColumns.SalePrice].EditedFormattedValue;
                        projectionsDataGridView.Rows[i].Cells[(int)SalesTableColumns.Cost].Value = dataGridView.Rows[i].Cells[(int)SalesTableColumns.Cost].EditedFormattedValue;
                        projectionsDataGridView.Rows[i].Cells[(int)SalesTableColumns.ProfitReturn].Value = dataGridView.Rows[i].Cells[(int)SalesTableColumns.ProfitReturn].EditedFormattedValue;
                    }
                }

                if (maxRowCount != inventoryDataGridView.RowCount)
                {
                    if (i < inventoryDataGridView.RowCount)
                    {
                        //Force update the existing row with whatever the fuller table has.
                        if (
                            inventoryDataGridView.Rows[i].Cells[(int)InventoryTableColumns.AdItem].EditedFormattedValue !=
                            dataGridView.Rows[i].Cells[(int)InventoryTableColumns.AdItem].EditedFormattedValue)
                        {
                            inventoryDataGridView.Rows.Add();
                        }
                    }
                    else
                    {
                        //Add the new row to the projections DataGridView
                        inventoryDataGridView.Rows.Insert(i - 1);
                    }

                    inventoryDataGridView.Rows[i].Cells[(int)InventoryTableColumns.AdItem].Value = dataGridView.Rows[i].Cells[(int)InventoryTableColumns.AdItem].EditedFormattedValue;
                    inventoryDataGridView.Rows[i].Cells[(int)InventoryTableColumns.IsDirty].Value = true;
                    if (i != _adSpecialIndex)
                    {
                        inventoryDataGridView.Rows[i].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                    }
                }

                if (maxRowCount != actualSalesDataGridView.RowCount)
                {
                    if (i < actualSalesDataGridView.RowCount)
                    {
                        //Force update the existing row with whatever the fuller table has.
                        if (
                            actualSalesDataGridView.Rows[i].Cells[(int)SalesTableColumns.AdItem].EditedFormattedValue !=
                            dataGridView.Rows[i].Cells[(int)SalesTableColumns.AdItem].EditedFormattedValue)
                        {
                            //Force the creation of the NewRow in the DataGridView.
                            actualSalesDataGridView.Rows.Add();
                        }
                    }
                    else
                    {
                        //Add the new row to the projections DataGridView
                        actualSalesDataGridView.Rows.Insert(i - 1);
                    }

                    actualSalesDataGridView.Rows[i].Cells[(int)SalesTableColumns.AdItem].Value = dataGridView.Rows[i].Cells[(int)SalesTableColumns.AdItem].EditedFormattedValue;
                    actualSalesDataGridView.Rows[i].Cells[(int)SalesTableColumns.IsDirty].Value = true;
                    if (i != _adSpecialIndex)
                    {
                        actualSalesDataGridView.Rows[i].HeaderCell.Style.BackColor = ApplicationColors.PendingEdit;
                    }

                    if (dataGridView == projectionsDataGridView)
                    {
                        actualSalesDataGridView.Rows[i].Cells[(int)SalesTableColumns.SalePrice].Value =
                            dataGridView.Rows[i].Cells[(int)SalesTableColumns.SalePrice].EditedFormattedValue;
                        actualSalesDataGridView.Rows[i].Cells[(int)SalesTableColumns.Cost].Value = dataGridView.Rows[i].Cells[(int)SalesTableColumns.Cost].EditedFormattedValue;
                        actualSalesDataGridView.Rows[i].Cells[(int)SalesTableColumns.ProfitReturn].Value = dataGridView.Rows[i].Cells[(int)SalesTableColumns.ProfitReturn].EditedFormattedValue;
                    }
                }
            }
            informationLabel.Text = @"Tables balanced.";
            //Update the NewRow's header cell text.
            projectionsDataGridView.Rows[projectionsDataGridView.RowCount - 1].HeaderCell.Value = projectionsDataGridView.RowCount.ToString();
            inventoryDataGridView.Rows[inventoryDataGridView.RowCount - 1].HeaderCell.Value = inventoryDataGridView.RowCount.ToString();
            actualSalesDataGridView.Rows[actualSalesDataGridView.RowCount - 1].HeaderCell.Value = actualSalesDataGridView.RowCount.ToString();
            //Enable validation events in the APC tables.
            projectionsDataGridView.CellEnter += StoreBeginningCellValue;
            inventoryDataGridView.CellEnter += StoreBeginningCellValue;
            actualSalesDataGridView.CellEnter += StoreBeginningCellValue;
            projectionsDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            inventoryDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            actualSalesDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
            projectionsDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            inventoryDataGridView.CellValidating += ValidateInventoryCellContents;
            actualSalesDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
            projectionsDataGridView.RowValidating += ValidateProjectedRow;
            inventoryDataGridView.RowValidating += ValidateInventoryRow;
            actualSalesDataGridView.RowValidating += ValidateActualSalesRow;
            //END ENABLE EVENTS
        }

        private void ClearRow(int rowIndex)
        {
            foreach (DataGridViewCell cell in projectionsDataGridView.Rows[rowIndex].Cells)
            {
                if (cell.ColumnIndex == (int) SalesTableColumns.AdItem) continue;
                var actualSalesCell = actualSalesDataGridView.Rows[rowIndex].Cells[cell.ColumnIndex];
                if (cell.ColumnIndex < (int) SalesTableColumns.IsHeaderRow)
                {
                    cell.Value = string.Empty;
                    actualSalesCell.Value = string.Empty;
                }
                else
                {
                    cell.Value = false;
                    actualSalesCell.Value = false;
                }
            }

            foreach (DataGridViewCell cell in inventoryDataGridView.Rows[rowIndex].Cells)
            {
                if (cell.ColumnIndex == (int)InventoryTableColumns.AdItem) continue;
                if (cell.ColumnIndex < (int)InventoryTableColumns.IsHeaderRow)
                {
                    cell.Value = string.Empty;
                }
                else
                {
                    cell.Value = false;
                }
            }
        }

        private enum FormRoll
        {
            AddRecord = 0,
            ModifyRecord = 1
        }
    }
}
