/*******************************************************************************
***  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_DESKTOP_UI_INTERNAL_BANKSWIDGET_H_
#define DRN_DESKTOP_UI_INTERNAL_BANKSWIDGET_H_

#include <QWidget>

#include <functional>
#include <set>

#include <accounting/AccountCode.h>
#include <foundation/ObserverPtr.hpp>
#include <foundation/Optional.hpp>
#include <ui_BanksWidget.h>
#include "WidgetObserverMap.hpp"


class QAbstractButton;
class QTreeWidgetItem;
class QModelIndex;
class QPushButton;

namespace pecunia
{
namespace currency
{

enum class Iso4217Codes : quint16;
class Money;

}}

namespace drn
{
namespace accounting
{

class Account;

}
namespace banking
{

class Bank;
struct BankAccountType;
enum class SupportedAccountTypes;
struct BankMap;
class BankName;

}
namespace desktop_ui
{
namespace internal
{

class AccountEntryWidget;
class BankEntryWidget;

/**
 * @brief Displays an interface for creating, editing, and deleting banks.
 */
class BanksWidget : public QWidget, Ui::BanksWdgt
{
	Q_OBJECT

	enum class SelectedItemTypes
	{
		Unknown,
		Bank,
		BankAccount,
		Account
	};

	foundation::Optional<foundation::ObserverPtr<QTreeWidgetItem>> findTreeItem(
		const std::function<bool(const ::QString&)>& isItem
	);
	void add(const banking::Bank& b);
	void add(
		banking::BankName bn,
		accounting::Account a,
		const banking::SupportedAccountTypes& sat
	);
	void add(const accounting::AccountCode& parent, accounting::Account a);
	banking::BankName currentSelectedBankName() const;
	bool isAnyItemDirty() const;
	void saveAccounts();
	void saveBanks();
	void displayError(
		const foundation::ObserverPtr<QTreeWidgetItem>& item,
		const ::QString& errorMessage
	);
	void remove(const foundation::ObserverPtr<QTreeWidgetItem>& item);
	bool areAllAccountsSaved() const;
	std::set<accounting::AccountNumber> bankAccountNumbers(
		const foundation::ObserverPtr<QTreeWidgetItem>& item
	) const;

	std::set<accounting::AccountNumber> accountNumbers_;
	const std::set<pecunia::currency::Iso4217Codes> usableCurrencies_;
	foundation::ObserverPtr<QPushButton> addBankPshbttn_;
	foundation::ObserverPtr<QPushButton> addAccountPshbttn_;
	SelectedItemTypes selectedType_;
	WidgetObserverMap<QTreeWidgetItem, BankEntryWidget> banks_;
	WidgetObserverMap<QTreeWidgetItem, AccountEntryWidget> accounts_;

private slots:
	void onClicked(const foundation::ObserverPtr<QAbstractButton>& button);
	void onCurrentItemChanged(
		foundation::ObserverPtr<QTreeWidgetItem> current,
		foundation::ObserverPtr<QTreeWidgetItem> previous
	);

public slots:
	void onSaveSucceeded(const banking::BankName& bn);
	void onSaveFailed(
		const foundation::Optional<banking::BankName>& original,
		const ::QString& errorMessage,
		const foundation::Optional<banking::BankName>& displayed
	);
	/**
	 * @brief Will close the widget and emit the closing signal.
	 *
	 * @return When the widget is closed gives true, false otherwise.
	 */
	bool close();
	void onAccountSaveSucceeded(accounting::AccountCode ac);
	void onAccountSaveFailed(
		const foundation::Optional<accounting::AccountCode>& original,
		const ::QString& errorMessage,
		const foundation::Optional<accounting::AccountCode>& displayed
	);

signals:
	/// @brief Triggered when the widget is in the process of being closed.
	void closing();
	/**
	 * @brief Triggered when a bank should be renamed.
	 *
	 * @param original The original name of the bank.
	 * @param change The new name of the bank.
	 */
	void rename(
		const banking::BankName& original,
		const banking::BankName& change
	);
	/**
	 * @brief Triggered when a bank is to be closed.
	 *
	 * @param name The name of the bank.
	 */
	void closed(const banking::BankName& bn);
	/**
	 * @brief Triggered when a bank is to be opened.
	 *
	 * @param name The name of the bank.
	 */
	void opened(const banking::BankName& bn);
	/**
	 * @brief Triggered when a bank is to be removed.
	 *
	 * @param name The name of the bank.
	 */
	void removed(const banking::BankName& bn);
	/**
	 * @brief Triggered when a bank is to be added.
	 *
	 * @param b The basic bank information, minus account codes.
	 * @param accountNumbersBankTypes The general ledger account numbers and the bank account type
	 * associated with the supplied bank.
	 */
	void added(
		const banking::Bank& b,
		const std::map<
			accounting::AccountNumber,
			banking::SupportedAccountTypes
		>& accountNumbersBankTypes
	);
	/**
	 * @brief Triggered when a bank is modified.
	 *
	 * @param b The modified bank data.
	 * @param accountNumbersBankTypes The associated accounts.
	 */
	void updated(
		const banking::Bank& b,
		const std::map<
			accounting::AccountNumber,
			banking::SupportedAccountTypes
		>& accountNumbersBankTypes
	);
	/**
	 * @brief Triggered when an account is to be added to the general ledger.
	 *
	 * @param name The name of the general ledger account.
	 * @param type The bank account type that will be associated with the general ledger account.
	 * @param openingBalance The opening balance to use in the general ledger account.
	 */
	void addedAccount(
		const ::QString& name,
		const banking::SupportedAccountTypes& type,
		const pecunia::currency::Money& openingBalance
	);
	/**
	 * @brief Triggered when an account is renamed.
	 *
	 * @param original The original name of the account.
	 * @param changed The new name of the account.
	 */
	void renamedAccount(
		const accounting::AccountCode& original,
		const accounting::AccountCode& changed
	);
	/**
	 * @brief Triggered when an account should be removed.
	 *
	 * @param ac The account that is to be removed.
	 */
	void removedAccount(const accounting::AccountCode& ac);

public:
	/**
	 * @brief Constructor for initialising the displayed banks.
	 *
	 * @exception Error When the bank add button fails to be created.
	 *
	 * @param usableCurrencies The currencies selected to be available for account creation.
	 * @param parent The parent responsible for the for the lifetime of this object.
	 */
	BanksWidget(
		const banking::BankMap& banks,
		const std::set<accounting::Account>& allAccounts,
		const std::set<banking::BankAccountType>& accountTypes,
		std::set<pecunia::currency::Iso4217Codes> usableCurrencies,
		const foundation::ObserverPtr<::QWidget> parent
	);
};

}}}

#endif

