/*
 * 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.
 */

#pragma once

#include <QDialog>
#include <QList>
#include <QThread>

#include "src/gui/message_operations.h" /* MsgOrigin */
#include "src/models/message_download_model.h"
#include "src/models/sort_filter_proxy_model.h"

class AcntIdDb; /* Forward declaration. */
class MsgId; /* Forward declaration. */

/*!
 * @brief Describes message origin and records management upload hierarchy target.
 */
class MsgOriginAndUpladTarget : public MsgOrigin {
public:
	MsgOriginAndUpladTarget(void)
	    : MsgOrigin(), taskFlags(0), recMgmtHierarchyId()
	{ }

	MsgOriginAndUpladTarget(const AcntIdDb &ai, const MsgId &mi, enum MessageDirection md,
	    bool d, int f, const QString &hi)
	    : MsgOrigin(ai, mi, md, d), taskFlags(f), recMgmtHierarchyId(hi)
	{ }

	MsgOrigin toMsgOrigin(void) const;

	int taskFlags; /*!< Download message flags. */
	QString recMgmtHierarchyId; /*!< records management hierarchy identifier. */

	static
	MsgOriginAndUpladTarget fromMsgOrigin(const MsgOrigin &origin);

	static
	QList<MsgOriginAndUpladTarget> fromMsgOriginList(const QList<MsgOrigin> &originList);

	static
	QList<MsgOrigin> toMsgOriginList(const QList<MsgOriginAndUpladTarget> &oautList);
};

/*!
 * @brief Message download thread.
 */
class MessageDownloadThread : public QThread {
	Q_OBJECT

public:
	/*!
	 * @brief Constructor.
	 *
	 * @param[in]      originAndTargetList List of message identifiers and
	 *                                     upload targets.
	 * @param[in, out] Variable to interrupt the process.
	 */
	explicit MessageDownloadThread(
	    const QList<MsgOriginAndUpladTarget> &originAndTargetList,
	    volatile bool &stop);

signals:
	/*!
	 * @brief Emitted on thread start.
	 *
	 * @param[in] i First message index, actually always 0.
	 * @param[in] total Total number of processed messages.
	 */
	void started(int i, int total);

	/*!
	 * @brief Emitted on thread stop.
	 *
	 * @param[in] interrupted True if the download process has been interrupted by user.
	 */
	void stopped(bool interrupted);

	/*!
	 * @brief Emitted after download of a message.
	 *
	 * @param[in] i Processed message index.
	 * @param[in] total Total number of processed messages.
	 * @param[in] acntIdDb Account identifier.
	 * @param[in] msgId Message identifier, delivery time may have changed.
	 * @param[in] success True when downloaded successfully.
	 * @param[in] error Error description.
	 */
	void processed(int i, int total, const AcntIdDb &acntIdDb,
	    const MsgId &msgId, bool success, const QString &error);

protected:
	/*!
	 * @brief Thread function.
	 */
	virtual
	void run(void) Q_DECL_OVERRIDE;

private:
	const QList<MsgOriginAndUpladTarget> &m_originAndTargetList; /*!< List of messages to be downloaded. */
	volatile bool &m_stop; /*!< Signals break of download loop. */
};

class MainWindow;

namespace Ui {
	class DlgDownloadMessages;
}

/*!
 * @brief Download messages dialogue.
 */
class DlgDownloadMessages : public QDialog {
	Q_OBJECT

private:
	/*!
	 * @brief Controls the proposal the user is offered to do.
	 */
	enum FinalProposal {
		ACCEPT_ALLOW, /*!< Allow continuation even if all downloads fail. */
		ACCEPT_ON_SOME_DOWNLOADED, /*!< Allow continuation if some downloads successful. */
		ACCEPT_INHIBIT /*!< Always disable continuation. */
	};

	/*!
	 * @brief Constructor.
	 *
	 * @param[in] originAndTargetList List of message identifiers and
	 *                                upload targets.
	 * @param[in] finalProposal Whether the acceptance of the dialogue
	 *                          should be enabled.
	 * @param[in] parent Parent object.
	 */
	DlgDownloadMessages(
	    const QList<MsgOriginAndUpladTarget> &originAndTargetList,
	    enum FinalProposal finalProposal, QWidget *parent = Q_NULLPTR);

public:
	/*!
	 * @brief Destructor.
	 */
	~DlgDownloadMessages(void);

private:
	/*!
	 * @brief Download given messages.
	 *
	 * @note Ignores whether they have been downloaded previously.
	 *
	 * @param[in]  originAndTargetList List of message identifiers and
	 *                                 upload targets.
	 * @param[in]  finalProposal Whether the acceptance of the dialogue
	 *                           should be enabled.
	 * @param[out] abort Set to true if user wants to skip the entire operation.
	 * @param[in]  parent Parent widget.
	 * @return List of now successfully downloaded messages.
	 */
	static
	QList<MsgOrigin> download(
	    const QList<MsgOriginAndUpladTarget> &originAndTargetList,
	    enum FinalProposal finalProposal, bool &abort,
	    QWidget *parent = Q_NULLPTR);

public:
	/*!
	 * @brief Download all messages regardless whether they've been
	 *     downloaded before.
	 *
	 * @param[in] originAndTargetList List of message identifiers and
	 *                                upload targets.
	 * @param[in] mw Pointer to main window.
	 * @param[in] parent Parent widget.
	 */
	static
	void downloadAll(
	    const QList<MsgOriginAndUpladTarget> &originAndTargetList,
	    MainWindow *mw, QWidget *parent = Q_NULLPTR);

	/*!
	 * @brief Offer and perform download of missing messages.
	 *
	 * @param[in] originAndTargetList List of message identifiers and
	 *                                upload targets.
	 * @param[in] mw Pointer to main window.
	 * @param[in] parent Parent widget.
	 * @return List of already downloaded and those which were downloaded now.
	 */
	static
	QList<MsgOrigin> offerDownloadForMissing(
	    const QList<MsgOriginAndUpladTarget> &originAndTargetList,
	    MainWindow *mw, QWidget *parent = Q_NULLPTR);

private slots:
	/*!
	 * @brief Interrupt the download or close the dialogue when no download running.
	 */
	void interruptDownload(void);

	/*!
	 * @brief Reset progress bar.
	 *
	 * @param[in] i Minimum.
	 * @param[in] total Maximum.
	 */
	void downloadStarted(int i, int total);

	/*!
	 * @brief handles end of download thread.
	 *
	 * @param[in] interrupted True if the download process has been interrupted by user.
	 */
	void downloadStopped(bool interrupted);

	/*!
	 * @brief handle download progress.
	 *
	 * @param[in] i Processed message index.
	 * @param[in] total Total number of processed messages.
	 * @param[in] acntIdDb Account identifier.
	 * @param[in] msgId Message identifier, delivery time may have changed.
	 * @param[in] success True when downloaded successfully.
	 * @param[in] error Error description.
	 */
	void downloadedMessage(int i, int total, const AcntIdDb &acntIdDb,
	    const MsgId &msgId, bool success, const QString &error);

private:
	/*!
	 * @brief Initialise the dialogue.
	 */
	void initDialogue(void);

	Ui::DlgDownloadMessages *m_ui; /*!< UI generated from UI file. */

	const enum FinalProposal m_finalProposal; /*!<
	                                           * Whether continuation
	                                           * should be allowed.
	                                           */

	SortFilterProxyModel m_downloadListProxyModel; /*!<
	                                                * Used for message
	                                                * sorting and filtering.
	                                                */
	MsgDownloadModel m_downloadModel; /*!< Lists downloaded messages. */

	volatile bool m_breakDownloadLoop; /*!< Signals break of download loop. */
	MessageDownloadThread m_downloadThread; /*!< Download thread. Doesn't block the GUI when downloading. */
};
