/*
 * Copyright (C) 2014-2024 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 <QCloseEvent>
#include <QMessageBox>
#include <QThread>

#include "src/dimensions/dimensions.h"
#include "src/global.h"
#include "src/gui/dlg_restore.h"
#include "src/gui/dlg_restore_internal.h"
#include "src/gui/dlg_restore_progress.h"
#include "src/settings/prefs_specific.h"
#include "ui_dlg_backup_progress.h"

DlgRestoreProgress::DlgRestoreProgress(QWidget *parent)
    : QDialog(parent),
    m_ui(new (::std::nothrow) Ui::DlgBackupProgress),
    m_restoreThread(new (::std::nothrow) QThread),
    m_restoreWorker(new (::std::nothrow) RestoreWorker)
{
	m_ui->setupUi(this);

	/* Don't do anything if failed to create the thread. */
	if (Q_UNLIKELY(m_restoreThread == Q_NULLPTR)) {
		Q_ASSERT(0);
		return;
	}
	if (Q_UNLIKELY(m_restoreWorker == Q_NULLPTR)) {
		Q_ASSERT(0);
		delete m_restoreThread; m_restoreThread = Q_NULLPTR;
		return;
	}

	initDialogue();

	m_restoreWorker->setDescr(
	    static_cast<RestoreWorker::RestorationDescr *>(DlgRestore::restoreDescr));

	/* Ensures that everything is deleted when thread finished. */
	connect(m_restoreWorker, SIGNAL(warning(QString, QString)),
	    this, SLOT(showWarning(QString, QString)));
	connect(m_restoreThread, SIGNAL(started()),
	    m_restoreWorker, SLOT(process()));
	connect(m_restoreWorker, SIGNAL(finished()),
	    m_restoreThread, SLOT(quit()));
	connect(m_restoreWorker, SIGNAL(finished()),
	    m_restoreWorker, SLOT(deleteLater()));
	connect(m_restoreThread, SIGNAL(finished()),
	    m_restoreThread, SLOT(deleteLater()));
}

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

void DlgRestoreProgress::restore(QWidget *parent)
{
	DlgRestoreProgress dlg(parent);

	const QString dlgName("restore_progress");
	const QSize dfltSize = dlg.size();
	{
		const QSize newSize = Dimensions::dialogueSize(&dlg,
		    PrefsSpecific::dlgSize(*GlobInstcs::prefsPtr, dlgName),
		    dfltSize);
		if (newSize.isValid()) {
			dlg.resize(newSize);
		}
	}

	dlg.exec();

	PrefsSpecific::setDlgSize(*GlobInstcs::prefsPtr, dlgName,
	    dlg.size(), dfltSize);
}

int DlgRestoreProgress::exec(void)
{
	m_restoreThread->start();
	return QDialog::exec();
}

void DlgRestoreProgress::setupProgress(int min, int max)
{
	m_ui->taskProgress->setMinimum(min);
	m_ui->taskProgress->setMaximum(max);
}

void DlgRestoreProgress::displayWork(const QString &name)
{
	m_ui->taskLabel->setText(tr("Restoring %1").arg(name));
}

void DlgRestoreProgress::cancelRestoration(void)
{
	/*
	 * You can send the signal immediately to the restoration worker but then
	 * you must use Qt::DirectConnection because the worker thread does
	 * not possess its own event loop.
	 */
	enum QMessageBox::StandardButton reply = QMessageBox::question(this,
	    tr("Cancel Restoration?"),
	    tr("Cancelling the restoration task may lead to data loss in the application. "
	       "Do you wish to cancel the restoration task?"),
	    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
	if (reply == QMessageBox::Yes) {
		m_restoreWorker->setBreakRestoreLoop();
		m_ui->notificationLabel->setText(
		    tr("Cancelling current and skipping all further tasks."));
	}
}

void DlgRestoreProgress::showWarning(const QString &title, const QString &text)
{
	QMessageBox::warning(this, title, text,
	    QMessageBox::Ok, QMessageBox::Ok);
}

void DlgRestoreProgress::successNotification(void)
{
	QMessageBox::information(this, tr("Restoration Finished"),
	    tr("Restoration task finished without any errors."),
	    QMessageBox::Ok, QMessageBox::Ok);
	accept();
}

void DlgRestoreProgress::closeEvent(QCloseEvent *event)
{
	cancelRestoration();
	event->ignore();
}

void DlgRestoreProgress::initDialogue(void)
{
	setWindowTitle(tr("Restoration Progress"));

	m_ui->taskLabel->setText(QString());
	m_ui->notificationLabel->setText(QString());

	m_ui->buttonBox->setStandardButtons(QDialogButtonBox::Cancel);

	if (m_restoreWorker != Q_NULLPTR) {
		connect(m_ui->buttonBox, SIGNAL(rejected()),
		    this, SLOT(cancelRestoration()));

		connect(m_restoreWorker, SIGNAL(started(int, int)),
		    this, SLOT(setupProgress(int, int)));
		connect(m_restoreWorker, SIGNAL(progress(int)),
		    m_ui->taskProgress, SLOT(setValue(int)));
		connect(m_restoreWorker, SIGNAL(working(QString)),
		    this, SLOT(displayWork(QString)));

		connect(m_restoreWorker, SIGNAL(succeeded()),
		    this, SLOT(successNotification()));
		connect(m_restoreWorker, SIGNAL(failed()),
		    this, SLOT(reject()));
	} else {
		/* Fallback. Close dialogue when worker does not exist. */
		connect(m_ui->buttonBox, SIGNAL(rejected()),
		    this, SLOT(reject()));
	}
}
