/*******************************************************************************
***  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 "AccountNumberGenerator.h"
using std::make_unique;
using std::set;
using std::unique_ptr;
using drn::accounting::AccountNumberGenerator;
using drn::accounting::AccountNumber;
using drn::accounting::SequentialAccountNumberGenerator;
using drn::accounting::presentationText;

#include <QObject>
using ::QObject;

#include <limits>
using std::numeric_limits;
#include <utility>
using std::move;

#include <accounting/Account.h>
using drn::accounting::Account;
using drn::accounting::AccountTypes;
#include <foundation/Error.h>
using drn::foundation::Error;


//{ AccountNumberGenerator

AccountNumberGenerator::AccountNumberGenerator(AccountNumber number) :
	number_{move(number)}
{}

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

const AccountNumber& AccountNumberGenerator::operator++()
{
	return this->number_ = this->increment();
}

AccountNumber AccountNumberGenerator::operator++(int)
{
	const auto prior{this->number_};
	this->number_ = this->increment();
	return prior;
}

const AccountNumber& AccountNumberGenerator::operator--()
{
	return this->number_ = this->decrement();
}

AccountNumber AccountNumberGenerator::operator--(int)
{
	const auto current{this->number_};
	this->number_ = this->decrement();
	return current;
}

const AccountNumber& AccountNumberGenerator::operator*() const
{
	return this->number();
}

AccountNumberGenerator& AccountNumberGenerator::operator=(const AccountNumber& number)
{
	this->number_ = number;
	return *this;
}

void AccountNumberGenerator::update(AccountNumber an)
{
	if (this->isLarger(an))
		this->number_ = move(an);
}

//}

//{ SequentialAccountNumberGenerator

AccountNumber SequentialAccountNumberGenerator::increment() const
{
	auto number{this->number()};

	if (number.integer() == numeric_limits<AccountNumber::ValueType>::max())
		throw Error{
			QObject::tr(
				"Cannot generate the next account number from, %1."
			).arg(presentationText(number))
		};
	return AccountNumber{number.integer() + 1};
}

AccountNumber SequentialAccountNumberGenerator::decrement() const
{
	auto number{this->number()};
	const auto min{numeric_limits<AccountNumber::ValueType>::min()};

	if (number.integer() == min)
		throw Error{
			QObject::tr(
				"Cannot generate the previous account number from, %1."
			).arg(presentationText(number))
		};
	const auto nextNumber{number.integer() - 1};
	return nextNumber == min ? AccountNumber{} : AccountNumber{nextNumber};
}

bool SequentialAccountNumberGenerator::isLarger(const AccountNumber& an) const
{
	return an > this->number();
}

AccountNumber SequentialAccountNumberGenerator::current(const set<Account>& accounts) const
{
	if (accounts.empty())
		return {};
	return accounts.rbegin()->code().number();
}

unique_ptr<AccountNumberGenerator> SequentialAccountNumberGenerator::clone() const
{
	return make_unique<SequentialAccountNumberGenerator>(*this);
}

//}
