/*******************************************************************************
***  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_ACCOUNTING_ACCOUNT_H_
#define DRN_ACCOUNTING_ACCOUNT_H_

#include <pecunia/Money.h>

#include <iosfwd>

#include "AccountCode.h"
#include "AccountTypes.h"
#include "accounting_export.h"


class QString;

namespace pecunia
{
namespace currency
{

enum class Iso4217Codes : quint16;

}}

namespace drn
{
namespace accounting
{
namespace internal
{

class AccountTransactionKey;

void roundBalance(pecunia::currency::Money& balance);

}

/**
 * @brief An entry location in the general ledger for similar kinds of transaction identified by a
 * code.
 */
class DRN_ACCOUNTING_EXPORT Account
{
	AccountTypes type_;
	AccountCode code_;
	pecunia::currency::Money balance_;
	bool isClosed_;

public:
	/**
	 * @brief Default constructs everything to their empty state.
	 */
	Account();
	/**
	 * @brief Simple constructor to establish the type of the account.
	 *
	 * @exception AccountError When the supplied currency code is unknown.
	 * @exception AccountError When the supplied account type is unknown.
	 *
	 * @param type The type the account represents.
	 * @param code A unique identifier for the account.
	 * @param currency The ISO currency code the account is using.
	 */
	Account(
		const AccountTypes type,
		AccountCode code,
		const pecunia::currency::Iso4217Codes& currency
	);
	/**
	 * @brief Full initialisation constructor.
	 *
	 * @exception AccountError When the supplied currency code is unknown.
	 * @exception AccountError When the supplied account type is unknown.
	 * @exception AccountError When the supplied account code is empty.
	 *
	 * @param type The type the account represents.
	 * @param code A unique identifier for the account.
	 * @param balance The running balance of the account.
	 * @param isClosed A flag that, when true, indicates that the account is closed.
	 */
	Account(
		const AccountTypes type,
		AccountCode code,
		const pecunia::currency::Money& balance,
		const bool isClosed = false
	);
	/**
	 * @brief Equality operator that compares all members.
	 *
	 * @param other The other object whose members should all be equal.
	 *
	 * @return When all member variables are the same it'll give true, else false.
	 */
	bool operator==(const Account& other) const;
	/*
	 * @brief Inequality operator that compares all members.
	 *
	 * @param other The other object whose members may not be equal.
	 *
	 * @return When any member variable is not the same it'll give true, else false.
	 */
	bool operator!=(const Account& other) const;
	/**
	 * @brief Less than operator that compares the unique identifier for the account to that of the
	 * supplied object.
	 *
	 * @param other The other object whose members should be compared.
	 *
	 * @return True when the members are less-than the supplied.
	 */
	bool operator<(const Account& other) const;
	/**
	 * @brief An accessor to the type the account represents.
	 */
	const AccountTypes& type() const;
	/**
	 * @brief The unique identifier for the account.
	 */
	const AccountCode& code() const;
	/**
	 * @brief Changes the unique identifier for the account.
	 *
	 * @exception AccountError When the account is closed.
	 *
	 * @param code The new unique identifier for the account.
	 */
	void setCode(AccountCode code);
	/**
	 * @brief The current running balance of the account.
	 */
	const pecunia::currency::Money& balance() const;
	/**
	 * @brief A flag that, when true, indicates the account is closed.
	 */
	const bool& isClosed() const;
	/**
	 * @brief Converts the running balance currency into another currency.
	 *
	 * @exception AccountError When the supplied currency code is unknown.
	 * @exception AccountError When the account is closed.
	 *
	 * @param currency The new currency to track the running balance in.
	 */
	void convertTo(const pecunia::currency::Iso4217Codes& currency);
	/**
	 * @brief Increases the account's running balance by the supplied amount. When the supplied
	 * increasing amount is not in the account currency, the balance is rounded towards even.
	 *
	 * @exception AccountError When the supplied amount is less-than zero.
	 * @exception AccountError When the account is closed.
	 *
	 * @param amount The amount to increase the balance by.
	 * @param allowed The object that allows access to modifying the balance.
	 */
	void increase(
		const pecunia::currency::Money& amount,
		const internal::AccountTransactionKey& allowed
	);
	/**
	 * @brief Decreases the account's running balance by the supplied amount. When the supplied
	 * decreasing amount is not in the account currency, the balance is rounded towards even.
	 *
	 * @exception AccountError When the supplied amount is less-than zero.
	 * @exception AccountError When the account is closed.
	 *
	 * @param amount The amount to decrease the balance by.
	 * @param allowed The object that allows access to modifying the balance.
	 */
	void decrease(
		const pecunia::currency::Money& amount,
		const internal::AccountTransactionKey& allowed
	);
	/**
	 * @brief Marks the account as closed.
	 */
	void close();
	/**
	 * @brief Marks the account as opened.
	 */
	void open();
	/**
	 * @brief Changes the parent of the account.
	 *
	 * @exception AccountError When the account is closed.
	 *
	 * @param parent The parent to change to.
	 */
	void setParent(foundation::Optional<AccountNumber> parent);
};

DRN_ACCOUNTING_EXPORT std::ostream& operator<<(std::ostream& o, const Account& a);

}}

#endif
