/*******************************************************************************
***  This file is part of Dux Rei Nummariae.                                 ***
***                                                                          ***
***  Copyright (C) 2017, 2018, 2019, 2020, 2021                              ***
***  John Schneiderman <Licensing _AT_ Schneiderman _DOT_ me>                ***
***                                                                          ***
***  This program is free software: you can redistribute it and/or modify it ***
***  under the terms of the GNU Affero General Public License as published   ***
***  by the Free Software Foundation, either version 3 of the License, or    ***
***  (at your option) any later version.                                     ***
***                                                                          ***
***  This program is distributed in the hope that it will be useful, but     ***
***  WITHOUT ANY WARRANTY; without even the implied warranty of              ***
***  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    ***
***  See the GNU Affero General Public License for more details.             ***
***                                                                          ***
***  You should have received a copy of the GNU Affero General Public License***
***  along with this program. If not, see <http://www.gnu.org/licenses/>.    ***
*******************************************************************************/
#include "MainWindow.h"
using pecunia::currency::Iso4217Codes;
using pecunia::currency::Money;
using std::set;
using ::QString;
using std::vector;
using std::chrono::hours;
using std::map;
using std::pair;
using std::set;
using drn::accounting::Account;
using drn::accounting::AccountCode;
using drn::adviser::BudgetHealth;
using drn::banking::BankAccount;
using drn::banking::BankAccountType;
using drn::banking::SupportedAccountTypes;
using drn::banking::BankMap;
using drn::banking::BankName;
using drn::budgeting::BillMap;
using drn::budgeting::BudgetItemDue;
using drn::budgeting::BudgetSource;
using drn::budgeting::BudgetTotals;
using drn::budgeting::BudgetItemTypes;
using drn::budgeting::DebtMap;
using drn::budgeting::GoalMap;
using drn::budgeting::NontrackMap;
using drn::budgeting::WageMap;
using drn::conversion::ConversionMap;
using drn::desktop_ui::MainWindow;
using drn::foundation::Days;
using drn::foundation::Switch;
using drn::navigation::BudgetBankAccount;

#include <QCloseEvent>
using ::QCloseEvent;
#include <QDebug>
// using qInfo
#include <QFileDialog>
using ::QFileDialog;
#include <QMessageBox>
using ::QMessageBox;
#include <QStandardPaths>
using ::QStandardPaths;
#include <QTimer>
using ::QTimer;

#include <chrono>
using namespace std::chrono_literals;
#include <memory>
using std::make_unique;

#include <drn/config.h>
// using DuxReiNummariae_APPLICATION_LONG_NAME.
// using DuxReiNummariae_APPLICATION_SHORT_NAME.
// using DuxReiNummariae_APPLICATION_TRANSLATED_NAME
// using DuxReiNummariae_APPLICATION_URL.
// using DuxReiNummariae_COPYRIGHT_YEARS.
// using DuxReiNummariae_FULL_VERSION.
#include <file-storage/Constants.hpp>
using drn::file_storage::budgetFileExtention_;
#include <foundation/QtMemory.hpp>
using drn::foundation::makeQtPtr;
#include <foundation/Optional.hpp>
using drn::foundation::Optional;
#include "internal/AccountsWidget.h"
using drn::desktop_ui::internal::AccountsWidget;
#include "internal/BanksWidget.h"
using drn::desktop_ui::internal::BanksWidget;
#include "internal/BillsWidget.h"
using drn::desktop_ui::internal::BillsWidget;
#include "internal/ConfigureDialog.h"
using drn::desktop_ui::internal::ConfigureDialog;
#include "internal/DashboardWidget.h"
using drn::desktop_ui::internal::DashboardWidget;
#include "internal/DebtsWidget.h"
using drn::desktop_ui::internal::DebtsWidget;
#include "internal/ExchangeRatesDialog.h"
using drn::desktop_ui::internal::ExchangeRatesDialog;
#include "internal/GoalsWidget.h"
using drn::desktop_ui::internal::GoalsWidget;
#include "internal/NontracksWidget.h"
using drn::desktop_ui::internal::NontracksWidget;
#include "internal/LedgersWidget.h"
using drn::desktop_ui::internal::LedgersWidget;
#include "internal/PostBudgetItemsWidget.h"
using drn::desktop_ui::internal::PostBudgetItemsWidget;
#include "internal/WagesWidget.h"
using drn::desktop_ui::internal::WagesWidget;
#include <ui_MainWindow.h>
using Ui::BwmMnwndw;


const qint32 MainWindow::statusBarTimeOut_{5000};
const QString MainWindow::budgetFileExtentionFilter_{
	QObject::tr("Budget Files (%1);;All Files (*.*)").arg(budgetFileExtention_)
};

void MainWindow::onShowAbout()
{
	qInfo() << "Showing about information.";
	QMessageBox::about(
		this,
		tr("About %1").arg(DuxReiNummariae_APPLICATION_SHORT_NAME),
		tr("%1 %2\n"
			"Copyright (C) %3 John M. Schneiderman\n\n"
			"More Information:\n\t%4\n\n"
			"%5 comes with ABSOLUTELY NO WARRANTY;\n"
			"This is free software, and you are welcome to redistribute it\n"
			"under certain conditions; see the LICENSE file for details,\n"
			"or the Free Software Foundation's AGPL."
		).arg(DuxReiNummariae_APPLICATION_LONG_NAME)
			.arg(DuxReiNummariae_FULL_VERSION)
			.arg(DuxReiNummariae_COPYRIGHT_YEARS)
			.arg(DuxReiNummariae_APPLICATION_URL)
			.arg(DuxReiNummariae_APPLICATION_SHORT_NAME)
	);
}

void MainWindow::onShowAboutQt()
{
	qInfo() << "Showing about Qt information.";
	QMessageBox::aboutQt(this, tr("%1 Qt Information").arg(DuxReiNummariae_APPLICATION_SHORT_NAME));
}

void MainWindow::onDisplayBudgetInterface(const BudgetItemTypes& type)
{
	qDebug() << "Displaying selected budget type" << presentationText(type);

	switch (type)
	{
	case BudgetItemTypes::Bill:
		emit prepareShowBills();
		break;
	case BudgetItemTypes::Debt:
		emit prepareShowDebts();
		break;
	case BudgetItemTypes::Goal:
		emit prepareShowGoals();
		break;
	case BudgetItemTypes::Nontrack:
		emit prepareShowNontracks();
		break;
	case BudgetItemTypes::Wage:
		emit prepareShowWages();
		break;
	case BudgetItemTypes::Unknown:
	default:
		Q_ASSERT(false && "Unhandled selected budget total item type.");
	};
}

void MainWindow::closeEvent(QCloseEvent* event)
{
	if (this->confirmedExit_)
		event->accept();
	else
	{
		event->ignore();
		QTimer::singleShot(100ms, this, &MainWindow::checkIsBudgetFileDirty);
	}
}

void MainWindow::onShowBills(
	const BillMap& bills,
	const hours& hrInWeek,
	const Days& dayInWeek,
	const Iso4217Codes& preferred,
	const set<Iso4217Codes>& usableCurrencies
)
{
	qInfo() << "Showing Bills.";
	auto w{makeQtPtr<BillsWidget>(bills, hrInWeek, dayInWeek, preferred, usableCurrencies, this)};
	connect(
		w.observed(),
		&BillsWidget::added,
		this,
		[this] (const auto& bill)
		{
			emit added(BudgetItemTypes::Bill, bill, {});
		}
	);
	connect(
		w.observed(),
		&BillsWidget::renamed,
		this,
		[this] (const auto& original, const auto& changed)
		{
			emit renamed(BudgetItemTypes::Bill, original, changed);
		}
	);
	connect(
		w.observed(),
		&BillsWidget::updated,
		this,
		[this] (const auto& bill)
		{
			emit updated(BudgetItemTypes::Bill, bill, {});
		}
	);
	connect(
		w.observed(),
		&BillsWidget::removed,
		this,
		[this] (const auto& source)
		{
			emit removed(BudgetItemTypes::Bill, source);
		}
	);
	connect(this, &MainWindow::saveSucceeded, w.observed(), &BillsWidget::onSaveSucceeded);
	connect(this, &MainWindow::saveFailed, w.observed(), &BillsWidget::onSaveFailed);
	connect(w.observed(), &BillsWidget::closing, this, &MainWindow::prepareShowDashboard);
	this->setCentralWidget(w.observed());
}

void MainWindow::onShowDebts(
	const DebtMap& debts,
	const set<BudgetBankAccount>& accounts,
	const set<BankName>& banks,
	const hours& hrInWeek,
	const Days& dayInWeek,
	const Iso4217Codes& preferred,
	const set<Iso4217Codes>& usableCurrencies
)
{
	qInfo() << "Showing Debts.";
	auto w{
		makeQtPtr<DebtsWidget>(
			debts,
			accounts,
			hrInWeek,
			dayInWeek,
			banks,
			preferred,
			usableCurrencies,
			this
		)
	};
	connect(
		w.observed(),
		&DebtsWidget::added,
		this,
		[this] (const auto& debt, const Optional<BankAccount>& ba)
		{
			emit added(BudgetItemTypes::Debt, debt, ba);
		}
	);
	connect(
		w.observed(),
		&DebtsWidget::renamed,
		this,
		[this] (const auto& original, const auto& changed)
		{
			emit renamed(BudgetItemTypes::Debt, original, changed);
		}
	);
	connect(
		w.observed(),
		&DebtsWidget::updated,
		this,
		[this] (const auto& debt, const Optional<BankAccount>& ba)
		{
			emit updated(BudgetItemTypes::Debt, debt, ba);
		}
	);
	connect(
		w.observed(),
		&DebtsWidget::removed,
		this,
		[this] (const auto& source)
		{
			emit removed(BudgetItemTypes::Debt, source);
		}
	);
	connect(this, &MainWindow::saveSucceeded, w.observed(), &DebtsWidget::onSaveSucceeded);
	connect(this, &MainWindow::saveFailed, w.observed(), &DebtsWidget::onSaveFailed);
	connect(w.observed(), &DebtsWidget::closing, this, &MainWindow::prepareShowDashboard);
	this->setCentralWidget(w.observed());
}

void MainWindow::onShowGoals(
	const GoalMap& goals,
	const set<BudgetBankAccount>& accounts,
	const set<BankName>& banks,
	const hours& hoursInWeek,
	const Days& daysInWeek,
	const Iso4217Codes& preferredCurrency,
	const set<Iso4217Codes>& usableCurrencies,
	const Money& wagesTotal
)
{
	qInfo() << "Showing Goals.";
	auto w{
		makeQtPtr<GoalsWidget>(
			goals,
			accounts,
			wagesTotal,
			hoursInWeek,
			daysInWeek,
			banks,
			preferredCurrency,
			usableCurrencies,
			this
		)
	};
	connect(
		w.observed(),
		&GoalsWidget::lookUpBankAccounts,
		this,
		&MainWindow::lookUpBankAccounts
	);
	connect(
		this,
		&MainWindow::lookedUpBankAccounts,
		w.observed(),
		&GoalsWidget::onLookedUpBankAccounts
	);
	connect(
		w.observed(),
		&GoalsWidget::added,
		this,
		[this] (const auto& goal, const Optional<BankAccount>& ba)
		{
			emit added(BudgetItemTypes::Goal, goal, ba);
		}
	);
	connect(
		w.observed(),
		&GoalsWidget::renamed,
		this,
		[this] (const auto& original, const auto& changed)
		{
			emit renamed(BudgetItemTypes::Goal, original, changed);
		}
	);
	connect(
		w.observed(),
		&GoalsWidget::updated,
		this,
		[this] (const auto& goal, const Optional<BankAccount>& ba)
		{
			emit updated(BudgetItemTypes::Goal, goal, ba);
		}
	);
	connect(
		w.observed(),
		&GoalsWidget::removed,
		this,
		[this] (const auto& source)
		{
			emit removed(BudgetItemTypes::Goal, source);
		}
	);
	connect(this, &MainWindow::saveSucceeded, w.observed(), &GoalsWidget::onSaveSucceeded);
	connect(this, &MainWindow::saveFailed, w.observed(), &GoalsWidget::onSaveFailed);
	connect(w.observed(), &GoalsWidget::closing, this, &MainWindow::prepareShowDashboard);
	this->setCentralWidget(w.observed());
}

void MainWindow::onShowNontracks(
	const NontrackMap& nontracks,
	const hours& hrInWeek,
	const Days& dayInWeek,
	const Iso4217Codes& preferred,
	const set<Iso4217Codes>& usableCurrencies
)
{
	qInfo() << "Showing Nontracks.";
	auto w{
		makeQtPtr<NontracksWidget>(
			nontracks,
			hrInWeek,
			dayInWeek,
			preferred,
			usableCurrencies,
			this
		)
	};
	connect(
		w.observed(),
		&NontracksWidget::added,
		this,
		[this] (const auto& nontrack)
		{
			emit added(BudgetItemTypes::Nontrack, nontrack, {});
		}
	);
	connect(
		w.observed(),
		&NontracksWidget::renamed,
		this,
		[this] (const auto& original, const auto& changed)
		{
			emit renamed(BudgetItemTypes::Nontrack, original, changed);
		}
	);
	connect(
		w.observed(),
		&NontracksWidget::updated,
		this,
		[this] (const auto& nontrack)
		{
			emit updated(BudgetItemTypes::Nontrack, nontrack, {});
		}
	);
	connect(
		w.observed(),
		&NontracksWidget::removed,
		this,
		[this] (const auto& source)
		{
			emit removed(BudgetItemTypes::Nontrack, source);
		}
	);
	connect(this, &MainWindow::saveSucceeded, w.observed(), &NontracksWidget::onSaveSucceeded);
	connect(this, &MainWindow::saveFailed, w.observed(), &NontracksWidget::onSaveFailed);
	connect(w.observed(), &NontracksWidget::closing, this, &MainWindow::prepareShowDashboard);
	this->setCentralWidget(w.observed());
}

void MainWindow::onShowWages(
	const WageMap& wages,
	const hours& hrInWeek,
	const Days& dayInWeek,
	const Iso4217Codes& preferred,
	const set<Iso4217Codes>& usableCurrencies
)
{
	qInfo() << "Showing Wages.";
	auto w{makeQtPtr<WagesWidget>(wages, hrInWeek, dayInWeek, preferred, usableCurrencies, this)};
	connect(
		w.observed(),
		&WagesWidget::added,
		this,
		[this] (const auto& wage)
		{
			emit added(BudgetItemTypes::Wage, wage, {});
		}
	);
	connect(
		w.observed(),
		&WagesWidget::renamed,
		this,
		[this] (const auto& original, const auto& changed)
		{
			emit renamed(BudgetItemTypes::Wage, original, changed);
		}
	);
	connect(
		w.observed(),
		&WagesWidget::updated,
		this,
		[this] (const auto& wage)
		{
			emit updated(BudgetItemTypes::Wage, wage, {});
		}
	);
	connect(
		w.observed(),
		&WagesWidget::removed,
		this,
		[this] (const auto& source)
		{
			emit removed(BudgetItemTypes::Wage, source);
		}
	);
	connect(this, &MainWindow::saveSucceeded, w.observed(), &WagesWidget::onSaveSucceeded);
	connect(this, &MainWindow::saveFailed, w.observed(), &WagesWidget::onSaveFailed);
	connect(w.observed(), &WagesWidget::closing, this, &MainWindow::prepareShowDashboard);
	this->setCentralWidget(w.observed());
}

void MainWindow::onShowAccounts(
	const set<Account>& accounts,
	const set<Iso4217Codes>& usableCurrencies
)
{
	qInfo() << "Showing Accounts Widget.";
	auto aw{makeQtPtr<AccountsWidget>(accounts, usableCurrencies, this)};
	connect(aw.observed(), &AccountsWidget::added, this, &MainWindow::addedAct);
	connect(aw.observed(), &AccountsWidget::recoded, this, &MainWindow::recoded);
	connect(aw.observed(), &AccountsWidget::closed, this, &MainWindow::closed);
	connect(aw.observed(), &AccountsWidget::opened, this, &MainWindow::opened);
	connect(aw.observed(), &AccountsWidget::removed, this, &MainWindow::removedAct);
	connect(this, &MainWindow::accountSaveSucceeded, aw.observed(), &AccountsWidget::onSaveSucceeded);
	connect(this, &MainWindow::accountSaveFailed, aw.observed(), &AccountsWidget::onSaveFailed);
	connect(aw.observed(), &AccountsWidget::closing, this, &MainWindow::prepareShowDashboard);
	this->setCentralWidget(aw.observed());
}

void MainWindow::onShowLedgers(
	const set<Account>& accounts,
	const set<Iso4217Codes>& usableCurrencies
)
{
	qInfo() << "Showing Ledgers Widget.";
	auto w{makeQtPtr<LedgersWidget>(accounts, usableCurrencies, this)};
	connect(
		w.observed(),
		&LedgersWidget::selectedLedger,
		this,
		&MainWindow::selectedLedger
	);
	connect(
		this,
		&MainWindow::showLedger,
		w.observed(),
		&LedgersWidget::onShowLedger
	);
	connect(
		w.observed(),
		&LedgersWidget::removedTransaction,
		this,
		&MainWindow::removedTransaction
	);
	connect(
		w.observed(),
		&LedgersWidget::addedTransaction,
		this,
		&MainWindow::addedTransaction
	);
	connect(
		w.observed(),
		&LedgersWidget::updatedTransaction,
		this,
		&MainWindow::updatedTransaction
	);
	connect(
		w.observed(),
		&LedgersWidget::markCleared,
		this,
		&MainWindow::markCleared
	);
	connect(
		w.observed(),
		&LedgersWidget::markReconciled,
		this,
		&MainWindow::markReconciled
	);
	connect(this, &MainWindow::transactionSaveSucceeded, w.observed(), &LedgersWidget::onSaveSucceeded);
	connect(this, &MainWindow::transactionSaveFailed, w.observed(), &LedgersWidget::onSaveFailed);
	connect(
		w.observed(),
		&LedgersWidget::closing,
		this,
		&MainWindow::prepareShowDashboard
	);
	this->setCentralWidget(w.observed());
}

void MainWindow::onShowBanks(
	const BankMap& banks,
	const set<Account>& allAccounts,
	const set<Iso4217Codes>& usableCurrencies,
	const set<BankAccountType>& accountTypes
)
{
	qInfo() << "Showing Bank Entry Widget.";
	auto w{
		makeQtPtr<BanksWidget>(banks, allAccounts, accountTypes, usableCurrencies, this)
	};
	connect(
		w.observed(),
		&BanksWidget::added,
		this,
		&MainWindow::bankAdded
	);
	connect(
		w.observed(),
		&BanksWidget::updated,
		this,
		&MainWindow::bankUpdated
	);
	connect(
		w.observed(),
		&BanksWidget::rename,
		this,
		&MainWindow::bankRenamed
	);
	connect(
		w.observed(),
		&BanksWidget::closed,
		this,
		&MainWindow::bankClosed
	);
	connect(
		w.observed(),
		&BanksWidget::opened,
		this,
		&MainWindow::bankOpened
	);
	connect(
		w.observed(),
		&BanksWidget::removed,
		this,
		&MainWindow::bankRemoved
	);
	connect(
		w.observed(),
		&BanksWidget::addedAccount,
		this,
		&MainWindow::addedBankAccount
	);
	connect(
		w.observed(),
		&BanksWidget::renamedAccount,
		this,
		&MainWindow::renamedBankAccount
	);
	connect(
		w.observed(),
		&BanksWidget::removedAccount,
		this,
		&MainWindow::removedBankAccount
	);
	connect(this, &MainWindow::bankSaveSucceeded, w.observed(), &BanksWidget::onSaveSucceeded);
	connect(this, &MainWindow::bankSaveFailed, w.observed(), &BanksWidget::onSaveFailed);
	connect(
		this,
		&MainWindow::bankAccountSaveSucceeded,
		w.observed(),
		&BanksWidget::onAccountSaveSucceeded
	);
	connect(
		this,
		&MainWindow::bankAccountSaveFailed,
		w.observed(),
		&BanksWidget::onAccountSaveFailed
	);
	connect(w.observed(), &BanksWidget::closing, this, &MainWindow::prepareShowDashboard);
	this->setCentralWidget(w.observed());
}

void MainWindow::onShowPostBudgetItems(
	const qint32& dueWithin,
	const vector<BudgetItemDue>& items,
	const set<BankName>& banks,
	const Iso4217Codes& preferredCurrency,
	const set<Iso4217Codes>& usableCurrencies
)
{
	qInfo() << "Showing Post Budget Items Widget.";
	auto w{
		makeQtPtr<PostBudgetItemsWidget>(
			dueWithin,
			items,
			banks,
			preferredCurrency,
			usableCurrencies,
			this
		)
	};
	connect(
		w.observed(),
		&PostBudgetItemsWidget::lookUpBankAccounts,
		this,
		&MainWindow::lookUpBankAccounts
	);
	connect(
		this,
		&MainWindow::lookedUpBankAccounts,
		w.observed(),
		&PostBudgetItemsWidget::onLookedUpBankAccounts
	);
	connect(
		w.observed(),
		&PostBudgetItemsWidget::lookUpBudgetItemDueSources,
		this,
		&MainWindow::lookUpBudgetItemSources
	);
	connect(
		this,
		&MainWindow::lookedUpBudgetItemSources,
		w.observed(),
		&PostBudgetItemsWidget::onLookUpBudgetItemDueSources
	);
	connect(
		w.observed(),
		&PostBudgetItemsWidget::posted,
		this,
		&MainWindow::postBudgetItemPosted
	);
	connect(
		w.observed(),
		&PostBudgetItemsWidget::postedUnexpected,
		this,
		&MainWindow::postBudgetItemPostedUnexpected
	);
	connect(
		w.observed(),
		&PostBudgetItemsWidget::skipped,
		this,
		&MainWindow::postBudgetItemSkipped
	);
	connect(
		this,
		&MainWindow::postBudgetItemSaveSucceeded,
		w.observed(),
		&PostBudgetItemsWidget::onSaveSucceeded
	);
	connect(
		this,
		&MainWindow::postBudgetItemSaveFailed,
		w.observed(),
		&PostBudgetItemsWidget::onSaveFailed
	);
	connect(w.observed(), &PostBudgetItemsWidget::closing, this, &MainWindow::prepareShowDashboard);
	this->setCentralWidget(w.observed());
}

void MainWindow::onShowConfirmNewBudgetFile()
{
	const auto response{
		QMessageBox::question(
			this,
			tr("Confirm New Budget"),
			tr(
				"Are you sure you wish to create a new budget?\n"
				"This will lose all current changes."
			),
			QMessageBox::Yes | QMessageBox::No,
			QMessageBox::No
		)
	};

	if (response == QMessageBox::No)
		return;
	emit confirmedNewBudgetFile();
}

void MainWindow::onClearedBudgetFile()
{
	QMainWindow::statusBar()->showMessage(tr("Created New Budget"), MainWindow::statusBarTimeOut_);
	emit prepareShowDashboard();
}

void MainWindow::onShowOpenBudgetFile()
{
	qInfo() << "Displaying Open Budget File Dialog.";
	auto fd{
		makeQtPtr<QFileDialog>(
			this,
			tr("Open Budget"),
			QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
			MainWindow::budgetFileExtentionFilter_
		)
	};
	fd->setAcceptMode(QFileDialog::AcceptOpen);
	connect(
		fd.observed(),
		&QFileDialog::fileSelected,
		this,
		&MainWindow::loadBudgetFile
	);
	connect(
		fd.observed(),
		&QFileDialog::finished,
		fd.observed(),
		&QFileDialog::deleteLater
	);
	fd->show();
}

void MainWindow::onShowConfirmLoadBudgetFile(const QString& filePathName)
{
	const auto response{
		QMessageBox::question(
			this,
			tr("Confirm Open Budget"),
			tr(
				"Are you sure you wish to open a the budget file:\n\t'%1'?\n"
				"This will lose all the current changes."
			).arg(filePathName),
			QMessageBox::Yes | QMessageBox::No,
			QMessageBox::No
		)
	};

	if (response == QMessageBox::No)
		return;
	emit confirmedLoadBudgetFile(filePathName, false);
}

void MainWindow::onLoadedBudgetFile(const QString& filePathName, const bool isReloaded)
{
	QMainWindow::statusBar()->showMessage(
		tr("%1: '%2'").arg(isReloaded ? tr("Reloaded") : tr("Opened")).arg(filePathName),
		MainWindow::statusBarTimeOut_
	);
	this->centralWidget()->deleteLater();
	emit prepareShowDashboard();
}

void MainWindow::onSavedBudgetFile(const QString& filePathName)
{
	qInfo() << "Successfully stored budget.";
	QMainWindow::statusBar()->showMessage(
		tr("Saved: '%1'").arg(filePathName),
		MainWindow::statusBarTimeOut_
	);
}

void MainWindow::onShowSaveAsBudgetFile()
{
	qInfo() << "Displaying Save As Budget File Dialog.";
	auto fd{
		makeQtPtr<QFileDialog>(
			this,
			tr("Save Budget As..."),
			QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
			budgetFileExtentionFilter_
		)
	};
	fd->setAcceptMode(QFileDialog::AcceptSave);
	connect(
		fd.observed(),
		&QFileDialog::fileSelected,
		this,
		&MainWindow::saveAsBudgetFile
	);
	connect(
		fd.observed(),
		&QFileDialog::finished,
		fd.observed(),
		&QFileDialog::deleteLater
	);
	fd->show();
}

void MainWindow::onShowNoBudgetFileOpened()
{
	qDebug() << "There is not a budget file currently to reload.";
	QMainWindow::statusBar()->showMessage(
		tr("No budget file opened to reload."),
		MainWindow::statusBarTimeOut_
	);
}

void MainWindow::onCheckCleanExit(const Switch& isDirty)
{
	if (isDirty)
	{
		const auto response{
			QMessageBox::question(
				this,
				tr("Confirm Exiting"),
				tr(
					"Are you sure you wish to exit?\n"
					"There are unsaved changes. Exiting will lose all current changes."
				),
				QMessageBox::Yes | QMessageBox::No,
				QMessageBox::No
			)
		};

		if (response == QMessageBox::No)
		{
			this->confirmedExit_.flipOff();
			return;
		}
	}
	this->confirmedExit_.flipOn();
	Q_ASSERT(this->close());
}

void MainWindow::show()
{
	QMainWindow::show();
	emit prepareShowDashboard();
}

void MainWindow::onShowDashboard(
	const BudgetTotals& bt,
	const vector<BudgetItemDue>& due,
	const BudgetHealth& bh,
	const Days& dueWithin,
	const Iso4217Codes& preferred
)
{
	qInfo() << "Showing dashboard.";
	auto dw{makeQtPtr<DashboardWidget>(bt, due, bh, dueWithin, preferred, this)};
	connect(
		dw.observed(),
		&DashboardWidget::displayBudgetInterface,
		this,
		&MainWindow::onDisplayBudgetInterface
	);
	this->setCentralWidget(dw.observed());
}

void MainWindow::onShowExchangeRates(
	const ConversionMap& currentConversions,
	const set<Iso4217Codes>& currencies
)
{
	qInfo() << "Showing exchange rates.";
	auto w{makeQtPtr<ExchangeRatesDialog>(currencies, currentConversions, this)};
	connect(
		w.observed(),
		&ExchangeRatesDialog::fetchExchangeRates,
		this,
		&MainWindow::fetchExchangeRates
	);
	connect(
		this,
		&MainWindow::fetchedExchangeRates,
		w.observed(),
		&ExchangeRatesDialog::onFetchedExchangeRates
	);
	connect(
		w.observed(),
		&ExchangeRatesDialog::savedExchangeRates,
		this,
		&MainWindow::storeExchangeRates
	);
	w->show();
}

void MainWindow::onShowConfigure(
	const hours& hrInWeek,
	const Days& dayInWeek,
	const Days& notifyDueWithin,
	const Iso4217Codes& preferred,
	const set<Iso4217Codes>& usable,
	const set<Iso4217Codes>& used
)
{
	qInfo() << "Showing configuration.";

	auto cd{
		makeQtPtr<ConfigureDialog>(
			hrInWeek,
			dayInWeek,
			notifyDueWithin,
			preferred,
			usable,
			used,
			this
		)
	};
	connect(cd.observed(), &ConfigureDialog::updated, this, &MainWindow::configureUpdated);
	connect(this, &MainWindow::configureSaveSucceeded, cd.observed(), &ConfigureDialog::onSaveSucceeded);
	connect(this, &MainWindow::configureSaveFailed, cd.observed(), &ConfigureDialog::onSaveFailed);
	connect(cd.observed(), &ConfigureDialog::closing, this, &MainWindow::prepareShowDashboard);
	cd->show();
}

MainWindow::MainWindow() :
	QMainWindow{nullptr},
	ui_{make_unique<BwmMnwndw>()},
	confirmedExit_{}
{
	this->ui_->setupUi(this);

	//{ Connect File Menu Actions

	connect(this->ui_->newActn, &QAction::triggered, this, &MainWindow::newBudgetFile);
	connect(this->ui_->openActn, &QAction::triggered, this, &MainWindow::onShowOpenBudgetFile);
	connect(this->ui_->saveActn, &QAction::triggered, this, &MainWindow::saveBudgetFile);
	connect(this->ui_->saveAsActn, &QAction::triggered, this, &MainWindow::onShowSaveAsBudgetFile);
	connect(this->ui_->reloadActn, &QAction::triggered, this, &MainWindow::reloadBudgetFile);
	connect(this->ui_->exitActn, &QAction::triggered, this, &MainWindow::close);

	//}

	//{ Connect Budget Menu Actions

	connect(this->ui_->billsActn, &QAction::triggered, this, &MainWindow::prepareShowBills);
	connect(this->ui_->debtsActn, &QAction::triggered, this, &MainWindow::prepareShowDebts);
	connect(this->ui_->goalsActn, &QAction::triggered, this, &MainWindow::prepareShowGoals);
	connect(this->ui_->nontracksActn, &QAction::triggered, this, &MainWindow::prepareShowNontracks);
	connect(this->ui_->wagesActn, &QAction::triggered, this, &MainWindow::prepareShowWages);

	//}

	//{ Connect Account Menu Actions

	connect(this->ui_->accountsActn, &QAction::triggered, this, &MainWindow::prepareShowAccounts);
	connect(this->ui_->banksActn, &QAction::triggered, this, &MainWindow::prepareShowBanks);
	connect(this->ui_->ledgersActn, &QAction::triggered, this, &MainWindow::prepareShowLedgers);
	connect(
		this->ui_->postBudgetItemsActn,
		&QAction::triggered,
		this,
		&MainWindow::prepareShowPostBudgetItems
	);

	//}

	//{ Connect Settings Menu Actions

	connect(
		this->ui_->configureActn,
		&QAction::triggered,
		this,
		&MainWindow::prepareShowConfigure
	);
	connect(
		this->ui_->exchangeRatesActn,
		&QAction::triggered,
		this,
		&MainWindow::prepareShowExchangeRates
	);

	//}

	//{ Connect About Menu Actions

	connect(this->ui_->aboutActn, &QAction::triggered, this, &MainWindow::onShowAbout);
	connect(this->ui_->aboutQtActn, &QAction::triggered, this, &MainWindow::onShowAboutQt);

	//}

	this->setWindowTitle(
		QString{"%1 (%2)"}
			.arg(DuxReiNummariae_APPLICATION_LONG_NAME)
			.arg(tr(DuxReiNummariae_APPLICATION_TRANSLATED_NAME))
	);

	//{ Add Tool-bar Actions

	this->ui_->budgetTbr->addAction(tr("Bills"), this, &MainWindow::prepareShowBills);
	this->ui_->budgetTbr->addAction(tr("Debts"), this, &MainWindow::prepareShowDebts);
	this->ui_->budgetTbr->addAction(tr("Goals"), this, &MainWindow::prepareShowGoals);

	//}
}

MainWindow::~MainWindow() = default;
