/*******************************************************************************
***  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_TRANSACTIONPOSTER_H_
#define DRN_ACCOUNTING_TRANSACTIONPOSTER_H_

#include <QtGlobal>

#include <ostream>
#include <utility>


namespace drn
{
namespace accounting
{

class Account;
class AccountCode;
enum class AccountTypes : quint8;
class Transaction;
class TransactionNumber;

namespace internal
{

/**
 * @brief Implements the logic for posting a transaction in compliance with the double entry
 * bookkeeping accounting method.
 */
struct TransactionPoster
{
	/**
	 * @brief Performs the supplied transaction upon the accounts supplied and located within the
	 * transaction. The transaction is executed according to the following rule table:
	 *    Type    |    Debit    |    Credit
	 *---------------------------------------
	 * Asset      |   Increase  |  Decrease
	 * Liability  |   Decrease  |  Increase
	 * Revenue    |   Decrease  |  Increase
	 * Expense    |   Increase  |  Decrease
	 * Capital    |   Decrease  |  Increase
	 *
	 * @exception TransactionError When the supplied debit account is not the same as the debit
	 * account in the transaction.
	 * @exception TransactionError When the supplied credit account is not the same as the credit
	 * account in the transaction.
	 * @exception TransactionError When the transaction debit account type is not known.
	 * @exception TransactionError When the transaction credit account type is not known.
	 *
	 * @param debit The account that is to be debited.
	 * @param credit The account that is to be credited.
	 * @param transaction The transaction to execute.
	 * @param number The unique number in the accounting system that identifies the transaction.
	 *
	 * @post The supplied debit and credit accounts will have their running balance adjusted in
	 * accordance to the transaction and accounting rules.
	 *
	 * @return The successfully processed transactions. The first being the transaction for the
	 * debit account and the second being the credit transaction.
	 */
	static std::pair<Transaction, Transaction> perform(
		Account& debit,
		Account& credit,
		const Transaction& transaction,
		const TransactionNumber& number
	);
	/**
	 * @brief Performs a reverse transaction upon the accounts supplied and located within the
	 * transaction. The transaction is executed according to the reverse of the rules stated
	 * in @ref TransactionPoster::perform .
	 *
	 * @exception TransactionError When the supplied debit account is not the same as the debit
	 * account in the transaction.
	 * @exception TransactionError When the supplied credit account is not the same as the credit
	 * account in the transaction.
	 * @exception TransactionError When the transaction debit account type is not known.
	 * @exception TransactionError When the transaction credit account type is not known.
	 *
	 * @param debit The account that was debited in the original transaction.
	 * @param credit The account that was credited in the original transaction.
	 * @param transaction The original transaction that is to be reversed.
	 */
	static void reverse(Account& debit, Account& credit, const Transaction& transaction);
	/**
	 * @brief Changes the account codes involved in a transaction. This does not modify any account
	 * balances as this is just a recoding of an account involved in a transaction.
	 *
	 * @exception TransactionError When the current account code is not a part of the transaction.
	 *
	 * @param toRecode The current account code that is to be recoded.
	 * @param transaction The transaction containing the account code to be recoded.
	 * @param recodeAs The new account code to recode the transaction under.
	 */
	static void recodeAccount(
		const AccountCode& toRecode,
		Transaction& transaction,
		const AccountCode& recodeAs
	);
};
/**
 * @brief The possible entry types an account may have in a transaction.
 */
enum class EntryTypes : quint8
{
	/// The account entry location is not known yet.
	Unknown,
	/// The account entry appears on the debit side of the T-account.
	Debit,
	/// The account entry appears on the credit side of the T-account.
	Credit,
};

std::ostream& operator<<(std::ostream& o, const EntryTypes& et);
/**
 * @brief Determines the side of the T-account based upon the account type and if it's increasing.
 *
 * @param type The type of account to consider.
 * @param isIncreasing A flag that, when true, indicates the account is increasing.
 *
 * @return The side of the T-account the account must be on for the balance to be increasing or
 *  decreasing.
 */
EntryTypes selectTAccount(const AccountTypes& type, const bool isIncreasing);

}}}

#endif
