/*******************************************************************************
***  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 "ExchangeRatesDialog.h"
using ::QDialog;
using ::QWidget;
using std::set;
using drn::conversion::ConversionMap;
using drn::desktop_ui::internal::ExchangeRatesDialog;
using drn::foundation::ObserverPtr;

#include <pecunia/Codes.h>
using pecunia::currency::Iso4217Codes;
using pecunia::currency::toIso4217Code;
using pecunia::currency::toStdString;
#include <QAbstractButton>
using ::QAbstractButton;
#include <QDateTime>
using ::QDateTime;
#include <QDebug>
// using qDebug.
#include <QDialogButtonBox>
using ::QDialogButtonBox;
#include <QDoubleValidator>
using ::QDoubleValidator;
#include <QLineEdit>
using ::QLineEdit;
#include <QLocale>
using ::QLocale;
#include <QPushButton>
using ::QPushButton;
#include <QString>
using ::QString;
#include <QStringList>
using ::QStringList;
#include <QTableWidgetItem>
using ::QTableWidgetItem;

#include <chrono>
using std::chrono::duration_cast;
using std::chrono::milliseconds;
#include <stdexcept>
using std::logic_error;

#include <conversion/Constants.hpp>
using drn::conversion::conversionRatioDigits;
#include "desktop-ui/DesktopUiErrors.h"
using drn::desktop_ui::DataEntryError;
#include <foundation/QtMemory.hpp>
using drn::foundation::makeQtPtr;


void ExchangeRatesDialog::saveRates()
{
	qDebug() << "Saving exchange rates dialog button.";
	QLocale locale{};
	ConversionMap conversions{};

	for (auto row{0} ; row < this->exchangeRatesTblwdgt->rowCount() ; ++row)
	{
		const auto fromCode{this->exchangeRatesTblwdgt->verticalHeaderItem(row)->text()};
		const auto from{toIso4217Code(fromCode.toStdString())};

		for (auto column{0} ; column < this->exchangeRatesTblwdgt->columnCount() ; ++column)
		{
			const auto toCodeStr{this->exchangeRatesTblwdgt->horizontalHeaderItem(column)->text()};
			const auto item{
				dynamic_cast<QLineEdit*>(this->exchangeRatesTblwdgt->cellWidget(row, column))
			};

			if (item == nullptr)
				throw logic_error{"Exchange Rate table contains non-line edit widget."};
			bool isConverted;
			const auto ratio{locale.toDouble(item->text(), &isConverted)};

			if ( ! isConverted)
				throw DataEntryError{
					QObject::tr("conversion ratio"),
					item->text(),
					QObject::tr("Going from %1 to %2 contains a non-number or is empty.")
						.arg(fromCode)
						.arg(toCodeStr)
				};
			qDebug() << "From:" << fromCode <<  "To:" <<  toCodeStr << "Ratio:" << ratio;
			const auto to{toIso4217Code(toCodeStr.toStdString())};
			conversions.setConversion(from, to, ratio);
		}
	}
	emit savedExchangeRates(conversions);
	this->close();
}

void ExchangeRatesDialog::prepareFetchExchangeRates()
{
	qDebug() << "Fetching rates.";
	set<Iso4217Codes> currencies;

	for (auto row{0} ; row < this->exchangeRatesTblwdgt->rowCount() ; ++row)
	{
		const auto fromCode{this->exchangeRatesTblwdgt->verticalHeaderItem(row)->text()};
		currencies.emplace(toIso4217Code(fromCode.toStdString()));
	}
	emit fetchExchangeRates(currencies);
}

void ExchangeRatesDialog::onClicked(ObserverPtr<QAbstractButton> button)
{
	qDebug() << "Handling exchange rates dialog button" << button->text();

	if (this->saveDiscardFetchDlgbttnbx->button(QDialogButtonBox::Discard) == button)
		this->close();
	else if (this->saveDiscardFetchDlgbttnbx->button(QDialogButtonBox::Save) == button)
		this->saveRates();
	else if (this->fetch_.observed() == button)
		this->prepareFetchExchangeRates();
	else
		throw logic_error{"Unknown button pressed, " + button->text().toStdString() + '.'};
}

void ExchangeRatesDialog::onFetchedExchangeRates(
	const set<Iso4217Codes>& currencies,
	const ConversionMap& conversions
)
{
	this->exchangeRatesTblwdgt->setColumnCount(static_cast<qint32>(currencies.size()));
	this->exchangeRatesTblwdgt->setRowCount(static_cast<qint32>(currencies.size()));
	QStringList currenciesHeaders;
	QLocale locale{};

	// From is the row, To is the column.
	qint32 row{0};
	for (const auto& fromCode : currencies)
	{
		currenciesHeaders << QString::fromStdString(toStdString(fromCode));
		qint32 column{0};

		for (const auto& toCode : currencies)
		{
			auto edit{makeQtPtr<QLineEdit>(this->exchangeRatesTblwdgt)};
			edit->setFrame(false);
			edit->setValidator(makeQtPtr<QDoubleValidator>(edit.observed()).observed());

			if (conversions.hasConversion(fromCode, toCode))
				edit->setText(
					locale.toString(
						static_cast<double>(conversions.at(fromCode).find(toCode)->ratio_),
						'f',
						conversionRatioDigits
					)
				);

			if (fromCode == toCode)
				edit->setReadOnly(true);
			this->exchangeRatesTblwdgt->setCellWidget(row, column, edit.observed());
			++column;
		}
		++row;
	}
	this->exchangeRatesTblwdgt->setHorizontalHeaderLabels(currenciesHeaders);
	this->exchangeRatesTblwdgt->setVerticalHeaderLabels(currenciesHeaders);
	this->lastFetchedDttmdt->setDateTime(
		QDateTime::fromMSecsSinceEpoch(
			duration_cast<milliseconds>(conversions.lastFetched().time_since_epoch()).count()
		)
	);
}

ExchangeRatesDialog::ExchangeRatesDialog(
	const set<Iso4217Codes>& currencies,
	const ConversionMap& currentConversions,
	const ObserverPtr<QWidget>& parent
) :
	QDialog{parent.observed()},
	fetch_{nullptr}
{
	this->setAttribute(Qt::WA_DeleteOnClose);
	this->setupUi(this);
	this->onFetchedExchangeRates(currencies, currentConversions);
	this->fetch_ = this->saveDiscardFetchDlgbttnbx->addButton(
		tr("&Fetch"),
		QDialogButtonBox::ActionRole
	);

	if (this->fetch_ == nullptr)
		throw logic_error{"Failed to create the fetch button."};
	connect(
		this->saveDiscardFetchDlgbttnbx,
		&QDialogButtonBox::clicked,
		this,
		&ExchangeRatesDialog::onClicked
	);
}
