/*******************************************************************************
***  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 "BudgetItemDue.h"
using ::QDate;
using std::vector;
using drn::budgeting::BudgetItemDue;
using drn::budgeting::BudgetItemIdentifier;
using drn::foundation::Days;

#include <pecunia/Algorithm.hpp>
using pecunia::sort;

#include <algorithm>
using std::sort;
#include <ostream>
using std::ostream;
#include <tuple>
using std::tie;

#include "Budget.h"
using drn::budgeting::Budget;
#include "BudgetItemIdentifier.h"
using drn::budgeting::BudgetItemIdentifier;
#include "BudgetItemTypes.h"
using drn::budgeting::BudgetItemTypes;
#include "BudgetedMoney.h"
using drn::budgeting::BudgetedMoney;
#include "BudgetedMoneyMap.hpp"
using drn::budgeting::BudgetedMoneyMap;


namespace
{

template<typename ItemType>
void addDue(
	const BudgetItemTypes type,
	const BudgetedMoneyMap<ItemType>& monies,
	const Days& within,
	vector<BudgetItemDue>& items
)
{
	const auto today{QDate::currentDate()};

	for (const auto& sourceMoney : monies)
	{
		const BudgetedMoney& item{sourceMoney.second};
		const auto daysTo{today.daysTo(item.nextOccurOn())};

		if (daysTo <= within.count())
			items.emplace_back(
				BudgetItemDue{
					BudgetItemIdentifier{type, item.source()},
					item.amount(),
					item.nextOccurOn()
				}
			);
	}
}

}

bool drn::budgeting::operator<(const BudgetItemDue& lhs, const BudgetItemDue& rhs)
{
	return tie(lhs.dueOn_, lhs.id_, lhs.amount_)
			< tie(rhs.dueOn_, rhs.id_, rhs.amount_);
}

bool drn::budgeting::operator>(const BudgetItemDue& lhs, const BudgetItemDue& rhs)
{
	return tie(lhs.dueOn_, lhs.id_, lhs.amount_)
			> tie(rhs.dueOn_, rhs.id_, rhs.amount_);
}

bool drn::budgeting::operator==(const BudgetItemDue& lhs, const BudgetItemDue& rhs)
{
	return tie(lhs.dueOn_, lhs.id_, lhs.amount_)
			== tie(rhs.dueOn_, rhs.id_, rhs.amount_);
}

bool drn::budgeting::operator!=(const BudgetItemDue& lhs, const BudgetItemDue& rhs)
{
	return ! (lhs == rhs);
}

vector<BudgetItemDue> drn::budgeting::budgetItemsDue(const Days& within, const Budget& b)
{
	vector<BudgetItemDue> items{};
	addDue(BudgetItemTypes::Bill, b.bills_, within, items);
	addDue(BudgetItemTypes::Debt, b.debts_, within, items);
	addDue(BudgetItemTypes::Goal, b.goals_, within, items);
	addDue(BudgetItemTypes::Nontrack, b.nontracks_, within, items);
	addDue(BudgetItemTypes::Wage, b.wages_, within, items);
	sort(items, &swap);
	return items;
}

ostream& drn::budgeting::operator<<(ostream& o, const BudgetItemDue& bid)
{
	return o << "Budget Id (" << bid.id_ << "), Amount (" << bid.amount_
			<< "), Due On (" << bid.dueOn_.toString().toStdString() << ')';
}

ostream& drn::budgeting::operator<<(ostream& o, const vector<BudgetItemDue>& bids)
{
	bool isFirst{true};
	o << "Budget Items Due (";

	for (const auto& bid : bids)
		o << (isFirst ? isFirst = false, "(" : ", (") << bid << ')';
	return o << ')';
}

void drn::budgeting::swap(BudgetItemDue& lhs, BudgetItemDue& rhs)
{
	BudgetItemDue temp{lhs};
	lhs.id_ = rhs.id_;
	pecunia::currency::swap(lhs.amount_, rhs.amount_);
	lhs.dueOn_ = rhs.dueOn_;
	rhs.id_ = temp.id_;
	pecunia::currency::swap(rhs.amount_, temp.amount_);
	rhs.dueOn_ = temp.dueOn_;
}
