/*******************************************************************************
***  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_FILE_STORAGE_INTERNAL_BUDGETELEMENTS_H_
#define DRN_FILE_STORAGE_INTERNAL_BUDGETELEMENTS_H_

#include <pecunia/MoneyTypes.h>

#include <QDate>
#include <QString>

#include <map>
#include <set>
#include <vector>

#include <foundation/Optional.hpp>


class QXmlStreamReader;
class QXmlStreamWriter;

namespace drn
{
namespace file_storage
{
namespace internal
{

struct BasicElement
{
	virtual ~BasicElement() = default;
	virtual const QLatin1String& tag() const = 0;
	virtual void read(QXmlStreamReader& xml) = 0;
	virtual void write(QXmlStreamWriter& xml) const = 0;
};

struct IndexedElement : BasicElement
{
	using IdType = quint32;

	IndexedElement();
	IndexedElement(const IdType id);
	~IndexedElement() override = default;

	static const IdType invalidId_;
	static const QLatin1String idAttribute_;
	IdType id_;
};

struct ApplicationElement : BasicElement
{
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String revisionAttribute_;
	static const quint8 currentRevision_;
	quint8 revision_;
};

struct WageElement : IndexedElement
{
	using MajorType = pecunia::UnitStorage;
	using MinorType = pecunia::MinorUnit;

	WageElement();
	WageElement(
		const IdType id,
		const MajorType major,
		const MinorType minor,
		const QString& period,
		const QString& sourceName,
		const QString& currency,
		const QDate& nextOccurOn,
		const quint32& accountNumber
	);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String majorAttribute_;
	static const QLatin1String minorAttribute_;
	static const QLatin1String periodAttribute_;
	static const QLatin1String nameAttribute_;
	static const QLatin1String currencyAttribute_;
	static const QLatin1String nextOccurOnAttribute_;
	static const QLatin1String accountIdAttribute_;
	MajorType major_;
	MinorType minor_;
	QString period_;
	QString sourceName_;
	QString currency_;
	QDate nextOccurOn_;
	quint32 accountId_;
};

struct WagesElement : BasicElement
{
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	std::vector<WageElement> wages_;
};

struct BillElement : IndexedElement
{
	using MajorType = pecunia::UnitStorage;
	using MinorType = pecunia::MinorUnit;

	BillElement();
	BillElement(
		const IdType id,
		const MajorType major,
		const MinorType minor,
		const QString& period,
		const QString& sourceName,
		const QString& currency,
		const QDate& nextOccurOn,
		const quint32& accountNumber
	);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String majorAttribute_;
	static const QLatin1String minorAttribute_;
	static const QLatin1String periodAttribute_;
	static const QLatin1String nameAttribute_;
	static const QLatin1String currencyAttribute_;
	static const QLatin1String nextOccurOnAttribute_;
	static const QLatin1String accountIdAttribute_;
	MajorType major_;
	MinorType minor_;
	QString period_;
	QString sourceName_;
	QString currency_;
	QDate nextOccurOn_;
	quint32 accountId_;
};

struct BillsElement : BasicElement
{
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	std::vector<BillElement> bills_;
};

struct DebtElement : IndexedElement
{
	using MajorType = pecunia::UnitStorage;
	using MinorType = pecunia::MinorUnit;

	DebtElement();
	DebtElement(
		const IdType id,
		const MajorType totalMajor,
		const MinorType totalMinor,
		const QString& period,
		const QString& sourceName,
		const QString& currency,
		const MajorType minimumMajor,
		const MinorType minimumMinor,
		const qreal interest,
		const QDate& nextOccurOn,
		const quint32& accountId
	);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String totalMajorAttribute_;
	static const QLatin1String totalMinorAttribute_;
	static const QLatin1String periodAttribute_;
	static const QLatin1String nameAttribute_;
	static const QLatin1String currencyAttribute_;
	static const QLatin1String minimumMajorAttribute_;
	static const QLatin1String minimumMinorAttribute_;
	static const QLatin1String interestAttribute_;
	static const QLatin1String nextOccurOnAttribute_;
	static const QLatin1String accountIdAttribute_;
	MajorType totalMajor_;
	MinorType totalMinor_;
	QString period_;
	QString sourceName_;
	QString currency_;
	MajorType minimumMajor_;
	MinorType minimumMinor_;
	qreal interest_;
	QDate nextOccurOn_;
	quint32 accountId_;
};

struct DebtsElement : BasicElement
{
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	std::vector<DebtElement> debts_;
};

struct GoalElement : IndexedElement
{
	using MajorType = pecunia::UnitStorage;
	using MinorType = pecunia::MinorUnit;

	GoalElement();
	GoalElement(
		const IdType id,
		const MajorType major,
		const MinorType minor,
		const QString& period,
		const QString& sourceName,
		const QString& currency,
		const MajorType finalMajor,
		const MinorType finalMinor,
		const QDate& nextOccurOn,
		const quint32& accountId
	);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String majorAttribute_;
	static const QLatin1String minorAttribute_;
	static const QLatin1String periodAttribute_;
	static const QLatin1String nameAttribute_;
	static const QLatin1String currencyAttribute_;
	static const QLatin1String finalMajorAttribute_;
	static const QLatin1String finalMinorAttribute_;
	static const QLatin1String nextOccurOnAttribute_;
	static const QLatin1String accountIdAttribute_;
	MajorType major_;
	MinorType minor_;
	QString period_;
	QString sourceName_;
	QString currency_;
	MajorType finalMajor_;
	MinorType finalMinor_;
	QDate nextOccurOn_;
	quint32 accountId_;
};

struct GoalsElement : BasicElement
{
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	std::vector<GoalElement> goals_;
};

struct DiscretionaryElement : IndexedElement
{
	using MajorType = pecunia::UnitStorage;
	using MinorType = pecunia::MinorUnit;

	DiscretionaryElement();
	DiscretionaryElement(
		const IdType id,
		const MajorType major,
		const MinorType minor,
		const QString& period,
		const QString& sourceName,
		const QString& currency,
		const QDate& nextOccurOn,
		const quint32& accountNumber
	);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String majorAttribute_;
	static const QLatin1String minorAttribute_;
	static const QLatin1String periodAttribute_;
	static const QLatin1String nameAttribute_;
	static const QLatin1String currencyAttribute_;
	static const QLatin1String nextOccurOnAttribute_;
	static const QLatin1String accountIdAttribute_;
	MajorType major_;
	MinorType minor_;
	QString period_;
	QString sourceName_;
	QString currency_;
	QDate nextOccurOn_;
	quint32 accountId_;
};

struct DiscretionariesElement : BasicElement
{
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	std::vector<DiscretionaryElement> discretionaries_;
};

struct AccountElement : IndexedElement
{
	using MajorType = pecunia::UnitStorage;
	using MinorType = pecunia::MinorUnit;

	AccountElement();
	AccountElement(
		const IndexedElement::IdType& number,
		const QString& name,
		const QString& type,
		const MajorType& major,
		const MinorType& minor,
		const QString& currency,
		const bool closed,
		const foundation::Optional<quint32> parent
	);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String nameAttribute_;
	static const QLatin1String typeAttribute_;
	static const QLatin1String majorAttribute_;
	static const QLatin1String minorAttribute_;
	static const QLatin1String currencyAttribute_;
	static const QLatin1String closedAttribute_;
	static const QLatin1String parentAttribute_;
	QString name_;
	QString type_;
	MajorType major_;
	MinorType minor_;
	QString currency_;
	bool isClosed_;
	foundation::Optional<quint32> parent_;
};

struct AccountsElement : BasicElement
{
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	std::vector<AccountElement> accounts_;
};

struct TransactionElement : IndexedElement
{
	using MajorType = pecunia::UnitStorage;
	using MinorType = pecunia::MinorUnit;

	TransactionElement();
	TransactionElement(
		const IndexedElement::IdType& transactionNumber,
		const QDate& date,
		const IndexedElement::IdType& accountId,
		const QString& entry,
		const MajorType& major,
		const MinorType& minor,
		const QString& currency,
		const QString& status,
		foundation::Optional<QString> description
	);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String dateAttribute_;
	static const QLatin1String accountIdAttribute_;
	static const QLatin1String entryAttribute_;
	static const QLatin1String majorAttribute_;
	static const QLatin1String minorAttribute_;
	static const QLatin1String currencyAttribute_;
	static const QLatin1String statusAttribute_;
	QDate date_;
	IndexedElement::IdType accountId_;
	QString entry_;
	MajorType major_;
	MinorType minor_;
	QString currency_;
	QString status_;
	foundation::Optional<QString> description_;
};

struct LedgerElement : IndexedElement
{
	LedgerElement();
	explicit LedgerElement(const IndexedElement::IdType& accountId);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	std::vector<TransactionElement> transactions_;
};

struct LedgersElement : BasicElement
{
	LedgersElement();
	LedgersElement(foundation::Optional<IndexedElement::IdType> openingAccountId);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String openingAccountIdAttribute_;
	foundation::Optional<IndexedElement::IdType> openingAccountId_;
	std::vector<LedgerElement> ledgers_;
};

struct BankAccountElement : BasicElement
{
	BankAccountElement();
	BankAccountElement(const IndexedElement::IdType accountId, const QString& type);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String accountIdAttribute_;
	static const QLatin1String typeAttribute_;
	IndexedElement::IdType accountId_;
	QString type_;
};

struct BankElement : BasicElement
{
	BankElement();
	BankElement(
		const QString& name,
		const bool isClosed,
		std::vector<BankAccountElement> accounts
	);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	static const QLatin1String nameAttribute_;
	static const QLatin1String isClosedAttribute_;
	QString name_;
	bool isClosed_;
	std::vector<BankAccountElement> accounts_;
};

struct BanksElement: BasicElement
{
	BanksElement();
	explicit BanksElement(std::vector<BankElement> banks);
	const QLatin1String& tag() const override;
	void read(QXmlStreamReader& xml) override;
	void write(QXmlStreamWriter& xml) const override;

	std::vector<BankElement> banks_;
};

}}}

#endif
