/*******************************************************************************
***  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 "Debt.h"
using ::QDate;
using pecunia::currency::Money;
using drn::budgeting::BudgetedMoney;
using drn::budgeting::BudgetSource;
using drn::budgeting::Debt;
using drn::budgeting::Percentage;
using drn::budgeting::EventFrequency;

#include <pecunia/Codes.h>
using pecunia::currency::Iso4217Codes;
#include <QObject>
using ::QObject;

#include <cmath>
using std::fabs;
using std::pow;
#include <ostream>
using std::fixed;
using std::ostream;
using std::setprecision;

#include "BudgetingErrors.h"
using drn::budgeting::BudgetItemError;


//{ Percentage

// N.B. The number of decimal places is chosen because it seems to be good enough for the number of
// digits most percentage rates are advertised in.
const int32_t Percentage::precision_{6u};

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

bool Percentage::operator<(const double& rate) const
{
	return *this < Percentage{rate};
}

bool Percentage::operator==(const Percentage& other) const
{
	static const double epsilon{1 / pow(10, precision_)};
	return fabs(this->rate_ - other.rate_) < epsilon;
}

//}

//{ Debt

Debt::Debt() :
	BudgetedMoney{},
	borrowed_{},
	interest_{}
{}

Debt::Debt(const BudgetSource& source) :
	BudgetedMoney{source},
	borrowed_{},
	interest_{}
{}

Debt::Debt(
	const BudgetSource& source,
	const Money& minimum,
	const EventFrequency& period,
	const QDate& nextOccurOn,
	const Money& borrowed,
	const Percentage& interest
) :
	BudgetedMoney{source, minimum, period, nextOccurOn},
	borrowed_{borrowed},
	interest_{interest}
{
	if (this->amount() < 0)
		throw BudgetItemError{this->source(), QObject::tr("The minimum cannot be less-than zero.")};

	if (this->amount().code() == Iso4217Codes::XXX)
		throw BudgetItemError{
			this->source(),
			QObject::tr("The minimum must be in some issued currency.")
		};

	if (this->interest_ < 0.0)
		throw BudgetItemError{
			this->source(),
			QObject::tr("The interest rate cannot be less-than zero.")
		};

	if (this->borrowed_.code() != this->amount().code())
		throw BudgetItemError{
			this->source(),
			QObject::tr(
				"The total amount borrowed, %1, must be in the same issued currency as the "
					"minimum payment, %2."
			)
				.arg(QString::fromStdString(to_string(this->borrowed_)))
				.arg(QString::fromStdString(to_string(this->amount())))
		};

	if (this->borrowed_ == 0)
		throw BudgetItemError{
			this->source(),
			QObject::tr("The total amount borrowed must be greater than zero.")
		};

	if (this->borrowed_ < this->amount())
		throw BudgetItemError{
			this->source(),
			QObject::tr(
				"The total amount borrowed, %1, must be greater than the minimum payment, %2."
			)
				.arg(QString::fromStdString(to_string(this->borrowed_)))
				.arg(QString::fromStdString(to_string(this->amount())))
		};
}

bool Debt::operator==(const Debt& other) const
{
	return BudgetedMoney::operator==(other)
		&& this->borrowed_ == other.borrowed_
		&& this->interest_ == other.interest_;
}

const Money& Debt::borrowed() const
{
	return this->borrowed_;
}

const Percentage& Debt::interest() const
{
	return this->interest_;
}

//}

ostream& drn::budgeting::operator<<(ostream& o, const Percentage& p)
{
	return o << "Rate (" << fixed << setprecision(Percentage::precision_) << p.rate_ << "%)";
}

ostream& drn::budgeting::operator<<(ostream& o, const Debt& d)
{
	return o << dynamic_cast<const BudgetedMoney&>(d) << ", Borrowed Amount(" << d.borrowed()
			<< ", Interest (" << d.interest() << ')';
}
