/*
 * Copyright (C) 2014-2021 CZ.NIC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <QFileDialog>
#include <QMessageBox>
#include <QString>

#include "src/common.h"
#include "src/datovka_shared/localisation/localisation.h"
#include "src/datovka_shared/settings/prefs.h"
#include "src/dimensions/dimensions.h"
#include "src/gui/dlg_pin_setup.h"
#include "src/gui/dlg_preferences.h"
#include "src/settings/prefs_specific.h"
#include "ui_dlg_preferences.h"

#define MSEC_IN_SEC 1000 /* One second in milliseconds. */
#define MSEC_IN_MIN 60000 /* One minute in milliseconds. */

/*!
 * @brief Language order as they are listed in the combo box.
 */
enum LangIndex {
	LANG_SYSTEM = 0,
	LANG_CZECH = 1,
	LANG_ENGLISH = 2
};

DlgPreferences::DlgPreferences(const Prefs &prefs, const PinSettings &pinSett,
    QWidget *parent)
    : QDialog(parent),
    m_ui(new (::std::nothrow) Ui::DlgPreferences),
    m_pinSett(pinSett),
    m_storeMessagesOnDiskOrig(PrefsSpecific::messageDbOnDisk(prefs)),
    m_storeAdditionalDataOnDiskOrig(PrefsSpecific::accountDbOnDisk(prefs))
{
	m_ui->setupUi(this);
	/* Tab order is defined in UI file. */

	{
		/* Adjust window size according to font size. */
		QSize newSize = Dimensions::windowSize(this, 46.0, 44.0);
		if (newSize.isValid()) {
			resize(newSize);
		}
	}
	initDialogue(prefs);
	activatePinButtons(m_pinSett);
}

DlgPreferences::~DlgPreferences(void)
{
	delete m_ui;
}

bool DlgPreferences::modify(Prefs &prefs, PinSettings &pinSett, QWidget *parent)
{
	DlgPreferences dlg(prefs, pinSett, parent);
	if (QDialog::Accepted == dlg.exec()) {
		dlg.saveSettings(prefs, pinSett);
		return true;
	} else {
		return false;
	}
}

void DlgPreferences::activateBackgroundTimer(int checkState)
{
	const bool enable = Qt::Checked == checkState;
	m_ui->timerPreLabel->setEnabled(enable);
	m_ui->timerSpinBox->setEnabled(enable);
	m_ui->timerPostLabel->setEnabled(enable);
}

/*!
 * @brief Notification dialogue.
 *
 * @param[in] parent Parent widget.
 */
static
void showNeededRestartNotification(QWidget *parent = Q_NULLPTR)
{
	QMessageBox::warning(parent, DlgPreferences::tr("Restart Needed"),
	    DlgPreferences::tr("In order to put this change into action you must restart the application after the new settings have been confirmed."),
	    QMessageBox::Ok, QMessageBox::Ok);
}

void DlgPreferences::storeMessagesOnDiskChanged(int state)
{
	if ((state == Qt::Checked) != m_storeMessagesOnDiskOrig) {
		showNeededRestartNotification(this);
	}
}

void DlgPreferences::storeAdditionalDataOnDiskChanged(int state)
{
	if ((state == Qt::Checked) != m_storeAdditionalDataOnDiskOrig) {
		showNeededRestartNotification(this);
	}
}

void DlgPreferences::setSavePath(void)
{
	QString newDir = QFileDialog::getExistingDirectory(this,
	    tr("Select directory"), m_ui->savePath->text(),
	    QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

	if (!newDir.isEmpty()) {
		m_ui->savePath->setText(newDir);
	}
}

void DlgPreferences::setAddFilePath(void)
{
	QString newDir = QFileDialog::getExistingDirectory(this,
	    tr("Select directory"), m_ui->addFilePath->text(),
	    QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
	if (!newDir.isEmpty()) {
		m_ui->addFilePath->setText(newDir);
	}
}

void DlgPreferences::setPin(void)
{
	if (Q_UNLIKELY(m_pinSett.pinConfigured())) {
		Q_ASSERT(0);
		return;
	}

	DlgPinSetup::change(m_pinSett, this);
	activatePinButtons(m_pinSett);
}

void DlgPreferences::changePin(void)
{
	if (Q_UNLIKELY(!m_pinSett.pinConfigured())) {
		Q_ASSERT(0);
		return;
	}

	DlgPinSetup::change(m_pinSett, this);
	activatePinButtons(m_pinSett);
}

void DlgPreferences::clearPin(void)
{
	if (Q_UNLIKELY(!m_pinSett.pinConfigured())) {
		Q_ASSERT(0);
		return;
	}

	DlgPinSetup::erase(m_pinSett, this);
	activatePinButtons(m_pinSett);
}

/*!
 * @brief Converts selected language index to language name.
 *
 * @param[in] index Language entry index.
 * @return Language name as used in settings.
 */
static
const char *indexToSettingsLang(int index)
{
	switch (index) {
	case LANG_CZECH:
		return Localisation::langCs;
		break;
	case LANG_ENGLISH:
		return Localisation::langEn;
		break;
	default:
		return Localisation::langSystem;
		break;
	}
}

/*!
 * @brief Converts language name to index.
 *
 * @param[in] lang Language name as used in settings.
 * @return Corresponding index.
 */
static
int settingsLangtoIndex(const QString &lang)
{
	if (Localisation::langCs == lang) {
		return LANG_CZECH;
	} else if (Localisation::langEn == lang) {
		return LANG_ENGLISH;
	} else {
		return LANG_SYSTEM;
	}
}

void DlgPreferences::saveSettings(Prefs &prefs, PinSettings &pinSett) const
{
	/* downloading */
	prefs.setBoolVal(
	    "window.main.action.sync_all_accounts.background.enabled",
	    m_ui->downloadOnBackground->isChecked());
	PrefsSpecific::setBackgroundSyncTimerMinutes(prefs,
	    m_ui->timerSpinBox->value());
	prefs.setBoolVal("action.sync_account.tie.action.download_message",
	    m_ui->autoDownloadWholeMessages->isChecked());
	prefs.setBoolVal("window.main.on.start.tie.action.sync_all_accounts",
	    m_ui->downloadAtStart->isChecked());
	prefs.setIntVal("network.isds.communication.timeout.ms",
	    m_ui->timeoutMinSpinBox->value() * MSEC_IN_MIN);
	prefs.setIntVal("window.main.message_list.read.mark_read.timeout.ms",
	    m_ui->timeoutMarkMsgSpinBox->value() * MSEC_IN_SEC);
	PrefsSpecific::setCheckNewVersions(prefs,
	    m_ui->checkNewVersions->isChecked());
	PrefsSpecific::setSendStatsWithVersionChecks(prefs,
	    m_ui->sendStatsWithVersionChecks->isChecked());

	/* security */
	prefs.setBoolVal("storage.database.messages.on_disk.enabled",
	    m_ui->storeMessagesOnDisk->isChecked());
	prefs.setBoolVal("storage.database.accounts.on_disk.enabled",
	    m_ui->storeAdditionalDataOnDisk->isChecked());
	prefs.setIntVal("crypto.message.validation.date.type",
	    m_ui->certValidationDateNow->isChecked() ?
	        PrefsSpecific::CURRENT_DATE : PrefsSpecific::DOWNLOAD_DATE);
	prefs.setBoolVal("crypto.crl.check.enabled",
	    m_ui->checkCrl->isChecked());
	prefs.setIntVal("crypto.message.timestamp.expiration.notify_ahead.days",
	    m_ui->timestampExpirSpinBox->value());
	pinSett = m_pinSett;

	/* navigation */
	{
		int val = PrefsSpecific::SELECT_NOTHING;
		if (m_ui->afterStartSelectNewest->isChecked()) {
			val = PrefsSpecific::SELECT_NEWEST;
		} else if (m_ui->afterStartSelectLast->isChecked()) {
			val = PrefsSpecific::SELECT_LAST_VISITED;
		} else {
			val = PrefsSpecific::SELECT_NOTHING;
		}
		prefs.setIntVal(
		    "window.main.message_list.select.on.account_change", val);
	}

	/* interface */
	{
		int val = PrefsSpecific::TEXT_UNDER_ICON;
		if (m_ui->toolButtonIconOnly->isChecked()) {
			val = PrefsSpecific::ICON_ONLY;
		} else if (m_ui->toolButtonTextBesideIcon->isChecked()) {
			val = PrefsSpecific::TEXT_BESIDE_ICON;
		} else {
			val = PrefsSpecific::TEXT_UNDER_ICON;
		}
		prefs.setIntVal("window.main.toolbar.button.style", val);
	}

	/* directories */
	PrefsSpecific::setUseGlobalPaths(prefs,
	    m_ui->enableGlobalPaths->isChecked());
	PrefsSpecific::setSaveAttachDir(prefs, m_ui->savePath->text());
	PrefsSpecific::setLoadAttachDir(prefs, m_ui->addFilePath->text());

	/* saving */
	prefs.setBoolVal(
	    "action.save_all_attachments.tie.action.export_message_zfo",
	    m_ui->allAttachmentsSaveZfoMsg->isChecked());
	prefs.setBoolVal(
	    "action.save_all_attachments.tie.action.export_acceptance_info_zfo",
	    m_ui->allAttachmentsSaveZfoDelInfo->isChecked());
	prefs.setBoolVal(
	    "action.save_all_attachments.tie.action.export_envelope_pdf",
	    m_ui->allAttachmentsSavePdfMsgEnvel->isChecked());
	prefs.setBoolVal(
	    "action.save_all_attachments.tie.action.export_acceptance_info_pdf",
	    m_ui->allAttachmentsSavePdfDelInfo->isChecked());
	prefs.setStrVal("file_system.format.filename.message",
	    m_ui->msgFileNameFmt->text());
	prefs.setStrVal("file_system.format.filename.acceptance_info",
	    m_ui->delInfoFileNameFmt->text());
	prefs.setStrVal("file_system.format.filename.attachment",
	    m_ui->attachFileNameFmt->text());
	prefs.setBoolVal(
	    "action.save_all_attachments.every_attachment.export_acceptance_info",
	    m_ui->delInfoForEveryFile->isChecked());
	prefs.setStrVal(
	    "file_system.format.filename.every_attachment_acceptance_info",
	    m_ui->allAttachDelInfoFileNameFmt->text());

	/* language */
	prefs.setStrVal("translation.language",
	    indexToSettingsLang(m_ui->langComboBox->currentIndex()));
}

void DlgPreferences::initDialogue(const Prefs &prefs)
{
	/* downloading */
	{
		bool val = false;
		if (prefs.boolVal(
		        "window.main.action.sync_all_accounts.background.enabled",
		        val)) {
			m_ui->downloadOnBackground->setChecked(val);
		}
	}
	m_ui->timerSpinBox->setValue(
	    PrefsSpecific::backgroundSyncTimerMinutes(prefs));
	{
		bool val = false;
		if (prefs.boolVal(
		        "action.sync_account.tie.action.download_message",
		        val)) {
			m_ui->autoDownloadWholeMessages->setChecked(val);
		}
	}
	{
		bool val = false;
		if (prefs.boolVal(
		        "window.main.on.start.tie.action.sync_all_accounts",
		        val)) {
			m_ui->downloadAtStart->setChecked(val);
		}
	}
	{
		qint64 val = 0;
		if (prefs.intVal("network.isds.communication.timeout.ms",
		        val)) {
			m_ui->timeoutMinSpinBox->setValue(val / MSEC_IN_MIN);
		}
	}
	m_ui->labelTimeoutNote->setText(tr(
	    "Note: If you have a slow network connection or you cannot download complete messages, here you can increase the connection timeout. "
	    "Default value is %1 minutes. Use 0 to disable timeout limit (not recommended).")
	        .arg(ISDS_DOWNLOAD_TIMEOUT_MS / MSEC_IN_MIN));
	{
		qint64 val = 0;
		if (prefs.intVal(
		        "window.main.message_list.read.mark_read.timeout.ms",
		        val)) {
			m_ui->timeoutMarkMsgSpinBox->setValue(val / MSEC_IN_SEC);
		}
	}
	m_ui->noteMarkMsgLabel->setText(tr(
	    "Note: Marked unread message will be marked as read after set interval. "
	    "Default value is %1 seconds. Use -1 disable the function.")
	        .arg(TIMER_MARK_MSG_READ_MS / MSEC_IN_SEC));
	m_ui->checkNewVersions->setChecked(
	    PrefsSpecific::checkNewVersions(prefs));
	m_ui->checkNewVersions->setEnabled(
	    PrefsSpecific::canConfigureCheckNewVersions());
	/* TODO - this choice must be disabled */
//	m_ui->sendStatsWithVersionChecks->setChecked(
//	    PrefsSpecific::sendStatsWithVersionChecks(prefs));
//	m_ui->sendStatsWithVersionChecks->setEnabled(
//	    m_ui->checkNewVersions->isChecked());

	connect(m_ui->downloadOnBackground, SIGNAL(stateChanged(int)),
	    this, SLOT(activateBackgroundTimer(int)));

	activateBackgroundTimer(m_ui->downloadOnBackground->checkState());

	/* security */
	{
		bool val = false;
		if (prefs.boolVal("storage.database.messages.on_disk.enabled",
		        val)) {
			m_ui->storeMessagesOnDisk->setChecked(val);
		}
	}
	{
		bool val = false;
		if (prefs.boolVal("storage.database.accounts.on_disk.enabled",
		        val)) {
			m_ui->storeAdditionalDataOnDisk->setChecked(val);
		}
	}
	{
		m_ui->certValidationDateNow->setChecked(false);
		m_ui->certValidationDateDownload->setChecked(false);
		qint64 val = PrefsSpecific::DOWNLOAD_DATE;
		if (prefs.intVal("crypto.message.validation.date.type", val)) {
			switch (val) {
			case PrefsSpecific::CURRENT_DATE:
				m_ui->certValidationDateNow->setChecked(true);
				break;
			case PrefsSpecific::DOWNLOAD_DATE:
				m_ui->certValidationDateDownload->setChecked(true);
				break;
			default:
				Q_ASSERT(0);
				break;
			}
		}
	}
	{
		bool val = false;
		if (prefs.boolVal("crypto.crl.check.enabled", val)) {
			m_ui->checkCrl->setChecked(val);
		}
	}
	{
		qint64 val = 0;
		if (prefs.intVal(
		    "crypto.message.timestamp.expiration.notify_ahead.days",
		    val)) {
			m_ui->timestampExpirSpinBox->setValue(val);
		}
	}

	connect(m_ui->storeMessagesOnDisk, SIGNAL(stateChanged(int)),
	    this, SLOT(storeMessagesOnDiskChanged(int)));
	connect(m_ui->storeAdditionalDataOnDisk, SIGNAL(stateChanged(int)),
	    this, SLOT(storeAdditionalDataOnDiskChanged(int)));

	connect(m_ui->setPinButton, SIGNAL(clicked()), this, SLOT(setPin()));
	connect(m_ui->changePinButton, SIGNAL(clicked()),
	    this, SLOT(changePin()));
	connect(m_ui->clearPinButton, SIGNAL(clicked()),
	    this, SLOT(clearPin()));

	/* navigation */
	{
		m_ui->afterStartSelectNewest->setChecked(false);
		m_ui->afterStartSelectLast->setChecked(false);
		m_ui->afterStartSelectNothing->setChecked(false);
		qint64 val = PrefsSpecific::SELECT_NOTHING;
		if (prefs.intVal(
		        "window.main.message_list.select.on.account_change",
		        val)) {
			switch (val) {
			case PrefsSpecific::SELECT_NEWEST:
				m_ui->afterStartSelectNewest->setChecked(true);
				break;
			case PrefsSpecific::SELECT_LAST_VISITED:
				m_ui->afterStartSelectLast->setChecked(true);
				break;
			case PrefsSpecific::SELECT_NOTHING:
				m_ui->afterStartSelectNothing->setChecked(true);
				break;
			default:
				Q_ASSERT(0);
				break;
			}
		}
	}

	/* interface */
	{
		m_ui->toolButtonIconOnly->setChecked(false);
		m_ui->toolButtonTextBesideIcon->setChecked(false);
		m_ui->toolButtonTextUnderIcon->setChecked(false);
		qint64 val = PrefsSpecific::TEXT_UNDER_ICON;
		if (prefs.intVal("window.main.toolbar.button.style", val)) {
			switch (val) {
			case PrefsSpecific::ICON_ONLY:
				m_ui->toolButtonIconOnly->setChecked(true);
				break;
			case PrefsSpecific::TEXT_BESIDE_ICON:
				m_ui->toolButtonTextBesideIcon->setChecked(true);
				break;
			case PrefsSpecific::TEXT_UNDER_ICON:
				m_ui->toolButtonTextUnderIcon->setChecked(true);
				break;
			default:
				Q_ASSERT(0);
				break;
			}
		}
	}

	/* directories */
	m_ui->enableGlobalPaths->setChecked(
	    PrefsSpecific::useGlobalPaths(prefs));
	m_ui->savePath->setText(PrefsSpecific::saveAttachDir(prefs));
	m_ui->addFilePath->setText(PrefsSpecific::loadAttachDir(prefs));

	connect(m_ui->savePathPushButton, SIGNAL(clicked()),
	    this, SLOT(setSavePath()));
	connect(m_ui->addFilePathPushButton, SIGNAL(clicked()),
	    this, SLOT(setAddFilePath()));

	/* saving */
	{
		bool val = false;
		if (prefs.boolVal(
		        "action.save_all_attachments.tie.action.export_message_zfo",
		        val)) {
			m_ui->allAttachmentsSaveZfoMsg->setChecked(val);
		}
	}
	{
		bool val = false;
		if (prefs.boolVal(
		        "action.save_all_attachments.tie.action.export_acceptance_info_zfo",
		        val)) {
			m_ui->allAttachmentsSaveZfoDelInfo->setChecked(val);
		}
	}
	{
		bool val = false;
		if (prefs.boolVal(
		        "action.save_all_attachments.tie.action.export_envelope_pdf",
		        val)) {
			m_ui->allAttachmentsSavePdfMsgEnvel->setChecked(val);
		}
	}
	{
		bool val = false;
		if (prefs.boolVal(
		        "action.save_all_attachments.tie.action.export_acceptance_info_pdf",
		        val)) {
			m_ui->allAttachmentsSavePdfDelInfo->setChecked(val);
		}
	}
	{
		QString val;
		if (prefs.strVal("file_system.format.filename.message", val)) {
			if (!val.isEmpty()) {
				m_ui->msgFileNameFmt->setText(val);
			}
		}
	}
	{
		QString val;
		if (prefs.strVal("file_system.format.filename.acceptance_info",
		        val)) {
			if (!val.isEmpty()) {
				m_ui->delInfoFileNameFmt->setText(val);
			}
		}
	}
	{
		QString val;
		if (prefs.strVal("file_system.format.filename.attachment",
		        val)) {
			if (!val.isEmpty()) {
				m_ui->attachFileNameFmt->setText(val);
			}
		}
	}
	{
		bool val = false;
		if (prefs.boolVal(
		        "action.save_all_attachments.every_attachment.export_acceptance_info",
		        val)) {
			m_ui->delInfoForEveryFile->setChecked(val);
		}
	}
	{
		QString val;
		if (prefs.strVal(
		        "file_system.format.filename.every_attachment_acceptance_info",
		        val)) {
			m_ui->allAttachDelInfoFileNameFmt->setText(val);
		}
	}

	/* language */
	m_ui->langComboBox->addItem(tr("Use system language"));
	m_ui->langComboBox->addItem(tr("Czech"));
	m_ui->langComboBox->addItem(tr("English"));
	{
		QString val;
		if (prefs.strVal("translation.language", val)) {
			m_ui->langComboBox->setCurrentIndex(
			    settingsLangtoIndex(val));
		}
	}
}

void DlgPreferences::activatePinButtons(const PinSettings &pinSett)
{
	m_ui->setPinButton->setEnabled(!pinSett.pinConfigured());
	m_ui->setPinButton->setVisible(!pinSett.pinConfigured());
	m_ui->changePinButton->setEnabled(pinSett.pinConfigured());
	m_ui->changePinButton->setVisible(pinSett.pinConfigured());
	m_ui->clearPinButton->setEnabled(pinSett.pinConfigured());
	m_ui->clearPinButton->setVisible(pinSett.pinConfigured());
}
