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

namespace AdvertsingProfitControl
{
    public partial class frmAddRecord : Form
    {
        //http://stackoverflow.com/questions/392397/why-do-we-use-arrays-instead-of-other-data-structures Structures of arrays.
        //Reordering rows: http://stackoverflow.com/questions/1620947/how-could-i-drag-and-drop-datagridview-rows-under-each-other/1623968#1623968
        private int _gLastRowHeaderIndex = -1;
        //Keep track of the AdSpecial row's index, both table's AdSpecial row will have the same row index value.
        private int _gAdSpecialIndex = -1;
        //Create an object to interact with the logging form.
        readonly LogConsole _gLogConsole = LogConsole.GetStaticInstance;

        private AutoCompleteStringCollection _gAdItemCollection = new AutoCompleteStringCollection();
        private AutoCompleteStringCollection _gSupplierCollection = new AutoCompleteStringCollection();
        //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.
        //Structure: "ADITEMSTRING":"SECTIONUSED" Section used means what side of the AdSpecialRow, one (1) being before and two (2) being after.
        private List<String>  _gUsedAdItems = new List<String>();
        //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 _gLastAdItemEntered = "";
        
        public frmAddRecord()
        {
            InitializeComponent();
        }

        private void frmAddRecord_Load(object sender, EventArgs e)
        {
            //Start by grabbing all the AdItems and putting them into memory.
            var dataBaseRetrieval = new RetrieveFromDatabase();
            _gAdItemCollection = dataBaseRetrieval.GetAdItemsSuggestionList();
            weekEndingDateMaskedTextBox.Leave += OnWeekEndingDateMaskedTextBoxLeave;
            //Now pull all the suppliers into memory.
            _gSupplierCollection = dataBaseRetrieval.GetSupplierSuggestionList();

            //Event handlers for the Projections DataGridView
            projectionsDataGridView.CellValidating += OnCellValidating;
            projectionsDataGridView.RowEnter += DetectAndDisplayIncompleteRows;
            projectionsDataGridView.RowLeave += OnRowLeave;
            projectionsDataGridView.RowsAdded += DisplayRowNumbers;
            projectionsDataGridView.RowsRemoved += OnRowRemoved;
            projectionsDataGridView.UserDeletingRow += OnRowRemoving;
            projectionsDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
            projectionsDataGridView.RowValidating += UpdateInventoryActualSalesDataGridView;

            //Event handlers for the Inventory / Actual Sales DataGridView
            inventoryActualSalesDataGridView.CellValidating += OnCellValidating;
            inventoryActualSalesDataGridView.RowEnter += DetectAndDisplayIncompleteRows;
            inventoryActualSalesDataGridView.RowLeave += OnRowLeave;
            inventoryActualSalesDataGridView.RowsAdded += DisplayRowNumbers;
            inventoryActualSalesDataGridView.RowsRemoved += OnRowRemoved;
            inventoryActualSalesDataGridView.UserDeletingRow += OnRowRemoving;
            inventoryActualSalesDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
            inventoryActualSalesDataGridView.RowValidating += UpdateProjectionsDataGridView;

            //Event handlers for the Suppliers DataGridView
            suppliersDataGridView.CellValidating += SupplierOnCellValidating;
            suppliersDataGridView.EditingControlShowing += DisplaySupplierNameAutoComplete;

            //Event handlers for the Weekly Sales DataGridView
            weeklySalesDataGridView.CellValidating += FormatWeeklySalesOnCellValidating;

            //Builds the DataGridViews
            ConstructDataGridViews();
        }

        //Begin DataGridView event handlers
        /// <summary>
        /// Event Used: Leave
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnWeekEndingDateMaskedTextBoxLeave(object sender, EventArgs e)
        {
            string input = weekEndingDateMaskedTextBox.Text;
            input = input.Replace(' ', '0');
            DateTime processedDate;
            if (!DateTime.TryParse(input, out processedDate))
            {
                weekEndingDateMaskedTextBox.Clear();
                MessageBox.Show(@"Failed to convert the date input.", @"Date Conversion Error");
            }
            else
            {
                weekEndingDateMaskedTextBox.Text = input;
            }
        }

        /// <summary>
        /// Event Used: CellValidating
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnCellValidating(object sender, DataGridViewCellValidatingEventArgs e)
        {
            var dataGridView = ((DataGridView)sender);
            TextInfo 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;
            }
            //Check to see if the current column is the ad item column.
            if (e.ColumnIndex == 0)
            {
                string adItemText = dataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString();
                //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 (adItemText.Replace(" ", "") != "")
                {
                    //Clear the error text since there is in fact an item entered.
                    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "";
                    //Check for the reserved character, and remove it if it is found.
                    if (adItemText.Contains(":"))
                    {
                        _gLogConsole.WriteToLog(LogConsole.Level.Warning, "Colon (:) is a reserved character and therefore not allowed to be used in an ad item name.");
                        adItemText = adItemText.Replace(":", "");
                    }
                    //Pretty up the ad item's name by uppercasing the name.
                    adItemText = textInfo.ToTitleCase(adItemText);
                    dataGridView.Rows[e.RowIndex].Cells[0].Value = adItemText;
                    dataGridView.RefreshEdit();
                    return;
                }
                else if (e.ColumnIndex == 0 && adItemText == "") //Otherwise, set the error text to inform the user.
                {
                    dataGridView.Rows[e.RowIndex].Cells[0].ErrorText = "Ad Item needed";
                    return;
                }                
            }
            //Make an ugly check for the Inventory / Actual Sales table, and perform logic on the inventory columns to allow 'Bin(s) [0-9]'.
            if (dataGridView.Name == "inventoryActualSalesDataGridView" && e.ColumnIndex > 0 && e.ColumnIndex < 6)
            {
                var invenotryStringCheck = new Regex(@"^[0-9] \bbin(s){0,1}\b", RegexOptions.IgnoreCase);

                if (
                    invenotryStringCheck.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.
                    var input = textInfo.ToTitleCase(dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString());
                    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = input;
                    dataGridView.RefreshEdit();
                    return;
                }
            }

            //Check to see if whether or not the current cell is not in the first column and not empty.
            if (e.ColumnIndex != 0 && !String.IsNullOrEmpty(dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString()))
            {
                //IF so, 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);
                //IF the current cell is in the cost column, check for the string format above, else move to the default method.
                if ((dataGridView.Columns[e.ColumnIndex].Name == "PrSalePrice" || dataGridView.Columns[e.ColumnIndex].Name == "AcSalePrice") && regExpression.IsMatch(dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString()))
                {
                    //Grab the input and split it at the forward slash (/) for formatting.
                    string input = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
                    input = input.Replace(" ", "");
                    //Remove any dollar signs as these cause errors.
                    input = input.Replace("$", "");
                    string[] stringArray = input.Split('/');
                    //Format the last number as Currency, and round it up if necessary.
                    dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = String.Format(@"{0}/{1:C}", stringArray[0], Math.Round(Decimal.Parse(stringArray[1]), 2));
                    //Always refresh edit so the new value shows up to the user.
                    dataGridView.RefreshEdit();
                }
                else
                {
                    //Check to see if the entered value can be parsed to a double, if not then throw an error to the user.
                    double doubleOuts = 0;
                    if (
                        !Double.TryParse(
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString(),
                            out doubleOuts))
                    {
                        _gLogConsole.WriteToLog(LogConsole.Level.Error, "The cell in column " + (e.ColumnIndex + 1) + " row " + (e.RowIndex + 1) + " only allows for numeric input.");
                        MessageBox.Show("Non numeric characters in column " + (e.ColumnIndex + 1) + " are not allowed.", "Invalid Characters Detected");
                        e.Cancel = true;
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = "";
                        dataGridView.RefreshEdit();
                        return;
                    }
                    //Check to see if the current DataGridView is the Inventory / Actual Sales grid or the Projections grid.
                    if (dataGridView.Name == "inventoryActualSalesDataGridView")
                    {
                        //Check to make sure the cell is not in the Inventory / Sold columns.
                        if (e.ColumnIndex > 5)
                        {
                            //IF not, then apply currency formatting to the cell.
                            double value;
                            value = Double.Parse(dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString(), NumberStyles.Currency);
                            //If all goes well, format the string in question by adding commas and decimal points if applicable.
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(value, 2).ToString("N", new CultureInfo("en-US"));
                            //Always refresh edit so the new value shows up to the user.
                            dataGridView.RefreshEdit();
                        }
                    }
                    else
                    {
                        //IF the current column is not the Sold column then apply currency formatting if numeric.
                        if (e.ColumnIndex != 1)
                        {
                            //IF not, then apply currency formatting to the cell.
                            var value = Double.Parse(dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString(), NumberStyles.Currency);
                            //If all goes well, format the string in question by adding commas and decimal points if applicable.
                            dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(value, 2).ToString("N", new CultureInfo("en-US"));
                            //Always refresh edit so the new value shows up to the user.
                            dataGridView.RefreshEdit();
                        }
                    }
                }
            }
        }//End OnCellValidating

        /// <summary>
        /// Event Used: RowEnter
        /// Spins through the index values of incomplete rows and displays to the user 
        /// which ones will not be included in the database if they attempt to add the record.
        /// </summary>
        /// <param name="sender">A reference to the DataGridView that fired the event.</param>
        /// <param name="e"></param>
        private void DetectAndDisplayIncompleteRows(Object sender, DataGridViewCellEventArgs e)
        {
            var message = "";
            var incompleteRows = new List<int>();
            var dataGridView = ((DataGridView)sender);
            var i = 0;
            //Get the indexes of all incomplete rows.
            for (var index = 0; index < dataGridView.Rows.Count; index++)
            {
                var row = dataGridView.Rows[index];
                if (!row.IsNewRow && row.Cells[0].ErrorText != "")
                {
                    incompleteRows.Add(i);
                }
                i++;
            }
            //Make the error message all nice and formatted, purely anesthetic.
            if (incompleteRows.Count <= 0) return;
            {
                i = 0;
                foreach (int index in incompleteRows)
                {   //Formatting for one item in the array.
                    if (incompleteRows.Count == 1)
                    {
                        message = "Row " + (index + 1);
                    }
                    else if (incompleteRows.Count == 2)
                    {   //Formatting for 2 items in the array.
                        if (i == 0)
                        {
                            message = "Rows " + (index + 1) + " and ";
                        }
                        else
                        {
                            message += (index + 1).ToString();
                        }
                    }
                    else
                    {   //Formatting for 3 or more items in the array.
                        if (i == 0) //Checks for the first entry in the array.
                        {
                            message = "Rows " + (index + 1) + ", ";
                        }
                        else if (i < incompleteRows.Count - 2) //Detects all the entries in between.
                        {
                            message += (index + 1) + ", ";
                        }
                        else if (i == incompleteRows.Count - 2) //Detects for the second to last item in the array, so get rid of the comma (,).
                        {
                            message += (index + 1).ToString();
                        }
                        else //Fires only on the last entry in the array.
                        {
                            message += " and " + (index + 1);
                        }
                    }
                    i++;
                }
                message += " will not be included in the database because no ad item is specified.";
                _gLogConsole.WriteToLog(LogConsole.Level.Error, message);
            }
        }   //End DetectAndDisplayIncompleteRows
        /// <summary>
        /// Event Used: RowLeave
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnRowLeave(object sender, DataGridViewCellEventArgs e)
        {
            DataGridView dataGridView = ((DataGridView)sender);

            PaintRowsOnLeave(e.RowIndex, dataGridView);
            //Add in used Ad Items to the list.
            if (dataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() == "") return;
            //Section one (1) detected.
            if (_gAdSpecialIndex == -1 || e.RowIndex < _gAdSpecialIndex)
            {
                if (!_gUsedAdItems.Contains(dataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString()))
                {
                    if (!_gUsedAdItems.Contains(dataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() + ":1"))
                    {
                        _gUsedAdItems.Add(dataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() + ":1");
                    }
                }
            }
            //Section two (2) detected.
            else if (e.RowIndex > _gAdSpecialIndex)
            {
                if (!_gUsedAdItems.Contains(dataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString()))
                {
                    if (!_gUsedAdItems.Contains(dataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() + ":2"))
                    {
                        _gUsedAdItems.Add(dataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() + ":2");
                    }      
                }
            }
        }//End OnRowLeave

        /// <summary>
        /// Event Used: RowsAdded
        /// Adds the row's number to the Row Header Cell whenever a new row is added.
        /// </summary>
        /// <param name="sender">The DataGridView that is getting new rows added to it.</param>
        /// <param name="e">Row arguments, like row index</param>
        private void DisplayRowNumbers(object sender, DataGridViewRowsAddedEventArgs e)
        {
            var table = ((DataGridView)sender);
            table.Rows[e.RowIndex].HeaderCell.Value = (e.RowIndex + 1).ToString();
        }
        /// <summary>
        /// Event Used: RowsRemoved
        /// Fires after the row has been removed. This function removes the other DataGridView's row at the index 
        /// specified in the e.RowIndex argument, ensuring that the tables are uniform as to not cause data corruption in the database.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnRowRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            var dataGridView = ((DataGridView)sender);
            //Provide protection against overflows
            if ((e.RowIndex + 1) > dataGridView.Rows.Count)
            {
                return;
            }
            //Check to see what DataGridView fired the event and delete the other's row at the e.RowIndex.
            if (dataGridView.Name == "projectionsDataGridView")
            {
                //First, un-subscribe the row remove events...
                inventoryActualSalesDataGridView.RowsRemoved -= OnRowRemoved;
                inventoryActualSalesDataGridView.UserDeletingRow -= OnRowRemoving;

                //IF the row count on the passed DataGridView is less then the other table's row count...
                if (projectionsDataGridView.RowCount <= inventoryActualSalesDataGridView.RowCount)
                {
                    //... it is lower so its safe to assume that both tables have the same row that can be removed.
                    if (!inventoryActualSalesDataGridView.Rows[e.RowIndex].IsNewRow)
                    {
                        inventoryActualSalesDataGridView.Rows.RemoveAt(e.RowIndex);  
                    }
                }
                //ELSE IF the row count is larger then the other table's row count...
                else
                {
                    //... 
                }
                //... After all the row removal has been finished re-enable the row removal events.
                inventoryActualSalesDataGridView.RowsRemoved += OnRowRemoved;
                inventoryActualSalesDataGridView.UserDeletingRow += OnRowRemoving;
            }
            else if(dataGridView.Name == "inventoryActualSalesDataGridView" && inventoryActualSalesDataGridView.RowCount <= projectionsDataGridView.RowCount)
            {
                //First, un-subscribe the row remove events....
                projectionsDataGridView.RowsRemoved -= OnRowRemoved;
                projectionsDataGridView.UserDeletingRow -= OnRowRemoving;
                //IF the row count on the passed DataGridView is less then the other table's row count...
                if (inventoryActualSalesDataGridView.RowCount <= projectionsDataGridView.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);
                    }
                }
                else
                {
                    //...
                }
                //... After all the row removal has been finished re-enable the row removal events.
                projectionsDataGridView.RowsRemoved += OnRowRemoved;
                projectionsDataGridView.UserDeletingRow += OnRowRemoving;
            }

            //Reset the row numbers in the tables.
            for (int i = e.RowIndex; i < (dataGridView.RowCount); i++)
            {
                projectionsDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
                inventoryActualSalesDataGridView.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();
            inventoryActualSalesDataGridView.RefreshEdit();
            PaintRowGroups(dataGridView);
        }
        /// <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 OnRowRemoving(object sender, CancelEventArgs e)
        {
            //Create an object that represents the DataGridView that fired the event.
            var dataGridView = ((DataGridView)sender);
            if (dataGridView.CurrentRow == null) return;
            var currentRowIndex = dataGridView.CurrentRow.Index;
            //Remove the ad item from the gUsedAdItem collection, if it exists.
            if (_gAdSpecialIndex == -1 || currentRowIndex < _gAdSpecialIndex)
            {
                //If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
                if (_gUsedAdItems.Contains(dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() + ":1"))
                {
                    //This is section one (1) since it occurs before the adSpecial row.
                    _gUsedAdItems.RemoveAll(I => I == dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() + ":1");
                }
            }
            else if (currentRowIndex > _gAdSpecialIndex)
            {
                //If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
                if (_gUsedAdItems.Contains(dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() + ":2"))
                {
                    //This is section two (2) since it after the adSpecial row.
                    _gUsedAdItems.RemoveAll(I => I == dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() + ":2");
                }
            }
            else if (currentRowIndex == _gAdSpecialIndex)
            {
                //Handle removing the AdSpecial row.
                DialogResult result = MessageBox.Show("Deleting the Ad Special row will remove all rows beneath it.\nDo you wish to continue?", "Clear " + dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue, MessageBoxButtons.YesNo);
                if (result == DialogResult.Yes)
                {
                    //Clear all events that handle row removal from both DataGridViews.
                    //Projections table
                    projectionsDataGridView.RowsRemoved -= OnRowRemoved;
                    projectionsDataGridView.UserDeletingRow -= OnRowRemoving;
                    //Inventory / Actual Sales table
                    inventoryActualSalesDataGridView.RowsRemoved -= OnRowRemoved;
                    inventoryActualSalesDataGridView.UserDeletingRow -= OnRowRemoving;

                    //Now for-each through each row that is underneath the Ad Special row.
                    for (var rowIndex = currentRowIndex; rowIndex < (dataGridView.RowCount -1); rowIndex++)
                    {
                        //If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
                        if (_gUsedAdItems.Contains(dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() + ":2"))
                        {
                            //This is section two (2) since its after the adSpecial row.
                            _gUsedAdItems.RemoveAll(I => I == dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() + ":2");
                        }
                        if (projectionsDataGridView.Rows[currentRowIndex].IsNewRow != true)
                        {
                            projectionsDataGridView.Rows.RemoveAt(currentRowIndex);
                        }
                        if (inventoryActualSalesDataGridView.Rows[currentRowIndex].IsNewRow != true)
                        {
                            inventoryActualSalesDataGridView.Rows.RemoveAt(currentRowIndex);
                        }
                    }
                    //Re-enable all row removal events on both tables.
                    //Projections table
                    projectionsDataGridView.RowsRemoved += OnRowRemoved;
                    projectionsDataGridView.UserDeletingRow += OnRowRemoving;
                    //Inventory / Actual Sales table
                    inventoryActualSalesDataGridView.RowsRemoved += OnRowRemoved;
                    inventoryActualSalesDataGridView.UserDeletingRow += OnRowRemoving;
                    //Reset the gAdSpecialIndex to -1.
                    _gAdSpecialIndex = -1;
                }
                else
                {
                    e.Cancel = true;
                }
            }
        }
        /// <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;
            if (e.Control is DataGridViewTextBoxEditingControl && dataGridView.CurrentCell.ColumnIndex == 0)
            {
                if (autoText != null)
                {
                    //Create a copy of the main ad item list that can be freely manipulated.
                    var customAutoComplete = new AutoCompleteStringCollection();
                    foreach (string item in _gAdItemCollection)
                    {
                        customAutoComplete.Add(item);
                    }
                    
                    //IF the current row is less than the AdSpecialRow, remove all used items with the section 1 attribute.
                    if (_gAdSpecialIndex == -1 || dataGridView.CurrentCell.RowIndex < _gAdSpecialIndex)
                    {
                        if (_gUsedAdItems != null)
                        {
                            foreach (var adItem in _gUsedAdItems)
                            {
                                string[] adItemString = adItem.Split(':');
                                string section = adItemString[1];
                                string adItemToRemove = adItemString[0];
                                if (section == "1")
                                {
                                    customAutoComplete.Remove(adItemToRemove);
                                }
                            }
                        }
                    }
                    //Occurs AFTER the AdSpecial row.
                    else if (dataGridView.CurrentCell.RowIndex > _gAdSpecialIndex)
                    {
                        if (_gUsedAdItems != null)
                        {
                            foreach (string adItem in _gUsedAdItems)
                            {
                                string[] adItemString = adItem.Split(':');
                                string section = adItemString[1];
                                string adItemToRemove = adItemString[0];
                                if (section == "2")
                                {
                                    customAutoComplete.Remove(adItemToRemove);
                                }
                            }
                        }
                    }
                    autoText.AutoCompleteMode = AutoCompleteMode.Suggest;
                    autoText.AutoCompleteSource = AutoCompleteSource.CustomSource;
                    autoText.AutoCompleteCustomSource = customAutoComplete;
                }
            }
            else if(e.Control is DataGridViewTextBoxEditingControl && dataGridView.CurrentCell.ColumnIndex != 0)
            {
                if (autoText != null)
                {
                    autoText.AutoCompleteMode = AutoCompleteMode.None;
                }
            }
        }//End DisplayAutoCompleteOnEditingControlShowing
        /// <summary>
        /// Event Used: RowValidating
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateInventoryActualSalesDataGridView(object sender, DataGridViewCellCancelEventArgs e)
        {
            //IF the current row is a new then simply return, we don't want to copy a new row over.
            if (projectionsDataGridView.Rows[e.RowIndex].IsNewRow)
            {
                return;
            }
            //IF the current row's Ad Item cell (column 0) is not empty then...
            if (projectionsDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() != "")
            {
                
                RowParsing parser = new RowParsing();
                string[] rowContents = new string[11];
                //Check to make sure the user didn't simply leave a row that already exists.
                if (projectionsDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() == inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString())
                {
                    return;
                }
                //Next check to see if the user changed the ad item is the corresponding row.
                if (projectionsDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() != inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString())
                {
                    if (projectionsDataGridView.RowCount == inventoryActualSalesDataGridView.RowCount)
                    {
                        string adItemText = projectionsDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString();
                        inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].Value = adItemText;
                        inventoryActualSalesDataGridView.RefreshEdit();
                        //Remove the last ad item from the UsedAdItem array, the new one will be added in the OnRowLeave event handler.
                        
                        if (_gAdSpecialIndex == -1 || e.RowIndex < _gAdSpecialIndex)
                        {
                            _gUsedAdItems.RemoveAll(I => I == _gLastAdItemEntered + ":1");
                        }
                        else
                        {
                            _gUsedAdItems.RemoveAll(I => I == _gLastAdItemEntered + ":2");
                        }

                        return;
                    }
                }
                _gLastAdItemEntered = projectionsDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString();
                //IF the current row is either a MemberRow OR an AdSpecialRow then only add the item in the first cell, place holder values are not needed.
                if (parser.GetRowAttribute(projectionsDataGridView.Rows[e.RowIndex]) == "MemberRow" || parser.GetRowAttribute(projectionsDataGridView.Rows[e.RowIndex]) == "AdSpecialRow")
                {
                    rowContents[0] = projectionsDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString();
                    inventoryActualSalesDataGridView.Rows.Add(rowContents);
                    PaintRowGroups(inventoryActualSalesDataGridView);
                    return;
                }
                //Spin through the DataGridViewCells in the row and add their contents to an array.
                for (int i = 0; i < 11; i++)
                {
                    //Grab the Ad Item in the first cell and add it into the array.
                    if (i == 0)
                    {
                        rowContents[0] = projectionsDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString();
                    }
                    //Index for the Sale Price cell.
                    else if (i == 6)
                    {
                        //IF the Sale Price cell is empty then place 0.00 into the array as a place holder value (assuming this row is a HeaderRow).
                        if (projectionsDataGridView.Rows[e.RowIndex].Cells[2].EditedFormattedValue.ToString() == "")
                        {
                            rowContents[6] = "0.00";
                        }
                        //ELSE place the value from the Projections table into the array, since Sale Price can be determined before actual data is used.
                        else
                        {
                            rowContents[6] = projectionsDataGridView.Rows[e.RowIndex].Cells[2].EditedFormattedValue.ToString();
                        }
                    }
                    //Index for the Cost cell.
                    else if (i == 8)
                    {
                        //IF the Cost cell is empty then place 0.00 into the array as a place holder value (assuming this row is a HeaderRow).
                        if (projectionsDataGridView.Rows[e.RowIndex].Cells[4].EditedFormattedValue.ToString() == "")
                        {
                            rowContents[8] = "0.00";
                        }
                        //ELSE place the value from the Projections table into the array, since Cost can be determined before actual data is used.
                        else
                        {
                            rowContents[8] = projectionsDataGridView.Rows[e.RowIndex].Cells[4].EditedFormattedValue.ToString();
                        }
                    }
                    else
                    {
                        //Add a place holder value for the cells that aren't Sale Price or Cost.
                        rowContents[i] = "0";
                    }
                }
                inventoryActualSalesDataGridView.Rows.Add(rowContents);
            }
            //ELSE IF the Ad Item cell is empty, then throw an error and inform the user that there must be an Ad Item specified.
            else
            {
                MessageBox.Show("An Ad Item is required.", "Invalid Ad Item");
                projectionsDataGridView.Rows[e.RowIndex].Cells[0].Selected = true;
                e.Cancel = true;
            }
        }
        /// <summary>
        /// Event Used: RowValidating
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateProjectionsDataGridView(object sender, DataGridViewCellCancelEventArgs e)
        {
            if (inventoryActualSalesDataGridView.Rows[e.RowIndex].IsNewRow)
            {
                return;
            }
            if (inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() != "")
            {
                var parser = new RowParsing();
                var rowContents = new string[11];
                //Check to make sure the user didn't simply leave a row that already exists.
                if (projectionsDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() == inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString())
                {
                    return;
                }
                //Next check to see if the user changed the ad item is the corresponding row.
                if (projectionsDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString() != inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString())
                {
                    if (inventoryActualSalesDataGridView.RowCount == projectionsDataGridView.RowCount)
                    {
                        var adItemText = inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString();
                        projectionsDataGridView.Rows[e.RowIndex].Cells[0].Value = adItemText;
                        projectionsDataGridView.RefreshEdit();
                        //Remove the last ad item from the UsedAdItem array, the new one will be added in the OnRowLeave event handler.
                        if (_gAdSpecialIndex == -1 || e.RowIndex < _gAdSpecialIndex)
                        {
                            _gUsedAdItems.RemoveAll(I => I == _gLastAdItemEntered + ":1");
                        }
                        else
                        {
                            _gUsedAdItems.RemoveAll(I => I == _gLastAdItemEntered + ":2");
                        }

                        return;
                    }
                }                        
                _gLastAdItemEntered = inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString();
                //IF the current row is either a MemberRow OR an AdSpecialRow then only ad the item in the first cell, place holder values are not needed.
                if (parser.GetRowAttribute(inventoryActualSalesDataGridView.Rows[e.RowIndex]) == "MemberRow" || parser.GetRowAttribute(inventoryActualSalesDataGridView.Rows[e.RowIndex]) == "AdSpecialRow")
                {
                    rowContents[0] = inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString();
                    projectionsDataGridView.Rows.Add(rowContents);
                    PaintRowGroups(projectionsDataGridView);
                    return;
                }
                //Spin through the DataGridViewCells in the row and add their contents to an array.
                for (var i = 0; i < 11; i++)
                {
                    //Grab the Ad Item in the first cell and add it into the array.
                    if (i == 0)
                    {
                        rowContents[0] = inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].EditedFormattedValue.ToString();
                    }
                    //Index for the Sale Price cell.
                    else if (i == 6)
                    {
                        //IF the Sale Price cell is empty then place 0.00 into the array as a place holder value (assuming this row is a HeaderRow).
                        if (inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[6].EditedFormattedValue.ToString() == "")
                        {
                            rowContents[2] = "0.00";
                        }
                        //ELSE place the value from the Projections table into the array, since Sale Price can be determined before actual data is used.
                        else
                        {
                            rowContents[2] = inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[6].EditedFormattedValue.ToString();
                        }
                    }
                    //Index for the Cost cell.
                    else if (i == 8)
                    {
                        //IF the Cost cell is empty then place 0.00 into the array as a place holder value (assuming this row is a HeaderRow).
                        if (inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[8].EditedFormattedValue.ToString() == "")
                        {
                            rowContents[4] = "0.00";
                        }
                        //ELSE place the value from the Projections table into the array, since Cost can be determined before actual data is used.
                        else
                        {
                            rowContents[4] = inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[8].EditedFormattedValue.ToString();
                        }
                    }
                    else
                    {
                        //Add a place holder value for the cells that aren't Sale Price or Cost.
                        rowContents[i] = "0";
                    }
                }
                projectionsDataGridView.Rows.Add(rowContents);
            }
            else
            {
                MessageBox.Show("An Ad Item is required.", "Invalid Ad Item");
                inventoryActualSalesDataGridView.Rows[e.RowIndex].Cells[0].Selected = true;
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Event Used: CellValidating
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SupplierOnCellValidating(object sender, DataGridViewCellValidatingEventArgs e)
        {
            var dataGridView = ((DataGridView) sender);
            //IF the row that fires the event is a new row, then return. Validation is not required on a new row.
            if(dataGridView.Rows[e.RowIndex].IsNewRow) return;
            if (dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString() == "") return;

            if (e.ColumnIndex == 0)
            {
                var input = new DateTime();
                if (!DateTime.TryParse(dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString(), out input))
                {
                    MessageBox.Show("You must enter a valid date with the format MM/DD/YYYY.", "Invalid Date");
                    e.Cancel = true;
                    return;
                }
                dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = input.ToString("MM/dd/yyyy");
                dataGridView.RefreshEdit();
            }

            if (e.ColumnIndex == 1)
            {
                var input = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
                TextInfo textInfo = new CultureInfo("en-US", false).TextInfo;
                dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = textInfo.ToTitleCase(input);
                dataGridView.RefreshEdit();
            }

            if (e.ColumnIndex == 3 || e.ColumnIndex == 4)
            {
                double input = 0;
                if (
                    !Double.TryParse(
                        dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString(), out input))
                {
                    MessageBox.Show("Only numbers may be entered in this column.", "Non Numeric Value");
                    e.Cancel = true;
                    return;
                }
                
                dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(input, 2).ToString("N", new CultureInfo("en-US"));
                dataGridView.RefreshEdit();
            }
        }

        /// <summary>
        /// Event Used: EditingControlShowing
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DisplaySupplierNameAutoComplete(object sender, DataGridViewEditingControlShowingEventArgs e)
        {
            var dataGridView = ((DataGridView)sender);
            var autoText = e.Control as TextBox;
            //IF the control is in fact an editing control and it is column two (2), then ...
            if (e.Control is DataGridViewTextBoxEditingControl && dataGridView.CurrentCell.ColumnIndex == 1)
            {
                if (autoText != null)
                {
                    autoText.AutoCompleteMode = AutoCompleteMode.Suggest;
                    autoText.AutoCompleteSource = AutoCompleteSource.CustomSource;
                    autoText.AutoCompleteCustomSource = _gSupplierCollection;
                }
            }
        }

        /// <summary>
        /// Event Used: CellValidating
        /// Simply applies formatting to the Weekly Sales table hen the user clicks out of a cell.
        /// Also replaces null cells with zeros to have a value for the database to enter.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FormatWeeklySalesOnCellValidating(object sender, DataGridViewCellValidatingEventArgs e)
        {
            if (weeklySalesDataGridView.Rows[0].Cells[e.ColumnIndex].EditedFormattedValue.ToString() == "0.00" || weeklySalesDataGridView.Rows[0].Cells[e.ColumnIndex].EditedFormattedValue.ToString() == "")
            {
                return;
            }
            double numberToFormat = 0;

            if (Double.TryParse(weeklySalesDataGridView.Rows[0].Cells[e.ColumnIndex].EditedFormattedValue.ToString(), out numberToFormat))
            {

                weeklySalesDataGridView.Rows[0].Cells[e.ColumnIndex].Value = Math.Round(numberToFormat, 2).ToString("N", new CultureInfo("en-US"));
                weeklySalesDataGridView.RefreshEdit();
            }
            else
            {
                MessageBox.Show("Only numeric values can be entered.", "Non Numeric Characters");
                e.Cancel = true;
            }
        }
//========================================= End DataGirdView event handlers =============================================================================
        /// <summary>
        /// Paints rows according to their RowAttribute and detects whether or not they are part of a group.
        /// This function offers a faster method of determining all of this by starting at the row index that 
        /// fired the row leave even. As opposed to the PaintRowGroups function which spins through all the 
        /// DataGridView's rows.
        /// </summary>
        /// <param name="startingIndex">The starting index (the row that fired the RowLeave event)</param>
        /// <param name="dataGridView">A reference to the DataGridView that fired the event.</param>
        private void PaintRowsOnLeave(int startingIndex, DataGridView dataGridView)
        {
            var parser = new RowParsing();
            //If the current row is an Ad Special Row, the color code it and return as nothing further needs to be done.
            if (parser.CheckForGroupKeyWord(dataGridView.Rows[startingIndex].Cells[0].EditedFormattedValue.ToString()) != "NoGroupFound")
            {
                _gAdSpecialIndex = startingIndex;
                dataGridView.Rows[startingIndex].DefaultCellStyle.BackColor = Color.Silver;
                return;
            }
            //Spin through the dataGridView rows starting at the row that fired the event.
            for (var i = startingIndex; i < (dataGridView.RowCount - 1); i++)
            {
                //Get the status of the current row.
                var rowStatus = parser.GetRowAttribute(dataGridView.Rows[i]);
                //IF the current row is a header row...
                if (rowStatus == "HeaderRow")
                {
                    _gLastRowHeaderIndex = i;
                    //Then check to see if the next row is a row header.
                    rowStatus = parser.GetRowAttribute(dataGridView.Rows[i + 1]);
                    //IF so, color code this row as White, since it is not a true group header.
                    if (rowStatus != "HeaderRow")
                    {
                        dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.White;
                    }
                    //IF the current row is not the first row in the data grid (index zero)...
                    if (i > 0)
                    {
                        //The start by checking the previous row's status.
                        rowStatus = parser.GetRowAttribute(dataGridView.Rows[i - 1]);
                        //IF the previous row is a header row OR if the previous row is index zero AND a member row, clear its coloring.
                        if (rowStatus == "HeaderRow" || rowStatus == "MemberRow" && (i - 1) == 0)
                        {
                            dataGridView.Rows[i - 1].DefaultCellStyle.BackColor = Color.White;
                        }
                        //IF the previous row is a header row AND has color coding saying it is a group header, then clear its color and apply it to this row (i).
                        else if (rowStatus == "HeaderRow" && dataGridView.Rows[i - 1].DefaultCellStyle.BackColor == Color.LightGray)
                        {
                            dataGridView.Rows[i - 1].DefaultCellStyle.BackColor = Color.White;
                            dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.LightGray;
                        }
                        //IF previous row is Incomplete, then call the stable PaintRowGroups function.
                        else if (rowStatus == "IncompleteRow")
                        {
                            PaintRowGroups(dataGridView);
                        }
                    }
                }
                else if (rowStatus == "MemberRow")
                {
                    if (i > 0)
                    {
                        //Check the previous row.
                        rowStatus = parser.GetRowAttribute(dataGridView.Rows[i - 1]);
                        //IF the previous row is a member row AND is the first row in the data grid, then remove the coloring for it and the current row (i).
                        if (rowStatus == "MemberRow" && (i - 1) == 0)
                        {
                            dataGridView.Rows[i - 1].DefaultCellStyle.BackColor = Color.White;
                            dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.White;
                        }
                        //IF the previous row is a member row AND it also has color coding suggesting that it is a member of a group, then add the current row (i) as well.
                        else if (rowStatus == "MemberRow" && dataGridView.Rows[i - 1].DefaultCellStyle.BackColor == Color.LightBlue)
                        {
                            dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.LightBlue;
                        }
                        //IF the previous row is simply a member row with no coloring at all, then remove the coloring from the current row (i).
                        else if (rowStatus == "MemberRow" || rowStatus == "AdSpecialRow")
                        {
                            dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.White;
                        }
                        //IF the previous row is a header row, then color it as a group header and color the current row as a member of said group.
                        else if (rowStatus == "HeaderRow")
                        {
                            dataGridView.Rows[i - 1].DefaultCellStyle.BackColor = Color.LightGray;
                            dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.LightBlue;
                        }
                        //IF the previous row is incomplete, then call the stable PaintRowGroups function.
                        else if (rowStatus == "IncompleteRow")
                        {
                            PaintRowGroups(dataGridView);
                        }
                    }
                    //ELSE the current row is the first row in the data grid, therefore it can't be a member row so remove all coloring.
                    else
                    {
                        dataGridView.Rows[0].DefaultCellStyle.BackColor = Color.White;
                    }
                }
            }
        }

        private void PaintRowGroups(DataGridView dataGridView)
        {
            var parser = new RowParsing();
            var statusReport = "";
            var incompleteRowsFound = 0;
            var lastHeaderIndex = -1;
            var i = 0;
            var groupCount = 0;

            foreach (DataGridViewRow row in dataGridView.Rows)
            {
                //Get the current row's status.
                var rowType = parser.GetRowAttribute(row);
                //If the current row is not a group header, the first row in the grid, or a new row and in fact a member row...
                if (i != 0 && rowType == "MemberRow" && !row.IsNewRow && rowType != "IncompleteRow")
                {
                    //Remove all coloring on the current row.
                    dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.White;

                    //Check to make sure the numbers are within bounds, errors occurs when an incomplete row is found at index zero (0), and the current row is one (1).
                    if ((i - (1 + incompleteRowsFound)) > 0)
                    {
                        //Check to see if the previous row is a header row by subtracting the number of rows that are (incomplete + 1) from the total number of rows (i).
                        //Adding one (1) to the incomplete count allows for checking the row right before the first incomplete row found so far to see if it's a header row.
                        if (parser.GetRowAttribute(dataGridView.Rows[i - (1 + incompleteRowsFound)]) == "HeaderRow")
                        {
                            row.DefaultCellStyle.BackColor = Color.LightBlue;   //Group member
                            dataGridView.Rows[lastHeaderIndex].DefaultCellStyle.BackColor = Color.LightGray;   //Group header
                            groupCount++;

                            statusReport += string.Format("\nGroup {0} created with the header row {1} and the following row members: {2} ", groupCount, (i - incompleteRowsFound).ToString(), (i + 1).ToString());
                        }
                        //Else if a header row was not found from the previous operation but a header row is set, then its safe to assume that this row can be a member.
                        else if (lastHeaderIndex >= 0)
                        {  
                            //Check to make sure that the last header index is underneath the Ad Special Row OR that the current row (i) is before the Ad Special Index.
                            if (lastHeaderIndex > _gAdSpecialIndex || i < _gAdSpecialIndex)
                            {
                                //Assume that the current row is a member of that group header and color it as such.
                                row.DefaultCellStyle.BackColor = Color.LightBlue;
                                statusReport += String.Format(" {0} ", (i + 1));
                            }
                        }
                    }
                }
                //If the current row is a potential group header AND the next row is as well remove the coloring from the current row, no need for checks.
                else if (rowType == "HeaderRow" && parser.GetRowAttribute(dataGridView.Rows[i + 1]) == "HeaderRow")
                {
                    dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.White;
                }
                //Check to see if this row is the first row, a header index row, AND the next row is a MemberRow.
                else if (rowType == "HeaderRow" && parser.GetRowAttribute(dataGridView.Rows[i + 1]) == "MemberRow" && i == 0)
                {
                    //IF so, set the last header index, and color code of this row along with the next row which is a MemberRow.
                    lastHeaderIndex = 0;
                    dataGridView.Rows[0].DefaultCellStyle.BackColor = Color.LightGray;
                    dataGridView.Rows[i + 1].DefaultCellStyle.BackColor = Color.LightBlue;
                    statusReport += string.Format("Group {0} created with the header row 1 and the following row member(s): {1}", groupCount, (i + 2).ToString());
                }
                //Check to see if this row is a group header.
                else if (rowType == "HeaderRow")
                {
                    //If so, reset the incomplete rows found count, set the lastHeaderIndex to this row's index (i) and change it's color to white.
                    incompleteRowsFound = 0;
                    lastHeaderIndex = i;
                    dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.White;
                }
                //If the current row is the first row in the grid AND is not a group header then remove all color from it, since its not allowed to be a header or a member.
                else if (i == 0 && rowType == "MemberRow")
                {
                    dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.White;
                }
                //Mark an incomplete row to determine whether or not we found one and remove any coloring it may have, incomplete rows are not allowed to be members or headers.
                if (rowType == "IncompleteRow")
                {
                    incompleteRowsFound++;
                    dataGridView.Rows[i].DefaultCellStyle.BackColor = Color.White;
                }
                i++;     
            }//end for-each
            
            dataGridView.Refresh();
            _gLogConsole.WriteToLog(LogConsole.Level.Debug, statusReport);
        }//End PaintRowGroups
        /// <summary>
        /// 
        /// </summary>
        private void ConstructDataGridViews()
        {
            //Arrays containing the (name)s of the columns to be displayed
            string[] projectionsColumns = { "AdItem", "PrSold", "PrSalePrice", "PrTotalSales", "PrCost", "PrProfitReturn", "PrTotalProfitReturn" };
            string[] inventoryActualSalesColumns = { "AdItem", "InvBeginingInventory", "InvReceived", "InvTotal", "InvEndingInventory", "AcSold", "AcSalePrice", "AcTotalSales", "AcCost", "AcProfitReturn", "AcTotalProfitReturn" };
            string[] invoicesColumns = { "InvoiceDate", "Supplier", "InvoiceNumber", "InvoiceNetAmountAtCost", "InvoiceNetAmount", "InvoiceNote" };
            string[] weeklySalesColumns = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "TotalSales" };

            //Arrays containing the header text of the columns
            string[] projectionsColumnHeaderText = { "Ad Item", "Sold", "Sale Price", "Total Sales", "Cost", "Profit Return", "Total Profit Returned" };
            string[] inventoryActualHeaderText = { "Ad Items", "Beginning Inventory", "Received", "Total", "Ending Inventory", "Sold", "Sale Price", "Total Sales", "Cost", "Profit Return", "Total Profit Return" };
            string[] invoicesHeaderText = { "Invoice Date", "Supplier", "Invoice Number", "Net Amount of Invoices at Cost", "Net Amount of Invoices Extended Retail", "Invoice Notes" };
            string[] weeklySalesHeaderText = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Total Sales" };
            //Index variable
            int i = 0;

            foreach (var columnName in projectionsColumns)
            {

                var column = new DataGridViewTextBoxColumn
                {
                    Name = columnName,
                    HeaderText = projectionsColumnHeaderText[i],
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    MaxInputLength = 50
                };
                projectionsDataGridView.Columns.Add(column);
                i++;
            }

            i = 0;
            foreach (var columnName in inventoryActualSalesColumns)
            {
                var column = new DataGridViewTextBoxColumn
                {
                    Name = columnName,
                    HeaderText = inventoryActualHeaderText[i],
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    MaxInputLength = 50
                };
                    inventoryActualSalesDataGridView.Columns.Add(column);
                i++;
            }

            i = 0;
            foreach (string columnName in invoicesColumns)
            {
                var column = new DataGridViewTextBoxColumn
                {
                    Name = columnName,
                    HeaderText = invoicesHeaderText[i],
                    SortMode = DataGridViewColumnSortMode.NotSortable
                };
                suppliersDataGridView.Columns.Add(column);
                i++;
            }

            i = 0;
            foreach (string columnName in weeklySalesColumns)
            {
                var column = new DataGridViewTextBoxColumn
                {
                    Name = columnName,
                    HeaderText = weeklySalesHeaderText[i],
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    ValueType = typeof (string),
                    DefaultCellStyle = {Format = "n2"}
                };
                weeklySalesDataGridView.Columns.Add(column);
                i++;
            }
            //Only one row exists in the Weekly Sales DataGridView.
            weeklySalesDataGridView.Rows.Add(0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00);
            weeklySalesDataGridView.Columns[0].SortMode = DataGridViewColumnSortMode.NotSortable;
        }//End ConstructForms
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void addRecordsButton_Click(object sender, EventArgs e)
        {
            //IF the tables don't line up, then the data in them is no good, so return.
            if (projectionsDataGridView.RowCount != inventoryActualSalesDataGridView.RowCount)
            {
                MessageBox.Show("The Projections and Inventory / Actual tables must have the same number of rows.", "Invalid Row Count");
                return;
            }
            //Initialize the database reader and writer classes.
            var retrieveFromDatabase = new RetrieveFromDatabase();
            var tracker = new DatabaseTracker();

            var dbWriter = new DatabaseWriter(tracker.DatabaseConnectionString);
            //Initialize the row parsing class for row analysis.
            var parser = new RowParsing();
            //
            int cellCount = (projectionsDataGridView.Rows[0].Cells.Count + inventoryActualSalesDataGridView.Rows[0].Cells.Count + 1);
            if (weekEndingDateMaskedTextBox.MaskCompleted == true)
            {
                if (retrieveFromDatabase.RetrieveDateIdByDateString(weekEndingDateMaskedTextBox.Text) == "-1")
                {
                    //Assume the date is correct and add it to the database.
                    //TODO: Check the date vs the last date entered (obtained by sorting dates) and check to see if the two are seven (7) or more days apart.
                    if (!dbWriter.InsertIntoWeekEnding(weekEndingDateMaskedTextBox.Text))
                    {
                        //IF the date failed to add for whatever reason, inform the user and break.
                        MessageBox.Show("An error has occurred attempting to write the new date into the database.", "Insert Error");
                        return;
                    }
                }
            }
            else
            {
                MessageBox.Show("You must have a valid date.", "Invalid Date format");
                return;
            }

            var parameters = new object[cellCount];
            //Most recent date ID, the hell was I thinking..
            var dateIdString = retrieveFromDatabase.RetrieveDateIdByDateString(weekEndingDateMaskedTextBox.Text);
            var commentsText = commentsTextBox.Text;
            if (Regex.Replace(commentsText, @"\s+", "") != "")
            {
                //TODO: Maybe a clear command?
                dbWriter.RedundantlessInsertIntoComments(commentsTextBox.Text, dateIdString);
            }

            var rowIndexNumber = 0;
            //Negative one (-1) shows that the variable is not set.
            var lastHeaderRowNumber = -1;
            var table = new DataTable();
            for (var i = 0; i <= 19; i++)
            {
                DataColumn column = new DataColumn();
                column.ColumnName = "Column" + i;
                table.Columns.Add(column);
            }
            foreach (DataGridViewRow row in projectionsDataGridView.Rows)
            {
                //DataRow newRow = table.NewRow();
                if (row.IsNewRow)
                {
                    break;
                }
                if (projectionsDataGridView.Rows[rowIndexNumber].Cells[0].EditedFormattedValue.ToString() != inventoryActualSalesDataGridView.Rows[rowIndexNumber].Cells[0].EditedFormattedValue.ToString())
                {
                    MessageBox.Show("Ad Items do not match up on either the Projections or the Inventory / Actual tables on row " + rowIndexNumber + ".", "Invalid Ad Item Entries");
                    return;
                }
                //Get the current row's attribute.
                string currentRowAttribute = parser.GetRowAttribute(row);

                //IF the current row is a member row and is the first row, then don't include it in the database
                if (currentRowAttribute != "IncompleteRow" || currentRowAttribute == "MemberRow" && rowIndexNumber == 0)
                {
                    parameters[16] = row.Cells[0].EditedFormattedValue.ToString();
                    //Do an in-depth check to determine if the row should be marked with a "Header Row" group tag.
                    //Only rows that have members will be explicitly marked as such.
                    if (currentRowAttribute == "HeaderRow")
                    {
                        if ((rowIndexNumber + 1) <= projectionsDataGridView.Rows.Count)
                        {
                            //Check to see if the next row is a Member Row.
                            if ((parser.GetRowAttribute(projectionsDataGridView.Rows[rowIndexNumber + 1])) == "MemberRow")
                            {
                                //Set the current row's attribute to HeaderRow in the database.
                                parameters[17] = "1";
                                lastHeaderRowNumber = rowIndexNumber;
                            }
                            else
                            {
                                //Clear the row number to prevent any mix ups down the line and the group ID.
                                lastHeaderRowNumber = -1;
                                parameters[17] = "";
                            }
                        }
                    }  
                    else if (currentRowAttribute == "MemberRow" && lastHeaderRowNumber != -1)
                    {
                        parameters[17] = "2";
                    }

                    //Now check to see if the current row we're dealing with is inside an Ad Special grouping.
                    if (rowIndexNumber > _gAdSpecialIndex && _gAdSpecialIndex != -1)
                    {
                        //IF the ad item is already in the used ad item list, then skip it as it has already been added to the data table.
                        if (!_gUsedAdItems.Contains(parameters[16] + ":1"))
                        {
                            //Get the group ID for the Ad Special Row.
                            var groupId =
                                retrieveFromDatabase.RetrieveGroupIDByString(
                                    parser.CheckForGroupKeyWord(row.Cells[0].EditedFormattedValue.ToString()));
                            //Check for the row's attribute
                            if ((string) parameters[17] == "")
                            {
                                if (groupId != "")
                                {
                                    //IF the group is in the database then add it to the group ID field.
                                    parameters[17] = groupId;
                                }
                                else
                                {
                                    //ELSE IF it is not, then use "Weekend Ad" (number 3 in the database) as the placeholder value.
                                    parameters[17] += "|3";
                                }
                            }
                            else
                            {
                                if (groupId != "")
                                {
                                    //IF the group is in the database then add it to the group ID field.
                                    parameters[17] = parameters[17] + "|" + groupId;
                                }
                                else
                                {
                                    //ELSE IF it is not, then use "Weekend Ad" (number 3 in the database) as the placeholder value.
                                    parameters[17] = parameters[17] + "|3";
                                }
                            }
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        if (_gUsedAdItems.Contains(parameters[16] + ":2"))
                        {
                            var groupId =
    retrieveFromDatabase.RetrieveGroupIDByString(
        parser.CheckForGroupKeyWord(row.Cells[0].EditedFormattedValue.ToString()));
                            if (parser.GetRowAttribute(row) == "HeaderRow")
                            {

                                parameters[17] = "1|" + groupId;
                            }else if (parser.GetRowAttribute(row) == "MemberRow")
                            {
                                parameters[17] = "2|" + groupId;
                            }

                        }
                    }     
               
                    parameters[18] = dateIdString;
                    var cellNumberTotal = 0;
                    var currentCellIndex = 0;
                    foreach (DataGridViewCell cell in projectionsDataGridView.Rows[rowIndexNumber].Cells)
                    {
                        if (currentCellIndex != 0)
                        {
                            var formattedValue = row.Cells[currentCellIndex].FormattedValue;
                            if (formattedValue != null && formattedValue.ToString() == "")
                            {
                                parameters[cellNumberTotal] = "0";
                            }
                            else
                            {
                                parameters[cellNumberTotal] = row.Cells[currentCellIndex].FormattedValue.ToString();
                            }  
                            cellNumberTotal++;
                        }          
                        
                        currentCellIndex++;
                    }
                    currentCellIndex = 0;
                    foreach (DataGridViewCell cell in inventoryActualSalesDataGridView.Rows[rowIndexNumber].Cells)
                    {
                        if (currentCellIndex != 0)
                        {
                            var formattedValue = inventoryActualSalesDataGridView.Rows[rowIndexNumber].Cells[currentCellIndex].FormattedValue;
                            if (formattedValue != null && formattedValue.ToString() == "")
                            {
                                parameters[cellNumberTotal] = "0";
                            }
                            else
                            {
                                parameters[cellNumberTotal] = inventoryActualSalesDataGridView.Rows[rowIndexNumber].Cells[currentCellIndex].FormattedValue.ToString();
                            }
                            cellNumberTotal++;
                        }
                        currentCellIndex++;
                    }

                    //TODO: Clean this shit up! But for now check to see if the current row is an Ad Special row and if so, don't add it to the array.
                    if (rowIndexNumber != _gAdSpecialIndex)
                    {
                        table.Rows.Add(parameters);
                    }
                }
                rowIndexNumber++;
            }//End for-each
            //Send the table's data to their respective functions.
            _gLogConsole.WriteToLog(LogConsole.Level.Info, "Added the following rows to the APC table:");
            var rowsEffected = dbWriter.RedundantlessInsertIntoAPC(table);
            foreach (var i in rowsEffected)
            {
                _gLogConsole.WriteToLog(LogConsole.Level.Info, (i + 1).ToString());
            }
            _gLogConsole.WriteToLog(LogConsole.Level.Info, "Added the following rows to the Invoices table:");
            rowsEffected = BuildInvoiceTableAndSendToDatabase();
            foreach (int i in rowsEffected)
            {
                _gLogConsole.WriteToLog(LogConsole.Level.Info, (i + 1).ToString());
            }
            if (InsertWeeklySalesIntoDatabase())
            {
                _gLogConsole.WriteToLog(LogConsole.Level.Info, "Successfully added weekly sales to the database.");
            }
            else
            {
                _gLogConsole.WriteToLog(LogConsole.Level.Info, "Failed to add weekly sales to the database.");
            }
        }

        private void closeFileMainMenu_Click(object sender, EventArgs e)
        {
            Close();
        }

        private List<int> BuildInvoiceTableAndSendToDatabase()
        {
            var tracker = new DatabaseTracker();
            var writer = new DatabaseWriter(tracker.DatabaseConnectionString);
            var retrival = new RetrieveFromDatabase(); 
            var supplierTable = new DataTable();
            //Build the new DataTable so that the scheme is the same.
            for (var i = 0; i <= 9; i++)
            {
                var column = new DataColumn {ColumnName = "Column" + i};
                supplierTable.Columns.Add(column);
            }

            foreach (DataGridViewRow row in suppliersDataGridView.Rows)
            {
                var newRow = supplierTable.NewRow();
                newRow[0] = row.Cells[0].EditedFormattedValue.ToString();
                newRow[1] = row.Cells[1].EditedFormattedValue.ToString();
                newRow[2] = row.Cells[2].EditedFormattedValue.ToString();
                newRow[3] = row.Cells[3].EditedFormattedValue.ToString();
                newRow[4] = row.Cells[4].EditedFormattedValue.ToString();
                newRow[5] = row.Cells[5].EditedFormattedValue.ToString();
                newRow[6] = retrival.RetrieveDateIdByDateString(weekEndingDateMaskedTextBox.Text);

                supplierTable.Rows.Add(newRow);
            }

            var rowsAdded = writer.RedundantlessInsertIntoInvoice(supplierTable);

            return rowsAdded;
        }

        private bool InsertWeeklySalesIntoDatabase()
        {
            var wasSuccessful = false;
            var rowsEffected = 0;
            var retrieval = new RetrieveFromDatabase();
            var dateID = retrieval.RetrieveDateIdByDateString(weekEndingDateMaskedTextBox.Text);
            var tracker = new DatabaseTracker();
            var writer = new DatabaseWriter(tracker.DatabaseConnectionString);
            var weeklySalesTable = new DataTable();
            weeklySalesTable.Columns.Add("Sunday", typeof(string));
            weeklySalesTable.Columns.Add("Monday", typeof(string));
            weeklySalesTable.Columns.Add("Tuesday", typeof(string));
            weeklySalesTable.Columns.Add("Wednesday", typeof(string));
            weeklySalesTable.Columns.Add("Thursday", typeof(string));
            weeklySalesTable.Columns.Add("Friday", typeof(string));
            weeklySalesTable.Columns.Add("Saturday", typeof(string));
            weeklySalesTable.Columns.Add("TotalSales", typeof(string));
            weeklySalesTable.Columns.Add("DateID", typeof(string));

            var sunday = weeklySalesDataGridView.Rows[0].Cells[0].EditedFormattedValue.ToString();
            var monday = weeklySalesDataGridView.Rows[0].Cells[1].EditedFormattedValue.ToString();
            var tuesday = weeklySalesDataGridView.Rows[0].Cells[2].EditedFormattedValue.ToString();
            var wednesday = weeklySalesDataGridView.Rows[0].Cells[3].EditedFormattedValue.ToString();
            var thursday = weeklySalesDataGridView.Rows[0].Cells[4].EditedFormattedValue.ToString();
            var friday = weeklySalesDataGridView.Rows[0].Cells[5].EditedFormattedValue.ToString();
            var saturday = weeklySalesDataGridView.Rows[0].Cells[6].EditedFormattedValue.ToString();
            var totalSales = weeklySalesDataGridView.Rows[0].Cells[7].EditedFormattedValue.ToString();

            weeklySalesTable.Rows.Add(sunday, monday, tuesday, wednesday, thursday, friday, saturday, totalSales, dateID);

            rowsEffected = writer.RedundantlessInsertIntoWeeklySales(weeklySalesTable.Rows[0]);

            if (rowsEffected == 1)
            {
                wasSuccessful = true;
            }
            return wasSuccessful;
        }

        private void clearFormFileMainMenu_Click(object sender, EventArgs e)
        {
            projectionsDataGridView.Rows.Clear();
            inventoryActualSalesDataGridView.Rows.Clear();
            suppliersDataGridView.Rows.Clear();
            int i = 0;
            foreach (var column in weeklySalesDataGridView.Columns)
            {
                weeklySalesDataGridView.Rows[0].Cells[i].Value = "";
                i++;
            }
            weeklySalesDataGridView.RefreshEdit();
            _gAdSpecialIndex = -1;
            _gUsedAdItems.Clear();
            _gLastRowHeaderIndex = - 1;
            _gLastAdItemEntered = "";
            weekEndingDateMaskedTextBox.Clear();
            commentsTextBox.Clear();
        }
    }
}
