using System;
using System.Data.Entity.Core;
using System.Data.Entity.Infrastructure;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Transactions;
using System.Windows.Forms;

namespace AdvertsingProfitControl
{
    public partial class FrmMain : Form
    {
        private decimal _salesProducedByAdItems;
        private decimal _totalProfitReturnFromAdItems;
        private decimal _totalInvoicePurchases; //Total Purchases
        private decimal _departmentSales; //weekly sales total
        private DateTime _currentActiveDate;
        private readonly FrmLogConsole _console = FrmLogConsole.GetStaticInstance;
        private int _lazyPageCounter;

        public FrmMain(bool isDebug)
        {
            InitializeComponent();
            debugMainMenu.Visible = isDebug;
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            var db = new AdvertisingProfitControlModel();
            RefreshDateListing();
            ConstructApcDataGridViews();
            ConstructInvoicesDataGridView();
            RowParsing.AdSpecialGroups.AddRange(db.AdSpecials.Select(x => x.Name));
            //Select the most recent date from the database.
            var recentDate = db.WeekEndingDates.OrderByDescending(x => x.EndingDate).FirstOrDefault();
            if (recentDate != null)
            {
                _currentActiveDate = recentDate.EndingDate;
                LoadDate(recentDate.EndingDate);
            }
            monthCalendar.DateChanged += ValidateDateChanged;
        }

        #region Calculate Analysis Methods

        private void CalculateProfitAnalysis(decimal shrink = 0.30m)
        {
            if (actualSalesDataGridView.Rows.Count == 0)
            {
                salesProducedLabel.Text = @"Sales Produced By Ad Items (A): No Data";
                totalProfitFromAdItemsLabel.Text = @"Total Profit Return " + Environment.NewLine + @" From Ad Items (B): No Data";
                if (Math.Abs(_departmentSales) < 1)
                {
                    departmentSalesLabel.Text = @"Department Sales: No Data";
                }
                return;
            }
            var incompleteData = false;
            //First grab the total sales and the total profit return generated by the actual sales of product.
            if (
                decimal.TryParse(
                    actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].Cells[3].EditedFormattedValue
                        .ToString(), out _salesProducedByAdItems))
            {
                //The sales parsed properly so there is data in the cell. Next try to grab the total profit return.
                if (decimal.TryParse(
                    actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].Cells[6].EditedFormattedValue
                        .ToString(), out _totalProfitReturnFromAdItems))
                {
                    
                }
            }
            if (Math.Abs(_salesProducedByAdItems) < 1)
            {
                salesProducedLabel.Text = @"Sales Produced By Ad Items (A): No Data";
                incompleteData = true;
            }
            if (Math.Abs(_totalProfitReturnFromAdItems) < 1)
            {
                totalProfitFromAdItemsLabel.Text = @"Total Profit Return " + Environment.NewLine + @"From Ad Items (B): No Data";
                incompleteData = true;
            }
            if (Math.Abs(_departmentSales) < 1)
            {
                departmentSalesLabel.Text = @"Department Sales: No Data";
                incompleteData = true;
            }
            if (incompleteData)
            {
                return;
            }
            departmentSalesLabel.Text = @"Department Sales: " + _departmentSales.ToString("C");
            salesProducedLabel.Text = @"Sales Produced By Ad Items (A): " + _salesProducedByAdItems.ToString("C");
            //Calculate the remaining sales.
            var remainingSales = _departmentSales - _salesProducedByAdItems;
            remainingSalesLabel.Text = @"Remaining Sales: " + remainingSales.ToString("C");
            //Again assume the total profit return is larger then zero (0).
            totalProfitFromAdItemsLabel.Text = @"Total Profit Return" + Environment.NewLine + @" From Ad Items (B): " + _totalProfitReturnFromAdItems.ToString("C");
            //Shrink is being used as a place holder for Cross Profit % which is obtained by dividing the total profit return from the department weekly retail sales.
            var totalProfitReturnFromRemainingSales = shrink * remainingSales;
            //
            totalProfitReturnFromRemaingLabel.Text = @"Total Profit Return" + Environment.NewLine + @" From Remaining Sales: " + totalProfitReturnFromRemainingSales.ToString("C");
            var totalProfitReturn = _totalProfitReturnFromAdItems + totalProfitReturnFromRemainingSales;
            totalProfitReturnLabel.Text = @"Total Profit Return: " + totalProfitReturn.ToString("C");
        }

        private void CalculateGrossProfit()
        {
            if (Math.Abs(_totalInvoicePurchases) < 1)
            {
                //IF Cost of Sales hasn't been calculated, then clear labels and return.
                if (Math.Abs(_departmentSales) < 1)
                {
                    grossProfitTotalSales.Text = @"Total Sales: No Data";
                }
                grossProfitLessCostOfSales.Text = @"Less Cost of Sales: No Data";
                return;
            }
            grossProfitTotalSales.Text = @"Total Sales: " + _departmentSales.ToString("C");
            grossProfitLessCostOfSales.Text = @"Less Cost of Sales: " + _totalInvoicePurchases.ToString("C");
            var dollarGrossProfit = _departmentSales - _totalInvoicePurchases;
            grossProfitDollarGrossProfitLabel.Text = @"Dollar Gross Profit: " + dollarGrossProfit.ToString("C");
            var grossProfitPercent = dollarGrossProfit/_departmentSales;
            perfectGrossProfitLabel.Text = @"Percent Gross Profit: " + grossProfitPercent.ToString("P");
        }

        #endregion

        #region Main Menu Click Handlers

        private void DisplayNewRecordForm(object sender, EventArgs e)
        {
            var db = new AdvertisingProfitControlModel();
            var form = new NewModifyRecord();
            form.ShowDialog();
            //One the user has closed the add record form, check to see if there is a newer date
            //available. If so, reload the form.
            RefreshDateListing();
            var date = db.WeekEndingDates.OrderByDescending(x => x.EndingDate).FirstOrDefault();
            if (date == null) return;
            if (date.EndingDate == _currentActiveDate) return;
            LoadDate(date.EndingDate);
        }

        private void DisplayModifyRecordForm(object sender, EventArgs e)
        {
            var form = new NewModifyRecord(_currentActiveDate);
            form.ShowDialog();
            RefreshDateListing();
            //On return reload the date that was just modified by the modify record form.
            LoadDate(_currentActiveDate);
        }

        private void ClearCurrentRecord(object sender, EventArgs e)
        {
            var result = MessageBox.Show(@"Are you sure you want to delete all records for the date " + _currentActiveDate.ToShortDateString() + @"?", @"Clear Date", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (result == DialogResult.No)
            {
                return;
            }

            var db = new AdvertisingProfitControlModel();
            var dateRecord = db.WeekEndingDates.Single(x => x.EndingDate == _currentActiveDate);
            var projections = db.Projections.Where(x => x.WeekEndingDate.EndingDate == dateRecord.EndingDate);
            var inventories = db.Inventories.Where(x => x.WeekEndingDate.EndingDate == dateRecord.EndingDate);
            var actualSales = db.ActualSales.Where(x => x.WeekEndingDate.EndingDate == dateRecord.EndingDate);
            var invoices = db.Invoices.Where(x => x.WeekEndingDate.EndingDate == dateRecord.EndingDate);
            var weeklySale = db.WeeklySales.SingleOrDefault(x => x.WeekEndingDate.EndingDate == dateRecord.EndingDate);
            var taxable = db.Taxables.SingleOrDefault(x => x.WeekEndingDate.EndingDate == dateRecord.EndingDate);
            var costAnalysis = db.CostAnalysis.SingleOrDefault(x => x.WeekEndingDate.EndingDate == dateRecord.EndingDate);
            var comment = db.Notes.SingleOrDefault(x => x.WeekEndingDate.EndingDate == dateRecord.EndingDate);

            using (var scope = new TransactionScope())
            {
                try
                {
                    //Clear projections
                    foreach (var projection in projections)
                    {
                        db.Projections.Remove(projection);
                    }
                    //Clear inventory
                    foreach (var inventory in inventories)
                    {
                        db.Inventories.Remove(inventory);
                    }
                    //Clear actual sales
                    foreach (var sale in actualSales)
                    {
                        db.ActualSales.Remove(sale);
                    }
                    //Clear invoices
                    foreach (var invoice in invoices)
                    {
                        db.Invoices.Remove(invoice);
                    }
                    if (weeklySale != null)
                    {
                        db.WeeklySales.Remove(weeklySale);
                    }
                    if (taxable != null)
                    {
                        db.Taxables.Remove(taxable);
                    }
                    if (costAnalysis != null)
                    {
                        db.CostAnalysis.Remove(costAnalysis);
                    }
                    if (comment != null)
                    {
                        db.Notes.Remove(comment);
                    }
                    db.WeekEndingDates.Remove(dateRecord);
                    db.SaveChanges();
                    scope.Complete();
                    ClearForm();
                }
                catch (DbUpdateException ex)
                {
                    MessageBox.Show(@"Failed to clear selected date.", @"Error");
                    _console.WriteToLog(FrmLogConsole.Level.Error, ex.Message);
                }
            }

            RefreshDateListing();
            var mostRecentDate = db.WeekEndingDates.OrderByDescending(x => x.EndingDate).FirstOrDefault();
            if (mostRecentDate != null)
            {
                LoadDate(mostRecentDate.EndingDate);
            }
        }

        private void DisplayAdItemManager(object sender, EventArgs e)
        {
            var adItemManager = new FrmManageAdItems();
            adItemManager.ShowDialog();
        }


        private void manageSuppliersToolsMainMenu_Click(object sender, EventArgs e)
        {
            var form = new FrmManageSuppliers();
            form.ShowDialog();
        }

        private void DisplayRegisterAdSpecial(object sender, EventArgs e)
        {
            var form = new FrmRegisterAdSpecial();
            form.ShowDialog();
        }

        private void ShowHideLogConsole(object sender, EventArgs e)
        {
            if (_console.IsVisable)
            {
                _console.Hide();
            }
            else
            {
                _console.Show();
            }
        }

        private void DisplayDatabaseVersionNumber(object sender, EventArgs e)
        {
            var db = new AdvertisingProfitControlModel();
            var version = db.Versions.FirstOrDefault(x => x.Id == 1);
            if (version == null)
            {
                MessageBox.Show(@"Failed to retrieve the version number of the SQL database", @"Database Version Number",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            MessageBox.Show(@"The current database's version is " + version.VersionNumber + @".", @"Database Version Number", MessageBoxButtons.OK, MessageBoxIcon.Information);
            
        }
        #endregion

        #region Print Handler Methods

        private void PrintPage(object sender, PrintPageEventArgs e)
        {
            Bitmap bitMap;
            if (_lazyPageCounter == 0)
            {
                bitMap = new Bitmap(Application.StartupPath + "\\FrontPage.png");
            }
            else
            {
                bitMap = new Bitmap(Application.StartupPath + "\\BackPage.png");
            }

            var rect = e.MarginBounds;

            if (bitMap.Width/(double) bitMap.Height > rect.Width / (double) rect.Height)
                // image is wider
            {
                rect.Height = (int) (bitMap.Height/(double) bitMap.Width* rect.Width);
            }
            else
            {
                rect.Width = (int) (bitMap.Width/(double) bitMap.Height* rect.Height);
            }

            if (_lazyPageCounter == 0)
            {
                e.Graphics.DrawImage(bitMap, new Rectangle(0, 25, 850, 1050));
                _lazyPageCounter++;
                e.HasMorePages = true;
            }
            else
            {
                e.Graphics.DrawImage(bitMap, new Rectangle(0, 0, 850, 1100));
                _lazyPageCounter = 0;
                e.HasMorePages = false;
            }
        }

        private void DisplayPrintPreview(object sender, EventArgs e)
        {
            //Build and render the front and back forms of the document.
            var test = new FrontPageGenerator();
            var backPageTest = new BackPageGenerator();
            var printTestPage = new PrintDocument();
            var printDialog = new PrintPreviewDialog();
            if (!usePreRenderedFilesCheckbox.Checked)
            {
                //var databaseTracker = new DatabaseTracker();
                //var databaseReader = new DatabaseReader();
                //var dateId =
                //    databaseReader.RetrieveDateIdByDateString(monthCalendar.SelectionStart.ToShortDateString(),
                //        databaseTracker.DatabaseConnectionString);
                //var remaingingSales = _departmentSales - _salesProducedByAdItems;
                //var totalProfitReturnFromReminaingSales = remaingingSales * .3m;
                //var totalProfitReturn = _totalProfitReturnFromAdItems + totalProfitReturnFromReminaingSales;
                ////test.BuildFormFrontCompressedLayout(dateId, _departmentSales, _salesProducedByAdItems, remaingingSales,
                //    //_totalProfitReturnFromAdItems, totalProfitReturnFromReminaingSales, totalProfitReturn,
                //    //commentsTextBox.Text);
                //test.RenderHtmlToImage();
                //backPageTest.GenerateWeeklyInventoryControlPage(dateId);
                //backPageTest.RenderHtmlToImage();
            }
            else
            {
                if (File.Exists(Application.StartupPath + "\\" + "FrontPage.html") &&
                    File.Exists(Application.StartupPath + "\\" + "Backpage.html"))
                {
                    test.RenderHtmlToImage();
                    backPageTest.RenderHtmlToImage();
                }
            }

            //var margin = new Margins(50, 50, 0, 0);
            //printDoc.DefaultPageSettings.Margins = margin;
            printTestPage.PrintPage += PrintPage;
            printDialog.Document = printTestPage;
            printDialog.ShowDialog();
            printDialog.Document = new PrintDocument();
        }

        #endregion

        #region DataGridView Constructor Methods

        /// <summary>
        /// Builds the appropriate columns in the APC DataGridViews so they can be filled.
        /// </summary>
        private void ConstructApcDataGridViews()
        {
            //Construct a list of column names for the inventory/actual sales DataGridViews and the inventory DataGirdView.
            string[] saleColumnNames =
            {
                "AdItem", "Sold", "SalePrice", "TotalSales", "Cost", "ProfitReturn",
                "TotalProfitReturn"
            };
            string[] inventoryColumnNames =
            {
                "AdItem", "BeginningInventory", "Received", "Total", "EndingInventory"
            };

            foreach (var name in saleColumnNames)
            {
                var column = new DataGridViewTextBoxColumn
                {
                    Name = name,
                    HeaderText = TextFormat.AddSpacesToSentence(name, false),
                    ValueType = typeof(string),
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    MaxInputLength = 20
                };
                projectionsDataGridView.Columns.Add(column);
            }
            //Add the columns into the inventory DataGridView after setting their types.
            foreach (var name in inventoryColumnNames)
            {
                var column = new DataGridViewTextBoxColumn
                {
                    Name = name,
                    HeaderText = TextFormat.AddSpacesToSentence(name, false),
                    ValueType = typeof(string),
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    MaxInputLength = 20
                };
                inventoryDataGridView.Columns.Add(column);
            }
            //
            foreach (var name in saleColumnNames)
            {
                var column = new DataGridViewTextBoxColumn
                {
                    Name = name,
                    HeaderText = TextFormat.AddSpacesToSentence(name, false),
                    ValueType = typeof(string),
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    MaxInputLength = 20
                };
                actualSalesDataGridView.Columns.Add(column);
            }
        }

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

            foreach (var name in invoicesColumnNames)
            {
                var column = new DataGridViewTextBoxColumn
                {
                    Name = name,
                    HeaderText = TextFormat.AddSpacesToSentence(name, false),
                    ValueType = typeof(string),
                    SortMode = DataGridViewColumnSortMode.NotSortable,
                    MaxInputLength = 20
                };
                invoicesDataGridView.Columns.Add(column);
            }
        }

        #endregion

        #region Date Loading Methods

        private void LoadDate(DateTime date)
        {
            var db = new AdvertisingProfitControlModel();
            //Check to see if that date record can be found before clearing the form.
            var dateRecord = db.WeekEndingDates.SingleOrDefault(x => x.EndingDate == date);
            if (dateRecord == null)
            {
                dateTimeGroupBox.Text = @"Failed to retrieve " + date.ToString("d") + @".";
                return;
            }
            ClearForm();
            //Load all the data attached to the date record.
            LoadProjectionsTable(dateRecord);
            LoadInventoryTable(dateRecord);
            LoadActualSalesTable(dateRecord);
            LoadInvoices(dateRecord);
            if (db.Notes.Any(x => x.FkDateId == dateRecord.Id))
            {
                var note = db.Notes.Single(x => x.FkDateId == dateRecord.Id);
                commentsTextBox.Text = note.Remark;
            }
            LoadWeeklySales(dateRecord);
            LoadTaxable(dateRecord);
            LoadCostAnalysis(dateRecord);
            CalculateProfitAnalysis();
            CalculateGrossProfit();
            monthCalendar.BoldedDates = db.WeekEndingDates.Select(zdate => zdate.EndingDate).ToArray();
            monthCalendar.SelectionStart = date;
            dateTimeGroupBox.Text = @"Loaded " + date.ToString("d");
        }

        private void LoadProjectionsTable(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();
            var adSpecialIndex = -1;
            decimal totalSales = 0;
            decimal totalProfitReturn = 0;
            var projections = db.Projections.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x);

            foreach (var p in projections)
            {
                projectionsDataGridView.Rows.Add();
                var index = projectionsDataGridView.RowCount - 1;
                projectionsDataGridView.Rows[index].Cells[0].Value = p.AdItem.Name;
                projectionsDataGridView.Rows[index].Cells[1].Value = p.Sold;
                projectionsDataGridView.Rows[index].Cells[2].Value = p.SalePrice;
                projectionsDataGridView.Rows[index].Cells[3].Value = $@"{p.TotalSales:N2}";
                if (p.TotalSales != null) totalSales += (decimal)p.TotalSales;
                projectionsDataGridView.Rows[index].Cells[4].Value = $@"{p.Cost:N2}";
                projectionsDataGridView.Rows[index].Cells[5].Value = $@"{p.ProfitReturn:N2}";
                projectionsDataGridView.Rows[index].Cells[6].Value = $@"{p.TotalProfitReturn:N2}";
                if (p.TotalProfitReturn != null) totalProfitReturn += (decimal)p.TotalProfitReturn;
                if (p.RowAttribute == 1)
                {
                    projectionsDataGridView.Rows[index].DefaultCellStyle.BackColor = ApplicationColors.HeaderRow;
                }
                else if (p.RowAttribute == 2)
                {
                    projectionsDataGridView.Rows[index].DefaultCellStyle.BackColor = ApplicationColors.MemberRow;
                }

                if (p.FkAdSpecialId == null || adSpecialIndex != -1) continue;
                //TODO: Fix null reference when no object is found.
                adSpecialIndex = index;
                projectionsDataGridView.Rows.Insert(index, 1);
                projectionsDataGridView.Rows[index].DefaultCellStyle.BackColor = ApplicationColors.AdSpecial;
                projectionsDataGridView.Rows[index].Cells[3].Value = db.AdSpecials.Single(x => x.Id == p.FkAdSpecialId).Name;
            }
            //Now add the totals row.
            if (projectionsDataGridView.RowCount == 0) return;
            var totalsRow = new DataGridViewRow();
            projectionsDataGridView.Rows.Add(totalsRow);
            projectionsDataGridView.Rows[projectionsDataGridView.Rows.Count - 1].Cells[0].Value = "Total";
            projectionsDataGridView.Rows[projectionsDataGridView.Rows.Count - 1].Cells[2].Style.Alignment =
                DataGridViewContentAlignment.MiddleRight;
            projectionsDataGridView.Rows[projectionsDataGridView.Rows.Count - 1].Cells[2].Value = @"(A)";
            if (Math.Abs(totalSales) > 0)
            {
                projectionsDataGridView.Rows[projectionsDataGridView.Rows.Count - 1].Cells[3].Value =
                    totalSales.ToString("N2");
            }
            projectionsDataGridView.Rows[projectionsDataGridView.Rows.Count - 1].Cells[5].Style.Alignment =
                DataGridViewContentAlignment.MiddleRight;
            projectionsDataGridView.Rows[projectionsDataGridView.Rows.Count - 1].Cells[5].Value = @"(B)";
            if (Math.Abs(totalProfitReturn) > 0)
            {
                projectionsDataGridView.Rows[projectionsDataGridView.Rows.Count - 1].Cells[6].Value =
                    totalProfitReturn.ToString("N2");
            }
        }

        private void LoadInventoryTable(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();
            var adSpecialIndex = -1;
            var inventories = db.Inventories.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x);

            foreach (var i in inventories)
            {
                inventoryDataGridView.Rows.Add();
                var index = inventoryDataGridView.RowCount - 1;
                inventoryDataGridView.Rows[index].Cells[0].Value = i.AdItem.Name;
                inventoryDataGridView.Rows[index].Cells[1].Value = i.BeginningInventory;
                inventoryDataGridView.Rows[index].Cells[2].Value = i.Recieved;
                inventoryDataGridView.Rows[index].Cells[3].Value = i.TotalInventory;
                inventoryDataGridView.Rows[index].Cells[4].Value = i.EndingInventory;
                if (i.RowAttribute == 1)
                {
                    inventoryDataGridView.Rows[index].DefaultCellStyle.BackColor = ApplicationColors.HeaderRow;
                }
                else if (i.RowAttribute == 2)
                {
                    inventoryDataGridView.Rows[index].DefaultCellStyle.BackColor = ApplicationColors.MemberRow;
                }

                if (i.FkAdSpecialId == null || adSpecialIndex != -1) continue;
                adSpecialIndex = index;
                inventoryDataGridView.Rows.Insert(index, 1);
                inventoryDataGridView.Rows[index].DefaultCellStyle.BackColor = ApplicationColors.AdSpecial;
                inventoryDataGridView.Rows[index].Cells[2].Value = db.AdSpecials.Single(x => x.Id == i.FkAdSpecialId).Name;
            }
        }

        private void LoadActualSalesTable(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();
            var adSpecialIndex = -1;
            decimal totalSales = 0;
            decimal totalProfitReturn = 0;
            var actualSales = db.ActualSales.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x);

            foreach (var a in actualSales)
            {
                actualSalesDataGridView.Rows.Add();
                var index = actualSalesDataGridView.RowCount - 1;
                actualSalesDataGridView.Rows[index].Cells[0].Value = a.AdItem.Name;
                actualSalesDataGridView.Rows[index].Cells[1].Value = a.Sold;
                actualSalesDataGridView.Rows[index].Cells[2].Value = a.SalePrice;
                actualSalesDataGridView.Rows[index].Cells[3].Value = $@"{a.TotalSales:N2}";
                if (a.TotalSales != null) totalSales += (decimal)a.TotalSales;
                actualSalesDataGridView.Rows[index].Cells[4].Value = $@"{a.Cost:N2}";
                actualSalesDataGridView.Rows[index].Cells[5].Value = $@"{a.ProfitReturn:N2}";
                actualSalesDataGridView.Rows[index].Cells[6].Value = $@"{a.TotalProfitReturn:N2}";
                if (a.TotalProfitReturn != null) totalProfitReturn += (decimal)a.TotalProfitReturn;
                if (a.RowAttribute == 1)
                {
                    actualSalesDataGridView.Rows[index].DefaultCellStyle.BackColor = ApplicationColors.HeaderRow;
                }
                else if (a.RowAttribute == 2)
                {
                    actualSalesDataGridView.Rows[index].DefaultCellStyle.BackColor = ApplicationColors.MemberRow;
                }

                if (a.FkAdSpecialId == null || adSpecialIndex != -1) continue;
                adSpecialIndex = index;
                actualSalesDataGridView.Rows.Insert(index, 1);
                actualSalesDataGridView.Rows[index].DefaultCellStyle.BackColor = ApplicationColors.AdSpecial;
                actualSalesDataGridView.Rows[index].Cells[3].Value = db.AdSpecials.Single(x => x.Id == a.FkAdSpecialId).Name;
            }            
            //Now add the totals row.
            if (actualSalesDataGridView.RowCount == 0) return;
            var totalsRow = new DataGridViewRow();
            actualSalesDataGridView.Rows.Add(totalsRow);
            actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].Cells[0].Value = "Total";
            actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].Cells[2].Style.Alignment =
                DataGridViewContentAlignment.MiddleRight;
            actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].Cells[2].Value = @"(A)";
            if (Math.Abs(totalSales) > 0)
            {
                actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].Cells[3].Value =
                    totalSales.ToString("N2");
            }
            actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].Cells[5].Style.Alignment =
                DataGridViewContentAlignment.MiddleRight;
            actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].Cells[5].Value = @"(B)";
            if (Math.Abs(totalProfitReturn) > 0)
            {
                actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].Cells[6].Value =
                    totalProfitReturn.ToString("N2");
            }
        }

        private void LoadInvoices(WeekEndingDate dateRecord)
        {
            _totalInvoicePurchases = 0;
            var db = new AdvertisingProfitControlModel();
            var invoices = db.Invoices.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x);

            foreach (var invoice in invoices)
            {
                invoicesDataGridView.Rows.Add();
                var index = invoicesDataGridView.RowCount - 1;
                invoicesDataGridView.Rows[index].Cells[0].Value = invoice.InvoiceDate.ToString("d");
                invoicesDataGridView.Rows[index].Cells[1].Value = invoice.Supplier.Name;
                invoicesDataGridView.Rows[index].Cells[2].Value = invoice.InvoiceNumber;
                invoicesDataGridView.Rows[index].Cells[3].Value = $@"{invoice.InvoiceNetAmountAtCost:N2}";
                invoicesDataGridView.Rows[index].Cells[4].Value = $@"{invoice.InvoiceNetAmount:N2}";                
                if (invoice.InvoiceNetAmount != null) _totalInvoicePurchases += (decimal) invoice.InvoiceNetAmount;
                invoicesDataGridView.Rows[index].Cells[5].Value = invoice.InvoiceNote;
            }
            //Add the total purchases row to the invoice table.
            var totalPurchaesRow = new DataGridViewRow();
            invoicesDataGridView.Rows.Add(totalPurchaesRow);
            invoicesDataGridView.Rows[invoicesDataGridView.Rows.Count - 1].Cells[0].Value = @"Total Purchases";
            if (Math.Abs(_totalInvoicePurchases) > 0)
            {
                invoicesDataGridView.Rows[invoicesDataGridView.Rows.Count - 1].Cells[4].Value = _totalInvoicePurchases.ToString("N2");                
            }
        }

        private void LoadWeeklySales(WeekEndingDate dateRecord)
        {
            //Clear the department sales and prepare to add up a total.
            _departmentSales = 0;
            var db = new AdvertisingProfitControlModel();
            var weeklySales = db.WeeklySales.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x);
            foreach (var sale in weeklySales)
            {
                if (sale.Sunday != null)
                {
                    sundayWeeklySalesLabel.Text = sale.Sunday != 0
                        ? @"Sunday: " + $@"{sale.Sunday:c}"
                        : @"Sunday: " + CheckForHoliday(DayOfWeek.Sunday);
                    _departmentSales += (decimal) sale.Sunday;
                }
                if (sale.Monday != null)
                {
                    mondayWeeklySalesLabel.Text = sale.Monday != 0
                        ? @"Monday: " + $@"{sale.Monday:c}"
                        : @"Monday: " + CheckForHoliday(DayOfWeek.Monday);
                    _departmentSales += (decimal) sale.Monday;
                }
                if (sale.Tuesday != null)
                {
                    tuesadayWeeklySalesLabel.Text = sale.Tuesday != 0
                        ? @"Tuesday: " + $@"{sale.Tuesday:c}"
                        : @"Tuesday: " + CheckForHoliday(DayOfWeek.Tuesday);
                    _departmentSales += (decimal) sale.Tuesday;
                }
                if (sale.Wednesday != null)
                {
                    wednesdayWeeklySalesLabel.Text = sale.Wednesday != 0
                        ? @"Wednesday: " + $@"{sale.Wednesday:c}"
                        : @"Wednesday: " + CheckForHoliday(DayOfWeek.Wednesday);
                    _departmentSales += (decimal) sale.Wednesday;
                }
                if (sale.Thursday != null)
                {
                    thursdayWeeklySalesLabel.Text = sale.Thursday != 0
                        ? @"Thursday: " + $@"{sale.Thursday:c}"
                        : @"Thursday: " + CheckForHoliday(DayOfWeek.Thursday);
                    _departmentSales += (decimal)sale.Thursday;
                }
                if (sale.Friday != null)
                {
                    fridayWeeklySalesLabel.Text = sale.Friday != 0
                        ? @"Friday: " + $@"{sale.Friday:c}"
                        : @"Friday: " + CheckForHoliday(DayOfWeek.Friday);
                    _departmentSales += (decimal) sale.Friday;
                }
                if (sale.Saturday != null)
                {
                    saturdayWeeklySalesLabel.Text = sale.Saturday != 0
                        ? @"Saturday: " + $@"{sale.Saturday:c}"
                        : @"Saturday: " + CheckForHoliday(DayOfWeek.Saturday);
                    _departmentSales += (decimal) sale.Saturday;
                }
                totalWeeklySalesLabel.Text = @"Total Sales: " + $@"{sale.TotalSales:c}";
            }                    
        }

        private void LoadTaxable(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();
            var taxables = db.Taxables.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x);
            foreach (var taxable in taxables)
            {
                sundayTaxableLabel.Text = taxable.Sunday != 0
                    ? @"Sunday: " + $@"{taxable.Sunday:c}"
                    : @"Sunday: " + CheckForHoliday(DayOfWeek.Sunday);
                mondayTaxableLabel.Text = taxable.Monday != 0
                    ? @"Monday: " + $@"{taxable.Monday:c}"
                    : @"Monday: " + CheckForHoliday(DayOfWeek.Monday);
                tuesdayTaxableLabel.Text = taxable.Tuesday != 0
                    ? @"Tuesday: " + $@"{taxable.Tuesday:c}"
                    : @"Tuesday: " + CheckForHoliday(DayOfWeek.Tuesday);
                wednesdayTaxableLabel.Text = taxable.Wednesday != 0
                    ? @"Wednesday: " + $@"{taxable.Wednesday:c}"
                    : @"Wednesday: " + CheckForHoliday(DayOfWeek.Wednesday);
                thursdayTaxableLabel.Text = taxable.Thursday != 0
                     ? @"Thursday: " + $@"{taxable.Thursday:c}"
                     : @"Thursday: " + CheckForHoliday(DayOfWeek.Thursday);
                fridayTaxableLabel.Text = taxable.Friday != 0
                     ? @"Friday: " + $@"{taxable.Friday:c}"
                     : @"Friday: " + CheckForHoliday(DayOfWeek.Friday);
                saturdayTaxableLabel.Text = taxable.Saturday != 0
                     ? @"Saturday: " + $@"{taxable.Saturday:c}"
                     : @"Saturday: " + CheckForHoliday(DayOfWeek.Saturday);
                totalTaxableLabel.Text = @"Total: " + $@"{taxable.Total:c}";
            }
        }

        private void LoadCostAnalysis(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();
            var analysis = db.CostAnalysis.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x);
            foreach (var a in analysis)
            {
                salesPerManHourLabel.Text = @"Sales Per Man Hour: " + $@"{a.SalesPerManHour:c}";
                salaryPercentageLabel.Text = @"Salary Percentage: " + $@"{a.SalaryPercentage:p}";
                salaryDollarsLabel.Text = @"Salary Dollars: " + $@"{a.SalaryDollar:c}";
                suppliesLabel.Text = @"Supplies: " + $@"{a.Supplies}";
            }          
        }

        #endregion

        private string CheckForHoliday(DayOfWeek day)
        {
            var holidayText = string.Empty;
            //Get the currently selected date.
            var date = monthCalendar.SelectionStart;
            //Now make sure the selected day on the calendar (the date that came from the database) is not the same
            //as the day provided, unless the day is Saturday. If the end of week date is not on a Saturday and the 
            //supplied day is Monday, than subtracting 5 would yield an incorrect date.
            if (date.DayOfWeek == DayOfWeek.Saturday)
            {
                switch (day)
                {
                    case DayOfWeek.Sunday:
                        date = date.AddDays(-6);
                        break;
                    case DayOfWeek.Monday:
                        date = date.AddDays(-5);
                        break;
                    case DayOfWeek.Tuesday:
                        date = date.AddDays(-4);
                        break;
                    case DayOfWeek.Wednesday:
                        date = date.AddDays(-3);
                        break;
                    case DayOfWeek.Thursday:
                        date = date.AddDays(-2);
                        break;
                    case DayOfWeek.Friday:
                        date = date.AddDays(-1);
                        break;
                }
            }
            var holiday = Holiday.IsHoliday(date);
            switch (holiday)
            {
                case Holidays.Thanksgiving:
                case Holidays.Christmas:
                case Holidays.NewYearsDay:
                    holidayText = @"Closed for " + TextFormat.AddSpacesToSentence(holiday.ToString(), false);
                    break;
            }
            return holidayText;
        }

        #region Form Reset And Data Loading Methods

        /// <summary>
        /// Checks to see if the newly selected date is one that should be loaded.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ValidateDateChanged(object sender, DateRangeEventArgs e)
        {
            if (e.Start == _currentActiveDate || !monthCalendar.BoldedDates.Contains(e.Start)) return;
            _currentActiveDate = e.Start;
            //Load the specified date from the database.
            LoadDate(e.Start);
        }

        private void ClearForm()
        {
            projectionsDataGridView.Rows.Clear();
            inventoryDataGridView.Rows.Clear();
            actualSalesDataGridView.Rows.Clear();
            invoicesDataGridView.Rows.Clear();
            commentsTextBox.Text = string.Empty;
            commentMainGroupBox.Text = @"Comments";
            dateTimeGroupBox.Text = string.Empty;
            salesPerManHourLabel.Text = @"Sales Per Man Hour:";
            salaryPercentageLabel.Text = @"Salary Percentage:";
            salaryDollarsLabel.Text = @"Salary Dollars:";
            suppliesLabel.Text = @"Supplies:";
            costAnalysisGroupBox.Text = @"Cost Analysis";
            sundayWeeklySalesLabel.Text = @"Sunday: ";
            mondayWeeklySalesLabel.Text = @"Monday: ";
            tuesadayWeeklySalesLabel.Text = @"Tuesday: ";
            wednesdayWeeklySalesLabel.Text = @"Wednesday: ";
            thursdayWeeklySalesLabel.Text = @"Wednesday: ";
            fridayWeeklySalesLabel.Text = @"Friday: ";
            saturdayWeeklySalesLabel.Text = @"Saturday: ";
            totalWeeklySalesLabel.Text = @"Total Sales: ";
            weeklySalesGroupBox.Text = @"Weekly Sales";
            sundayTaxableLabel.Text = @"Sunday: ";
            mondayTaxableLabel.Text = @"Monday: ";
            tuesdayTaxableLabel.Text = @"Tuesday: ";
            wednesdayTaxableLabel.Text = @"Wednesday: ";
            thursdayTaxableLabel.Text = @"Wednesday: ";
            fridayTaxableLabel.Text = @"Friday: ";
            saturdayTaxableLabel.Text = @"Saturday: ";
            totalTaxableLabel.Text = @"Total: ";
            taxableGroupBox.Text = @"Taxable";
            departmentSalesLabel.Text = @"Department Sales: ";
            salesProducedLabel.Text = @"Sales Produced By Ad Items (A):";
            remainingSalesLabel.Text = @"Remaining Sales:";
            totalProfitFromAdItemsLabel.Text = @"Total Profit Return" + Environment.NewLine + @"From Ad Items (B): ";
            totalProfitReturnFromRemaingLabel.Text = @"Total Profit Return" + Environment.NewLine + @"From Remaining Sales: ";
            totalProfitReturnLabel.Text = @"Total Profit Return: ";
            grossProfitTotalSales.Text = @"Total Sales:";
            grossProfitLessCostOfSales.Text = @"Less Cost of Sales: ";
            grossProfitDollarGrossProfitLabel.Text = @"Dollar Gross Profit: ";
            perfectGrossProfitLabel.Text = @"Percent Gross Profit: ";
            grossProfitEstimatedWeeklyDeptmartmentExpenseLabel.Text = @"Estimated Weekly" + Environment.NewLine + @"Department Expense: ";
        }

        private void RefreshDateListing()
        {
            var db = new AdvertisingProfitControlModel();
            monthCalendar.BoldedDates = db.WeekEndingDates.Select(zdate => zdate.EndingDate).ToArray();
            if (monthCalendar.BoldedDates.Length > 0)
            {
                modifyRecordMainMenu.Enabled = true;
                modifyRecordMainMenu.ToolTipText = string.Empty;
                clearCurrentRecordSelectedDateToolsMainMenu.Enabled = true;
                clearCurrentRecordSelectedDateToolsMainMenu.ToolTipText = string.Empty;
                clearCurrentActiveYearTools.Enabled = true;
                clearCurrentActiveYearTools.ToolTipText = string.Empty;
            }
            else
            {
                dateTimeGroupBox.Text = @"No records found in the database.";
                modifyRecordMainMenu.Enabled = false;
                modifyRecordMainMenu.ToolTipText = @"There are no records to modify.";
                clearCurrentRecordSelectedDateToolsMainMenu.Enabled = false;
                clearCurrentRecordSelectedDateToolsMainMenu.ToolTipText = @"There is no date to clear.";
                clearCurrentActiveYearTools.Enabled = false;
                clearCurrentActiveYearTools.ToolTipText = @"There are no records to clear.";
            }
        }

        #endregion

        #region Debug Tool Menu Items

        private void DisplayDebugTool(object sender, EventArgs e)
        {
            var form = new DebugDatabaseConverter();
            form.ShowDialog();
            RefreshDateListing();
        }

        /// <summary>
        /// Displays the legacy Add Record form. Completely for looks.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DisplayLegacyAddRecord(object sender, EventArgs e)
        {
            var form = new FrmAddRecord();
            form.ShowDialog();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DisplayRawDatabaseView(object sender, EventArgs e)
        {

        }

        #endregion

        private void ClearActiveDateYearRecords(object sender, EventArgs e)
        {
            var result = MessageBox.Show(@"Are you sure you want to remove all records for the year of " + _currentActiveDate.Year + @"? This cannot be undone!", @"Clear " + _currentActiveDate.Year + @"'s Records", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
            if (result != DialogResult.Yes) return;
            var db = new AdvertisingProfitControlModel();
            var distinctYears = db.WeekEndingDates.Select(x => x.EndingDate.Year).Distinct().ToArray();
            if (distinctYears.Length == 1)
            {
                result = MessageBox.Show(_currentActiveDate.Year + @" appears to be the only year, clearing it will wipe all data in the database. Are you sure you wish to continue?", @"Clear " + _currentActiveDate.Year + @"'s Records", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
                if (result != DialogResult.Yes) return;
            }
            if (ClearSelectedDate(_currentActiveDate.Year))
            {
                RefreshDateListing();
            }
            else
            {
                MessageBox.Show(@"Failed to clear the records for " + _currentActiveDate.Year + @".", @"Update Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #region Writing Operations

        private bool ClearSelectedDate(int year)
        {
            var db = new AdvertisingProfitControlModel();
            var status = true;

            using (var scope = new TransactionScope())
            {
                try
                {
                    var dateRecords = db.WeekEndingDates.Select(x => x).Where(x => x.EndingDate.Year == year).ToArray();
                    foreach (var dateRecord in dateRecords)
                    {
                        ClearProjectionRecords(dateRecord);
                        ClearInventoyRecords(dateRecord);
                        ClearActualSalesRecords(dateRecord);
                        ClearInvoices(dateRecord);
                        ClearWeeklySales(dateRecord);
                        ClearTaxableRecords(dateRecord);
                        ClearCostAnalysisRecords(dateRecord);
                        ClearCommentsRecords(dateRecord);
                        db.WeekEndingDates.Remove(dateRecord);
                    }
                    db.SaveChanges();
                    scope.Complete();
                }
                catch (DbUpdateException e)
                {
                    _console.WriteToLog(FrmLogConsole.Level.Error, e.Message);
                    status = false;
                }
            }

            return status;
        }

        private void ClearProjectionRecords(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();

            using (var scope = new TransactionScope())
            {
                var projections = db.Projections.Select(x => x).Where(x => x.FkDateId == dateRecord.Id);
                db.Projections.RemoveRange(projections);
                db.SaveChanges();
                scope.Complete();
            }
        }

        private void ClearInventoyRecords(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();

            using (var scope = new TransactionScope())
            {
                var inventories = db.Inventories.Select(x => x).Where(x => x.FkDateId == dateRecord.Id);
                db.Inventories.RemoveRange(inventories);
                db.SaveChanges();
                scope.Complete();
            }
        }

        private void ClearActualSalesRecords(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();

            using (var scope = new TransactionScope())
            {
                var actualSales = db.ActualSales.Select(x => x).Where(x => x.FkDateId == dateRecord.Id);
                db.ActualSales.RemoveRange(actualSales);
                db.SaveChanges();
                scope.Complete();
            }
        }

        private void ClearInvoices(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();

            using (var scope = new TransactionScope())
            {
                var invoices = db.Invoices.Select(x => x).Where(x => x.FkDateId == dateRecord.Id);
                db.Invoices.RemoveRange(invoices);
                db.SaveChanges();
                scope.Complete();
            }
        }

        private void ClearWeeklySales(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();

            using (var scope = new TransactionScope())
            {
                var weeklySales = db.WeeklySales.Select(x => x).Where(x => x.FkDateId == dateRecord.Id);
                db.WeeklySales.RemoveRange(weeklySales);
                db.SaveChanges();
                scope.Complete();
            }
        }

        private void ClearTaxableRecords(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();

            using (var scope = new TransactionScope())
            {
                var taxables = db.Taxables.Select(x => x).Where(x => x.FkDateId == dateRecord.Id);
                db.Taxables.RemoveRange(taxables);
                db.SaveChanges();
                scope.Complete();
            }
        }

        private void ClearCostAnalysisRecords(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();

            using (var scope = new TransactionScope())
            {
                var costAnalysis = db.CostAnalysis.Select(x => x).Where(x => x.FkDateId == dateRecord.Id);
                db.CostAnalysis.RemoveRange(costAnalysis);
                db.SaveChanges();
                scope.Complete();
            }
        }

        private void ClearCommentsRecords(WeekEndingDate dateRecord)
        {
            var db = new AdvertisingProfitControlModel();

            using (var scope = new TransactionScope())
            {
                var comments = db.Notes.Select(x => x).Where(x => x.FkDateId == dateRecord.Id);
                db.Notes.RemoveRange(comments);
                db.SaveChanges();
                scope.Complete();
            }
        }

        #endregion
    }
}