/*******************************************************************************
***  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/>.    ***
*******************************************************************************/
#ifndef DRN_NAVIGATION_NAVIGATOR_H_
#define DRN_NAVIGATION_NAVIGATOR_H_

#include <pecunia/Money.h>
#include <QObject>
#include <QString>
#include <vector>

#include <chrono>
#include <map>
#include <memory>
#include <set>

#include <accounting/Account.h>
#include <accounting/AccountCode.h>
#include <accounting/LedgerBooks.h>
#include <accounting/Transaction.h>
#include <adviser/BudgetHealth.h>
#include <banking/BankName.h>
#include <banking/BankAccount.h>
#include <banking/BankAccountType.h>
#include <banking/BankMap.h>
#include <budgeting/BillMap.hpp>
#include <budgeting/Budget.h>
#include <budgeting/BudgetedMoney.h>
#include <budgeting/BudgetItemDue.h>
#include <budgeting/BudgetItemIdentifier.h>
#include <budgeting/BudgetItemTypes.h>
#include <budgeting/BudgetSource.h>
#include <budgeting/DebtMap.h>
#include <budgeting/GoalMap.h>
#include <budgeting/NontrackMap.hpp>
#include <budgeting/WageMap.hpp>
#include <conversion/ConversionMap.h>
#include <foundation/CalendarTime.hpp>
#include <foundation/ObserverPtr.hpp>
#include <foundation/Optional.hpp>
#include <foundation/Switch.h>
#include "navigation_export.h"
#include <network/QtAccessManager.h>
#include "UserPreferences.h"


namespace drn
{
namespace conversion
{

struct CurrencySource;

}
namespace navigation
{

struct BudgetBankAccount;

namespace internal
{

class BudgetBankLedgers;

}

class DRN_NAVIGATION_EXPORT Navigator : public ::QObject
{
	Q_OBJECT

	foundation::Optional<::QString> filePathName_;
	foundation::Switch isDirty_;
	std::unique_ptr<internal::BudgetBankLedgers> budgetBankLedgers_;
	UserPreferences preferences_;
	network::QtAccessManager networkManager_;
	foundation::ObserverPtr<conversion::CurrencySource> exchangeSource_;

public slots:
	void onPrepareShowBills();
	void onPrepareShowDebts();
	void onPrepareShowGoals();
	void onPrepareShowNontracks();
	void onPrepareShowWages();
	void onAdded(
		const budgeting::BudgetItemTypes& type,
		const budgeting::BudgetedMoney& item,
		const foundation::Optional<banking::BankAccount>& ba
	);
	void onRenamed(
		const budgeting::BudgetItemTypes& type,
		const budgeting::BudgetSource& original,
		const budgeting::BudgetSource& changed
	);
	void onUpdated(
		const budgeting::BudgetItemTypes& type,
		const budgeting::BudgetedMoney& item,
		const foundation::Optional<banking::BankAccount>& ba
	);
	void onRemoved(const budgeting::BudgetItemTypes& type, const budgeting::BudgetSource& source);
	void onPrepareShowAccounts();
	void onAddedAct(const accounting::Account& a);
	void onRecoded(const accounting::AccountCode& current, const accounting::AccountCode& changed);
	void onClosed(const accounting::AccountCode& code);
	void onOpened(const accounting::AccountCode& code);
	void onRemovedAct(const accounting::AccountCode& code);
	void onPrepareShowLedgers();
	void onSelectedLedger(const accounting::AccountCode& code);
	void onAddedTransaction(const accounting::Transaction& txn);
	void onUpdatedTransaction(
		const accounting::TransactionNumber& tn,
		const accounting::Transaction& txn
	);
	void onMarkCleared(const accounting::TransactionNumber& tn);
	void onMarkReconciled(const accounting::TransactionNumber& tn); // TODO: need to handle updating the reconciled bank account balance.
	void onRemovedTransaction(const accounting::TransactionNumber& tn);
	void onPrepareShowBanks();
	void onBankAdded(
		const banking::Bank& b,
		const std::map<
			accounting::AccountNumber,
			banking::SupportedAccountTypes
		>& accountNumbersBankTypes
	);
	void onBankUpdated(
		const banking::Bank& b,
		const std::map<
			accounting::AccountNumber,
			banking::SupportedAccountTypes
		>& accountNumbersBankTypes
	);
	void onBankRenamed(
		const banking::BankName& original,
		const banking::BankName& change
	);
	void onBankClosed(const banking::BankName& bn);
	void onBankOpened(const banking::BankName& bn);
	void onBankRemoved(const banking::BankName& bn);
	void onAddedBankAccount(
		const ::QString& name,
		const banking::SupportedAccountTypes& type,
		const pecunia::currency::Money& openingBalance
	);
	void onRenamedBankAccount(  // TODO: need to handle when a bank account is renamed for reconciled accounts.
		const accounting::AccountCode& original,
		const accounting::AccountCode& changed
	);
	void onRemovedBankAccount(const accounting::AccountCode& ac);
	void onPrepareShowPostBudgetItems();
	void onLookUpBankAccounts(const banking::BankName& bn);
	void onLookUpBudgetItemSources(
		const budgeting::BudgetItemIdentifier& requestedBy,
		const budgeting::BudgetItemTypes& bit
	);
	void onPostBudgetItemPosted(
		const budgeting::BudgetItemDue& bid,
		const accounting::AccountCode& ac,
		const ::QDate& postOn
	);
	void onPostBudgetItemPostedUnexpected(
		const budgeting::BudgetItemIdentifier& bii,
		const pecunia::currency::Money& amount,
		const accounting::AccountCode& ac,
		const ::QDate& postOn
	);
	void onPostBudgetItemSkipped(const budgeting::BudgetItemIdentifier& bii);
	void onNewBudgetFile();
	void onConfirmedNewBudgetFile();
	void onLoadBudgetFile(const ::QString& filePathName);
	void onConfirmedLoadBudgetFile(const ::QString& filePathName, const bool isReloaded);
	void onSaveBudgetFile();
	void onSaveAsBudgetFile(const ::QString& filePathName);
	void onReloadBudgetFile();
	void onCheckIsBudgetFileDirty();
	void onPrepareShowDashboard();
	void onPrepareShowExchangeRates();
	void onFetchExchangeRates(const std::set<pecunia::currency::Iso4217Codes>& currencies);
	void onStoreExchangeRates(const conversion::ConversionMap& conversions);
	void onPrepareShowConfigure();
	void onConfigureUpdated(
		const std::chrono::hours& hrInWeek,
		const foundation::Days& dayInWeek,
		const foundation::Days& notifyDueWithin,
		const pecunia::currency::Iso4217Codes& preferred,
		const std::set<pecunia::currency::Iso4217Codes>& usable
	);

signals:
	void showBills(
		const budgeting::BillMap& bills,
		const std::chrono::hours& hrInWeek,
		const foundation::Days& dayInWeek,
		const pecunia::currency::Iso4217Codes& preferred,
		const std::set<pecunia::currency::Iso4217Codes>& usableCurrencies
	);
	void showDebts(
		const budgeting::DebtMap& debts,
		const std::set<navigation::BudgetBankAccount>& accounts,
		const std::set<banking::BankName>& banks,
		const std::chrono::hours& hrInWeek,
		const foundation::Days& dayInWeek,
		const pecunia::currency::Iso4217Codes& preferred,
		const std::set<pecunia::currency::Iso4217Codes>& usableCurrencies
	);
	void showGoals(
		const budgeting::GoalMap& goals,
		const std::set<navigation::BudgetBankAccount>& accounts,
		const std::set<banking::BankName>& banks,
		const std::chrono::hours& hrInWeek,
		const foundation::Days& dayInWeek,
		const pecunia::currency::Iso4217Codes& preferred,
		const std::set<pecunia::currency::Iso4217Codes>& usableCurrencies,
		const pecunia::currency::Money& wagesTotal
	);
	void showNontracks(
		const budgeting::NontrackMap& nontracks,
		const std::chrono::hours& hrInWeek,
		const foundation::Days& dayInWeek,
		const pecunia::currency::Iso4217Codes& preferred,
		const std::set<pecunia::currency::Iso4217Codes>& usableCurrencies
	);
	void showWages(
		const budgeting::WageMap& wages,
		const std::chrono::hours& hrInWeek,
		const foundation::Days& dayInWeek,
		const pecunia::currency::Iso4217Codes& preferred,
		const std::set<pecunia::currency::Iso4217Codes>& usableCurrencies
	);
	void saveSucceeded(const budgeting::BudgetSource& source);
	void saveFailed(
		const foundation::Optional<budgeting::BudgetSource>& original,
		const QString& errorMessage,
		const foundation::Optional<budgeting::BudgetSource>& displayed
	);
	void showAccounts(
		const std::set<accounting::Account>& accounts,
		const std::set<pecunia::currency::Iso4217Codes>& usableCurrencies
	);
	void accountSaveSucceeded(const accounting::AccountCode& code);
	void accountSaveFailed(
		const foundation::Optional<accounting::AccountCode>& original,
		const ::QString& errorMessage,
		const foundation::Optional<accounting::AccountCode>& displayed
	);
	void showLedgers(
		const std::set<accounting::Account>& accounts,
		const std::set<pecunia::currency::Iso4217Codes>& usableCurrencies
	);
	void showLedger(const accounting::LedgerAccount& ledger);
	void transactionSaveSucceeded(
		const foundation::Optional<accounting::TransactionNumber>& displayed,
		const foundation::Optional<accounting::TransactionNumber>& posted
	);
	void transactionSaveFailed(
		const foundation::Optional<accounting::TransactionNumber>& tn,
		const ::QString& errorMessage
	);
	void showBanks(
		const banking::BankMap& banks,
		const std::set<accounting::Account>& allAccounts,
		const std::set<pecunia::currency::Iso4217Codes>& usableCurrencies,
		const std::set<banking::BankAccountType>& accountTypes
	);
	void bankSaveSucceeded(const banking::BankName& bn);
	void bankSaveFailed(
		const foundation::Optional<banking::BankName>& original,
		const ::QString& errorMessage,
		const foundation::Optional<banking::BankName>& displayed
	);
	void bankAccountSaveSucceeded(const accounting::AccountCode& ac);
	void bankAccountSaveFailed(
		const foundation::Optional<accounting::AccountCode>& original,
		const ::QString& errorMessage,
		const foundation::Optional<accounting::AccountCode>& displayed
	);
	void showPostBudgetItems(
		const qint32& dueWithin,
		const std::vector<budgeting::BudgetItemDue>& items,
		const std::set<banking::BankName>& banks,
		const pecunia::currency::Iso4217Codes& preferredCurrency,
		const std::set<pecunia::currency::Iso4217Codes>& usableCurrencies
	);
	void lookedUpBankAccounts(
		const banking::BankName& bn,
		const std::set<banking::BankAccountType>& accountTypes
	);
	void lookedUpBudgetItemSources(
		const budgeting::BudgetItemIdentifier& requestedBy,
		const budgeting::BudgetItemTypes& bit,
		const std::set<drn::budgeting::BudgetSource>& sources
	);
	void postBudgetItemSaveSucceeded(
		const budgeting::BudgetItemIdentifier& bii,
		const foundation::Optional<accounting::TransactionNumber>& txnNum
	);
	void postBudgetItemSaveFailed(
		const budgeting::BudgetItemIdentifier& bii,
		const ::QString& errorMessage
	);
	void showConfirmNewBudgetFile();
	void clearedBudgetFile();
	void showConfirmLoadBudgetFile(const ::QString& filePathName);
	void loadedBudgetFile(const ::QString& filePathName, const bool isReloaded);
	void showSaveAsBudgetFile();
	void savedBudgetFile(const ::QString& filePathName);
	void showNoBudgetFileOpened();
	void checkedIsBudgetFileDirty(const foundation::Switch& isDirty);
	void showDashboard(
		const budgeting::BudgetTotals& bt,
		const std::vector<budgeting::BudgetItemDue>& due,
		const adviser::BudgetHealth& bh,
		const foundation::Days& dueWithin,
		const pecunia::currency::Iso4217Codes& preferred
	);
	void showExchangeRates(
		const conversion::ConversionMap& currentConversions,
		const std::set<pecunia::currency::Iso4217Codes>& currencies
	);
	void fetchedExchangeRates(
		const std::set<pecunia::currency::Iso4217Codes>& currencies,
		const conversion::ConversionMap& conversions
	);
	void showConfigure(
		const std::chrono::hours& hrInWeek,
		const foundation::Days& dayInWeek,
		const foundation::Days& notifyDueWithin,
		const pecunia::currency::Iso4217Codes& preferred,
		const std::set<pecunia::currency::Iso4217Codes>& usable,
		const std::set<pecunia::currency::Iso4217Codes>& used
	);
	void configureSaveSucceeded();
	void configureSaveFailed(const ::QString& errorMessage);

public:
	Navigator();
	~Navigator() override;
};

}}

#endif
