/*******************************************************************************
***  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_ACCOUNTCODE_H_
#define DRN_ACCOUNTING_ACCOUNTCODE_H_

#include <QtGlobal>
#include <QString>

#include <iosfwd>

#include "accounting_export.h"
#include "AccountNumber.h"
#include <foundation/Optional.hpp>


namespace drn
{
namespace accounting
{

/**
 * @brief The representation of a unique identifier and its associated name for an account. The only
 * identifying item is the number, the name is a convenience for the user to associate the number
 * with a name.
 *
 *  TODO: Should split the parent out so that usages that contain the parent are always valid.
 */
class DRN_ACCOUNTING_EXPORT AccountCode
{
	AccountNumber number_;
	::QString name_;
	foundation::Optional<AccountNumber> parent_;

public:
	AccountCode() = default;
	/**
	 * @brief Partially initialises the object with supplied account code name, and defaults the
	 * rest.
	 *
	 * @exception AccountError When the supplied name is effectively empty.
	 * @exception AccountError When the supplied name contains the account separator.
	 * @exception AccountError When the parent is invalid.
	 *
	 * @param name The unique name for the account, e.g. Cash.
	 * @param parent (Optional) The parent account number for the account.
	 */
	explicit AccountCode(::QString name, foundation::Optional<AccountNumber> parent = {});
	/**
	 * @brief Partially initialises the object with supplied values, and defaults the rest.
	 *
	 * @exception AccountError When the number is not set.
	 * @exception AccountError When the parent and the value are the same.
	 * @exception AccountError When the parent is invalid.
	 *
	 * @param number The unique number for the account, e.g. 1000.
	 * @param parent (Optional) The parent account number for the account.
	 */
	explicit AccountCode(AccountNumber number, foundation::Optional<AccountNumber> parent = {});
	/**
	 * @brief Fully initialises the object.
	 *
	 * @exception AccountError When the supplied number is zero.
	 * @exception AccountError When the supplied name is effectively empty.
	 * @exception AccountError When the supplied name contains the account separator.
	 *
	 * @param number The unique number for the account, e.g. 1000.
	 * @param name The unique name for the account, e.g. Cash.
	 * @param parent (Optional) The parent account number for the account.
	 */
	AccountCode(
		AccountNumber number,
		::QString name,
		foundation::Optional<AccountNumber> parent = {}
	);
	/**
	 * @brief Less-than operator compares the unique identifier number.
	 *
	 * @param other The other object whose number should be greater than the current object.
	 *
	 * @return When the current object's number is less-than the supplied object it'll give true,
	 * else false.
	 */
	bool operator<(const AccountCode& other) const;
	/**
	 * @brief Equality operator compares the unique identifier number.
	 *
	 * @param other The other object whose unique identifier number should be equal.
	 *
	 * @return When the unique identifier numbers are the same it'll give true, else false.
	 */
	bool operator==(const AccountCode& other) const;
	/**
	 * @brief Inequality operator compares the unique identifier number.
	 *
	 * @param other The other object whose unique identifier number should not be equal.
	 *
	 * @return When the unique identifier numbers are not the same it'll give true, else false.
	 */
	bool operator!=(const AccountCode& other) const;
	/**
	 * @brief The numeric identifier for the account.
	 */
	const AccountNumber& number() const;
	/**
	 * @brief Changes the number for the account.
	 *
	 * @exception Error When the parent and the number are the same.
	 *
	 * @param number The new number for the account.
	 */
	void setNumber(AccountNumber number);
	/**
	 * @brief The human friendly name for the account.
	 */
	const ::QString& name() const;
	/**
	 * @brief Changes the human friendly name for the account.
	 *
	 * @exception AccountError When the supplied identifier is effectively empty.
	 *
	 * @param name The new name for the account.
	 */
	void setName(::QString name);
	/**
	 * @brief The number of the parent account for this account.
	 */
	const foundation::Optional<AccountNumber>& parent() const;
	/**
	 * @brief Changes the parent for the account.
	 *
	 * @exception Error When the parent is invalid.
	 * @exception Error When the parent and the current number are the same.
	 *
	 * @param parent The new parent for the account.
	 */
	void setParent(foundation::Optional<AccountNumber> parent);

	/// The character separator to place between the number and name when outputting the account
	/// code, currently an em-dash.
	static const ::QString separator_;
};

DRN_ACCOUNTING_EXPORT bool operator==(const AccountCode& lhs, const AccountNumber& rhs);
DRN_ACCOUNTING_EXPORT bool operator!=(const AccountCode& lhs, const AccountNumber& rhs);
DRN_ACCOUNTING_EXPORT bool operator==(const AccountNumber& lhs, const AccountCode& rhs);
DRN_ACCOUNTING_EXPORT bool operator!=(const AccountNumber& lhs, const AccountCode& rhs);
DRN_ACCOUNTING_EXPORT std::ostream& operator<<(std::ostream& o, const AccountCode& ac);
/**
 * @brief Converts an account code into a human readable form.
 *
 * @param ac The account code to convert.
 *
 * @return The account code in the form: NumberSeparatorName.
 */
DRN_ACCOUNTING_EXPORT ::QString presentationText(const AccountCode& ac);
/**
 * @brief Converts a string representation of the account code into the strong type.
 *
 * @throw Error When the supplied string form of the account code is not convertible.
 * @throw AccountError When the supplied string is an invalid account code.
 *
 * @param accountCode The account code to convert.
 *
 * @return The strong form of the account code.
 */
DRN_ACCOUNTING_EXPORT AccountCode toAccountCode(const ::QString& accountCode);
/**
 * @brief Converts a string representation of the account code and parent into the strong type.
 *
 * @throw Error When the supplied string form of the account code is not convertible.
 * @throw AccountError When the supplied string is an invalid account code.
 *
 * @param accountCode The account code to convert.
 * @param parentNumber (Optional) The number for the parent account.
 *
 * @return The strong form of the account code.
 */
DRN_ACCOUNTING_EXPORT AccountCode toFullAccountCode(
	const ::QString& accountCode,
	const ::QString& parentNumber
);

}}

Q_DECLARE_METATYPE(drn::accounting::AccountCode)

#endif
