/*******************************************************************************
***  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 "PostBudgetItemEntryWidget.h"
using std::set;
using std::set;
using drn::desktop_ui::internal::BudgetItemDueAction;
using drn::desktop_ui::internal::PostBudgetItemEntryWidget;
using drn::foundation::Switch;
using drn::foundation::SwitchStates;

#include <pecunia/Codes.h>
using pecunia::currency::Iso4217Codes;
#include <pecunia/Money.h>
using pecunia::currency::Money;
#include <QDate>
using ::QDate;
#include <QPair>
using ::QPair;
#include <QString>
using ::QString;
#include <QStringList>
using ::QStringList;
#include <QtDebug>
// using qDebug

#include <cassert>
//using assert

#include <accounting/AccountCode.h>
using drn::accounting::AccountCode;
using drn::accounting::toAccountCode;
#include <accounting/Transaction.h>
using drn::accounting::TransactionNumber;
#include "BankAccountsWidget.h"
using drn::desktop_ui::internal::BankAccountsWidget;
#include <banking/BankName.h>
using drn::banking::BankName;
using drn::banking::presentationText;
#include <banking/BankAccountType.h>
using drn::banking::BankAccountType;
#include <budgeting/BudgetItemDue.h>
using drn::budgeting::BudgetItemDue;
#include <budgeting/BudgetItemIdentifier.h>
using drn::budgeting::BudgetItemIdentifier;
#include <budgeting/BudgetItemTypes.h>
using drn::budgeting::allBudgetItemTypes;
using drn::budgeting::BudgetItemTypes;
using drn::budgeting::toBudgetItemTypes;
#include <budgeting/BudgetSource.h>
using drn::budgeting::BudgetSource;
#include <foundation/ObserverPtr.hpp>
using drn::foundation::ObserverPtr;
#include <foundation/Optional.hpp>
using drn::foundation::Optional;


void PostBudgetItemEntryWidget::setReadOnly(const bool isReadOnly)
{
	if (isReadOnly)
		this->isReadOnly_.flipOn();
	else
		this->isReadOnly_.flipOff();
	this->typeCmbbx->setEnabled( ! this->isReadOnly_);
	this->budgetItemDueCmbbx->setEnabled( ! this->isReadOnly_);
	this->amountMnyntrydgt->setReadOnly(this->isReadOnly_);
	this->sourceCmbbx->setReadOnly(this->isReadOnly_);
	this->postOnDtdt->setEnabled( ! this->isReadOnly_);
	this->actionIgnoreRdbttn->setEnabled( ! this->isReadOnly_);
	this->actionPostRdbttn->setEnabled( ! this->isReadOnly_);
	this->actionSkipRdbttn->setEnabled( ! this->isReadOnly_);
}

void PostBudgetItemEntryWidget::updateSourceDescription(const BudgetItemTypes& bit)
{
	switch (bit)
	{
	case BudgetItemTypes::Goal:
	case BudgetItemTypes::Wage:
		this->sourceCmbbx->setDescription(tr("Store In:"));
		break;
	case BudgetItemTypes::Bill:
	case BudgetItemTypes::Debt:
	case BudgetItemTypes::Nontrack:
		this->sourceCmbbx->setDescription(tr("Pay From:"));
		break;
	case BudgetItemTypes::Unknown:
	default:
		this->sourceCmbbx->setDescription(tr("Pay/Store:"));
	};

}

void PostBudgetItemEntryWidget::onTypeIndexChanged(const int index)
{
	const auto text{this->typeCmbbx->itemText(index)};
	qDebug() << "Requesting budget item sources look-up under budget item type" << text;
	const auto itemType{this->itemType()};
	this->updateSourceDescription(itemType);
	emit lookUpBudgetItemDueSources({itemType, this->itemDue()}, itemType);
	this->markDirty();
}

void PostBudgetItemEntryWidget::markDirty()
{
	if (this->isReadOnly_)
	{
		qDebug() << "Not marking" << this->budgetItemDueCmbbx->currentText()
				<< "as dirty since it's read-only";
		return;
	}

	if ( ! this->isDirty_)
	{
		qDebug() << "Marking" << this->budgetItemDueCmbbx->currentText() << "as dirty";
		this->isDirty_.flipOn();

		if (this->actionIgnoreRdbttn->isChecked())
		{
			this->actionPostRdbttn->setChecked(true);
			emit actionChanged(BudgetItemDueAction::Post);
		}
	}
}

void PostBudgetItemEntryWidget::onLookUpBankAccounts(const BankName& name)
{
	this->hasRequestedAccounts_.flipOn();
	emit lookUpBankAccounts(name);
}

PostBudgetItemEntryWidget::PostBudgetItemEntryWidget(
	const Optional<BudgetItemDue>& item,
	const set<BankName>& banks,
	const set<Iso4217Codes>& usableCurrencies,
	const ObserverPtr<QWidget>& parent
) :
	QWidget{parent.observed()},
	isDirty_{SwitchStates::Off},
	isReadOnly_{SwitchStates::Off},
	isUnexpectedItem_{SwitchStates::Off},
	hasRequestedAccounts_{SwitchStates::Off}
{
	this->setAttribute(Qt::WA_DeleteOnClose);
	this->setupUi(this);
	this->typeCmbbx->addItem(""); // BudgetItemTypes::Unknown

	for (const auto& budgetItemType : allBudgetItemTypes)
		if (budgetItemType != BudgetItemTypes::Unknown)
			this->typeCmbbx->addItem(presentationText(budgetItemType));
	this->amountMnyntrydgt->setUsableCodes(usableCurrencies);

	if ( ! item.hasValue())
	{
		this->typeCmbbx->setEnabled(true);
		this->budgetItemDueCmbbx->setEnabled(true);
		this->dueOnLbl->hide();
		this->dueOnDtdt->hide();
		this->actionSkipRdbttn->hide();
		this->isUnexpectedItem_.flipOn();
		this->updateSourceDescription(BudgetItemTypes::Unknown);
	}
	else
	{
		this->budgetItemDueCmbbx->addItem(item->id_.source_.name());
		this->dueOnDtdt->setDate(item->dueOn_);
		this->typeCmbbx->setCurrentText(presentationText(item->id_.type_));
		this->amountMnyntrydgt->setMoney(item->amount_);
		this->updateSourceDescription(item->id_.type_);
		this->sourceCmbbx->disableSupportedAccountTypes();
	}
	this->postLbl->hide();
	this->postOnDtdt->setDate(QDate::currentDate());
	this->errorMessageLbl->hide();
	this->sourceCmbbx->setBanks(banks);

	//{ Connect the amountMnyntrydgt

	connect(
		this->amountMnyntrydgt,
		&MoneyEntryWidget::moneyChanged,
		this,
		&PostBudgetItemEntryWidget::amountChanged
	);
	connect(
		this->amountMnyntrydgt,
		&MoneyEntryWidget::moneyChanged,
		this,
		&PostBudgetItemEntryWidget::markDirty
	);

	//}

	//{ Connect the sourceCmbbx

	connect(
		this->sourceCmbbx,
		&BankAccountsWidget::lookUpBankAccounts,
		this,
		&PostBudgetItemEntryWidget::onLookUpBankAccounts
	);
	connect(
		this->sourceCmbbx,
		&BankAccountsWidget::changedBankName,
		this,
		&PostBudgetItemEntryWidget::sourceChanged
	);
	connect(
		this->sourceCmbbx,
		&BankAccountsWidget::changedBankName,
		this,
		&PostBudgetItemEntryWidget::markDirty
	);
	connect(
		this->sourceCmbbx,
		&BankAccountsWidget::changedAccountNumber,
		this,
		&PostBudgetItemEntryWidget::accountNumberChanged
	);
	connect(
		this->sourceCmbbx,
		&BankAccountsWidget::changedAccountNumber,
		this,
		&PostBudgetItemEntryWidget::markDirty
	);

	//}

	//{ Connect the postOnDtdt

	connect(
		this->postOnDtdt,
		&QDateEdit::dateChanged,
		this,
		&PostBudgetItemEntryWidget::markDirty
	);

	//}

	//{ Connect the actionIgnoreRdbttn

	connect(
		this->actionIgnoreRdbttn,
		&QRadioButton::clicked,
		this,
		[this] ()
		{
			emit actionChanged(BudgetItemDueAction::Ignore);
		}
	);
	connect(
		this->actionIgnoreRdbttn,
		&QRadioButton::clicked,
		this,
		[this] ()
		{
			qDebug() << "Marking" << this->budgetItemDueCmbbx->currentText()
					<< "as not dirty since it's ignored";
			this->isDirty_.flipOff();
		}
	);

	//}

	//{ Connect the actionPostRdbttn

	connect(
		this->actionPostRdbttn,
		&QRadioButton::clicked,
		this,
		[this] ()
		{
			emit actionChanged(BudgetItemDueAction::Post);
		}
	);
	connect(
		this->actionPostRdbttn,
		&QRadioButton::clicked,
		this,
		&PostBudgetItemEntryWidget::markDirty
	);

	//}

	//{ Connect the actionSkipRdbttn

	connect(
		this->actionSkipRdbttn,
		&QRadioButton::clicked,
		this,
		[this] ()
		{
			emit actionChanged(BudgetItemDueAction::Skip);
		}
	);
	connect(
		this->actionSkipRdbttn,
		&QRadioButton::clicked,
		this,
		&PostBudgetItemEntryWidget::markDirty
	);

	//}

	connect(
		this->typeCmbbx,
		qOverload<qint32>(&QComboBox::currentIndexChanged),
		this,
		&PostBudgetItemEntryWidget::onTypeIndexChanged
	);
	connect(
		this->budgetItemDueCmbbx,
		qOverload<qint32>(&QComboBox::currentIndexChanged),
		this,
		 &PostBudgetItemEntryWidget::markDirty
	);
}

Optional<BankName> PostBudgetItemEntryWidget::bankName() const
{
	return this->sourceCmbbx->bank();
}

BudgetItemTypes PostBudgetItemEntryWidget::itemType() const
{
	const auto text{this->typeCmbbx->currentText()};

	if (text.isEmpty())
		return BudgetItemTypes::Unknown;
	return toBudgetItemTypes(text);
}

BudgetSource PostBudgetItemEntryWidget::itemDue() const
{
	auto txt{this->budgetItemDueCmbbx->currentText()};

	if (txt.isEmpty())
		return BudgetSource{};
	return BudgetSource{std::move(txt)};
}

BudgetItemDueAction PostBudgetItemEntryWidget::dueAction() const
{
	if (this->actionPostRdbttn->isChecked())
		return BudgetItemDueAction::Post;

	if (this->actionIgnoreRdbttn->isChecked())
		return BudgetItemDueAction::Ignore;

	if (this->actionSkipRdbttn->isChecked())
		return BudgetItemDueAction::Skip;
	return BudgetItemDueAction::Unknown;
}

Money PostBudgetItemEntryWidget::amount() const
{
	return this->amountMnyntrydgt->money();
}

Optional<AccountCode> PostBudgetItemEntryWidget::accountCode() const
{
	return this->sourceCmbbx->accountCode();
}

QDate PostBudgetItemEntryWidget::dueOn() const
{
	return this->dueOnDtdt->date();
}

QDate PostBudgetItemEntryWidget::postOn() const
{
	return this->postOnDtdt->date();
}

bool PostBudgetItemEntryWidget::isUnexpectedItem() const
{
	return this->isUnexpectedItem_;
}

void PostBudgetItemEntryWidget::setAccounts(set<BankAccountType> accountTypes)
{
	qDebug() << "Setting budget item entry account codes";
	this->sourceCmbbx->lookedUpBankAccounts(std::move(accountTypes));
	this->hasRequestedAccounts_.flipOff();
}

void PostBudgetItemEntryWidget::setPosted(const TransactionNumber& txnNum)
{
	qDebug() << "Marking posted" << this->budgetItemDueCmbbx->currentText() << "under"
			<< QString::number(txnNum.integer());
	this->postLbl->setText(tr("Posted Transaction #%1").arg(txnNum.integer()));
	this->postLbl->show();
}

void PostBudgetItemEntryWidget::setSkipped()
{
	qDebug() << "Marking skipped" << this->budgetItemDueCmbbx->currentText();
	this->postLbl->setText(tr("Item Due Skipped."));
	this->postLbl->show();
}

bool PostBudgetItemEntryWidget::isDirty() const
{
	return this->isDirty_;
}

bool PostBudgetItemEntryWidget::isReadOnly() const
{
	return this->isReadOnly_;
}

void PostBudgetItemEntryWidget::clearBankName()
{
	this->sourceCmbbx->setBank({});
}

void PostBudgetItemEntryWidget::saved()
{
	qDebug() << "Marking" << this->budgetItemDueCmbbx->currentText() << "as saved.";
	this->setReadOnly(true);
	this->errorMessageLbl->hide();
	this->errorMessageLbl->clear();
	this->isDirty_.flipOff();
}

void PostBudgetItemEntryWidget::setErrorMessage(const QString& message)
{
	qDebug() << "Updating" << this->budgetItemDueCmbbx->currentText() << "with error message."
			<< message;
	this->errorMessageLbl->setText(message);
	this->errorMessageLbl->show();
	this->isDirty_.flipOn();
}

void PostBudgetItemEntryWidget::setItemDueSources(const set<BudgetSource>& sources)
{
	this->budgetItemDueCmbbx->clear();
	this->budgetItemDueCmbbx->addItem("");

	for (const auto& source : sources)
		this->budgetItemDueCmbbx->addItem(source.name());
	this->isDirty_.flipOn();
}

bool PostBudgetItemEntryWidget::hasRequestedAccounts() const
{
	return this->hasRequestedAccounts_;
}
