/*******************************************************************************
***  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 "ConfigureDialog.h"
using namespace std::chrono_literals;
using pecunia::currency::allIso4217Codes;
using pecunia::currency::currencyName;
using pecunia::currency::Iso4217Codes;
using pecunia::currency::toIso4217Code;
using pecunia::currency::toStdString;
using ::QDialog;
using std::chrono::hours;
using std::set;
using drn::desktop_ui::internal::ConfigureDialog;
using drn::foundation::Days;

#include <exception>
using std::exception;

#include <QAbstractButton>
using ::QAbstractButton;
#include <QIntValidator>
using ::QIntValidator;
#include <QPushButton>
using ::QPushButton;
#include <QString>
using ::QString;
#include <QtDebug>
// using qDebug.
#include <QWidget>
using ::QWidget;

#include <budgeting/Constants.hpp>
using drn::budgeting::maximumDaysInAWeek;
using drn::budgeting::maximumDaysNotifyWithin;
using drn::budgeting::maximumHoursInAWeek;
using drn::budgeting::minimumDaysInAWeek;
using drn::budgeting::minimumDaysNotifyWithin;
using drn::budgeting::minimumHoursInAWeek;
#include <drn/config.h>
// using DuxReiNummariae_APPLICATION_LONG_NAME
#include <foundation/ObserverPtr.hpp>
using drn::foundation::ObserverPtr;
#include <foundation/QtMemory.hpp>
using drn::foundation::makeQtPtr;


void ConfigureDialog::handleSave()
try
{
	bool converted;
	const hours hrInWeek{this->workHoursLndt->text().toLongLong(&converted)};

	if ( ! converted)
	{
		this->onSaveFailed(tr("The number of work hours per week is not a number or is empty."));
		return;
	}
	const Days dayInWeek{this->workDaysLndt->text().toLongLong(&converted)};

	if ( ! converted)
	{
		this->onSaveFailed(tr("The number of work days per week is not a number or is empty."));
		return;
	}
	const Iso4217Codes& preferred{
		toIso4217Code(this->preferredCmbbx->currentText().split(' ').at(0).toStdString())
	};
	const set<Iso4217Codes> usable{
		[this, &preferred] ()
		{
			set<Iso4217Codes> usableCodes;
			for (const auto& selected : this->usableLstwdgt->selectedItems())
				usableCodes.emplace(
					toIso4217Code(selected->text().split(' ').at(0).toStdString())
				);

			if (usableCodes.empty())
				usableCodes.insert(preferred);
			return usableCodes;
		}()
	};
	const Days notifyWithin{this->notifyDueLndt->text().toLongLong(&converted)};

	if ( ! converted)
	{
		this->onSaveFailed(tr("The number of days to notify is not a number or is empty."));
		return;
	}
	emit updated(hrInWeek, dayInWeek, notifyWithin, preferred, usable);
}
catch (const exception& error)
{
	this->onSaveFailed(error.what());
}

void ConfigureDialog::updateUsable(const int& index)
{
	const auto text{this->usableLstwdgt->item(index)->text()};
	qDebug() << "Updating usable currencies" << text;
	auto items{this->usableLstwdgt->findItems(text, Qt::MatchExactly)};
	Q_ASSERT(items.size() == 1);
	items.at(0)->setSelected(true);
}

void ConfigureDialog::handleClicked(QAbstractButton* button)
{
	qDebug() << "Handling settings button" << button->text();

	if (this->saveDiscardBttnbx->button(QDialogButtonBox::Discard) == button)
	{
		this->close();
		return;
	}

	if (this->saveDiscardBttnbx->button(QDialogButtonBox::Save) == button)
	{
		this->handleSave();
		return;
	}
	Q_ASSERT(false && "Unknown button pressed.");
}

void ConfigureDialog::onSaveSucceeded()
{
	this->errorsLbl->clear();
	this->errorsLbl->hide();
	this->close();
}

void ConfigureDialog::onSaveFailed(const QString& errorMessage)
{
	this->errorsLbl->setText(errorMessage);
	this->errorsLbl->show();
}

bool ConfigureDialog::close()
{
	qDebug() << "The configure dialog is about to close.";
	emit closing();
	return QWidget::close();
}

ConfigureDialog::ConfigureDialog(
	const hours& hrInWeek,
	const Days& dayInWeek,
	const Days& notifyDueWithin,
	const Iso4217Codes& preferred,
	const set<Iso4217Codes>& usable,
	const set<Iso4217Codes>& used,
	const ObserverPtr<QWidget>& parent
) :
	QDialog{parent.observed()},
	used_{used}
{
	this->setAttribute(Qt::WA_DeleteOnClose);
	this->setupUi(this);
	this->setWindowTitle(tr("Configure %1").arg(DuxReiNummariae_APPLICATION_LONG_NAME));
	this->errorsLbl->hide();

	//{ Budgeting Tab

	this->workHoursLndt->setText(QString::number(hrInWeek.count()));
	this->workHoursLndt->setValidator(
		makeQtPtr<QIntValidator>(minimumHoursInAWeek, maximumHoursInAWeek, this).observed()
	);
	this->workDaysLndt->setText(QString::number(dayInWeek.count()));
	this->workDaysLndt->setValidator(
		makeQtPtr<QIntValidator>(minimumDaysInAWeek, maximumDaysInAWeek, this).observed()
	);
	this->notifyDueLndt->setText(QString::number(notifyDueWithin.count()));
	this->notifyDueLndt->setValidator(
		makeQtPtr<QIntValidator>(minimumDaysNotifyWithin, maximumDaysNotifyWithin, this).observed()
	);

	//}

	//{ Currencies Tab

	qint32 usableRow{0};
	for (const auto& code : allIso4217Codes)
		if (code != Iso4217Codes::XXX)
		{
			const auto iso4217{QString::fromStdString(toStdString(code))};
			const auto name{QString::fromStdString(currencyName(code))};
			const auto entryText{QString("%1 (%2)").arg(iso4217).arg(name)};
			this->preferredCmbbx->addItem(entryText);
			this->usableLstwdgt->addItem(entryText);

			if (preferred == code)
				this->preferredCmbbx->setCurrentText(entryText);

			if (usable.find(code) != usable.cend())
				this->usableLstwdgt->item(usableRow)->setSelected(true);
			++usableRow;
		}
	connect(
		this->preferredCmbbx,
		static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
		this,
		&ConfigureDialog::updateUsable
	);

	//}
}
