/*
 * 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_backup_progress.h"
#include "src/settings/prefs_specific.h"
#include "ui_dlg_backup_progress.h"

DlgBackupProgress::DlgBackupProgress(const QString &dirPath,
    const QList<BackupWorker::MessageEntry> &msgDbSets,
    const QString &accountDbFile, const QString &tagDbFile, QWidget *parent)
    : QDialog(parent),
    m_ui(new (::std::nothrow) Ui::DlgBackupProgress),
    m_backupThread(new (::std::nothrow) QThread),
    m_backupWorker(new (::std::nothrow) BackupWorker)
{
	m_ui->setupUi(this);

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

	initDialogue();

	m_backupWorker->setDir(dirPath);
	m_backupWorker->setMessageDbSets(msgDbSets);
	m_backupWorker->setAccountDb(accountDbFile);
	m_backupWorker->setTagDb(tagDbFile);

	m_backupWorker->moveToThread(m_backupThread);

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

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

bool DlgBackupProgress::backup(const QString &dirPath,
    const QList<BackupWorker::MessageEntry> &msgDbSets,
    const QString &accountDbFile, const QString &tagDbFile, QWidget *parent)
{
	DlgBackupProgress dlg(dirPath, msgDbSets, accountDbFile, tagDbFile,
	    parent);

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

	int ret = dlg.exec();

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

	return ret == QDialog::Accepted;
}

int DlgBackupProgress::exec(void)
{
	m_backupThread->start();
	return QDialog::exec();
}

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

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

void DlgBackupProgress::cancelBackup(void)
{
	/*
	 * You can send the signal immediately to the backup worker but then
	 * you must use Qt::DirectConnection because the worker thread does
	 * not possess its own event loop.
	 */
	m_backupWorker->setBreakBackupLoop();
	m_ui->notificationLabel->setText(
	    tr("Cancelling current and skipping all further tasks."));
}

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

void DlgBackupProgress::successNotification(void)
{
	QMessageBox::information(this, tr("Back-up Finished"),
	    tr("Back-up task finished without any errors."),
	    QMessageBox::Ok, QMessageBox::Ok);
	accept();
}

void DlgBackupProgress::closeEvent(QCloseEvent *event)
{
	cancelBackup();
	event->ignore();
}

void DlgBackupProgress::initDialogue(void)
{
	setWindowTitle(tr("Back-Up Progress"));

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

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

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

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

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