/*******************************************************************************
***  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/>.    ***
*******************************************************************************/
#include "AccountCode.h"
using ::QString;
using ::quint32;
using ::quint8;
using drn::accounting::AccountNumber;
using drn::accounting::AccountCode;
using drn::foundation::Optional;

#include <QObject>
using ::QObject;

#include <ostream>
using std::ostream;
#include <utility>
using std::move;

#include "AccountingErrors.h"
using drn::accounting::AccountError;
#include <foundation/Error.h>
using drn::foundation::Error;


AccountCode::AccountCode(QString name, Optional<AccountNumber> parent) :
	number_{},
	name_{move(name)},
	parent_{move(parent)}
{
	if (this->name_.trimmed().isEmpty())
		throw AccountError{*this, QObject::tr("The account code name cannot be empty.")};

	if (this->name_.contains(AccountCode::separator_))
		throw AccountError{
			*this,
			QObject::tr("The account code name cannot contain the number and name separator.")
		};

	if (this->number_ == this->parent_)
		throw AccountError{
			*this,
			QObject::tr("The account number cannot be the same as the parent.")
		};
}

AccountCode::AccountCode(AccountNumber number, Optional<AccountNumber> parent) :
	number_{move(number)},
	name_{},
	parent_{move(parent)}
{
	if (this->number_ == this->parent_)
		throw AccountError{
			*this,
			QObject::tr("The account number cannot be the same as the parent.")
		};
}

AccountCode::AccountCode(AccountNumber number, QString name, Optional<AccountNumber> parent) :
	number_{move(number)},
	name_{move(name)},
	parent_{move(parent)}
{
	if (this->name_.trimmed().isEmpty())
		throw AccountError{*this, QObject::tr("The account code name cannot be empty.")};

	if (this->name_.contains(AccountCode::separator_))
		throw AccountError{
			*this,
			QObject::tr("The account code name cannot contain the number and name separator.")
		};

	if (this->number_ == this->parent_)
		throw AccountError{
			*this,
			QObject::tr("The account number cannot be the same as the parent.")
		};
}

bool AccountCode::operator<(const AccountCode& other) const
{
	return this->number_ < other.number_;
}

bool AccountCode::operator==(const AccountCode& other) const
{
	return this->number_ == other.number_;
}

bool AccountCode::operator!=(const AccountCode& other) const
{
	return ! (*this == other);
}

const AccountNumber& AccountCode::number() const
{
	return this->number_;
}

void AccountCode::setNumber(AccountNumber number)
{
	if (number == this->parent_)
		throw AccountError{
			*this,
			QObject::tr("The account number cannot be set to be the same as the parent.")
		};
	this->number_ = move(number);
}

const QString& AccountCode::name() const
{
	return this->name_;
}

void AccountCode::setName(QString name)
{
	if (name.trimmed().isEmpty())
		throw AccountError{*this, QObject::tr("The new account code name cannot be empty.")};
	this->name_ = move(name);
}

const Optional<AccountNumber>& AccountCode::parent() const
{
	return this->parent_;
}

void AccountCode::setParent(Optional<AccountNumber> parent)
{
	if (AccountNumber::invalid_ == this->number_)
		throw AccountError{
			*this,
			QObject::tr("The account parent cannot be set while the number is not set.")
		};

	if (this->number_ == parent)
		throw AccountError{
			*this,
			QObject::tr("The account number cannot be set to be the same as the parent.")
		};
	this->parent_ = move(parent);
}

const QString AccountCode::separator_{"\u2014"};

bool drn::accounting::operator==(const AccountCode& lhs, const AccountNumber& rhs)
{
	return lhs.number() == rhs;
}

bool drn::accounting::operator!=(const AccountCode& lhs, const AccountNumber& rhs)
{
	return ! (lhs == rhs);
}

bool drn::accounting::operator==(const AccountNumber& lhs, const AccountCode& rhs)
{
	return rhs == lhs;
}

bool drn::accounting::operator!=(const AccountNumber& lhs, const AccountCode& rhs)
{
	return rhs != lhs;
}

ostream& drn::accounting::operator<<(ostream& o, const AccountCode& ac)
{
	return o << "Number (" << ac.number() << "), Name (" << ac.name().toStdString() << "), Parent ("
			<< ac.parent() << ')';
}

QString drn::accounting::presentationText(const AccountCode& ac)
{
	return presentationText(ac.number()).append(AccountCode::separator_).append(ac.name());
}

AccountCode drn::accounting::toAccountCode(const QString& accountCode)
{
	return toFullAccountCode(accountCode, {});
}

AccountCode drn::accounting::toFullAccountCode(
	const QString& accountCode,
	const QString& parentNumber
)
{
	const auto codeNameSplit{accountCode.split(AccountCode::separator_)};

	if (codeNameSplit.size() != 2)
		throw Error{
			accountCode.isEmpty()
				? QObject::tr("The account code cannot be empty.")
				: QObject::tr("The account code does not contain the account separator.")
		};
	bool isConverted;
	const auto number{codeNameSplit.at(0).toUInt(&isConverted)};
	const auto name{codeNameSplit.at(1)};

	if ( ! isConverted)
		throw Error{QObject::tr("The account code number is not an integer.")};

	if (number == AccountNumber::invalid_ && name.isEmpty())
		return AccountCode{};
	Optional<AccountNumber> parent;

	if ( ! parentNumber.isEmpty())
	{
		const auto parentNum{parentNumber.toUInt(&isConverted)};

		if ( ! isConverted)
			throw Error{QObject::tr("The account code parent number is not an integer.")};
		parent = AccountNumber{parentNum};
	}

	if (number == AccountNumber::invalid_)
		return AccountCode{name, move(parent)};
	return AccountCode{AccountNumber{number}, name, move(parent)};
}
