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

#pragma once

#include <QAbstractTableModel>
#include <QDateTime>
#include <QList>

#include "src/identifiers/account_id_db.h"
#include "src/identifiers/message_id.h"
#include "src/io/message_db.h"

/*!
 * @brief Message download state model.
 */
class MsgFoundModel : public QAbstractTableModel {
	Q_OBJECT

public:
	/*!
	 * @brief Additional roles.
	 */
	enum UserRoles {
		ROLE_PROXYSORT = (Qt::UserRole + 1) /*!< Allows fine-grained sorting of boolean values which may be displayed as icons or check boxes. */
	};
#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
	Q_ENUM(UserRoles)
#else /* < Qt-5.5 */
	Q_ENUMS(UserRoles)
#endif /* >= Qt-5.5 */

	/*!
	 * @brief Column which this model holds.
	 */
	enum Columns {
		COL_ACCOUNT_ID = 0, /*!< Account identifier. */
		COL_MESSAGE_ID, /*!< Message ID. */
		COL_ANNOTATION, /*< Message annotation. */
		COL_SENDER, /*!< Sender name. */
		COL_RECIPIENT, /*!< Recipient name. */
		COL_DELIVERY_TIME, /*!< Delivery time. */
		COL_ACCEPT_TIME, /*!< Acceptance time. */
		COL_ATTACH_DOWNLOADED, /*< Attachments downloaded. */

		MAX_COLNUM /* Maximal number of columns (convenience value). */
	};
#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
	Q_ENUM(Columns)
#else /* < Qt-5.5 */
	Q_ENUMS(Columns)
#endif /* >= Qt-5.5 */

	/*!
	 * @brief Identifies the origin of a message.
	 */
	class MsgIdentification {
	public:
		MsgIdentification(void)
		    : acntIdDb(), msgId(), isVodz(false),
		    msgType(MessageDb::TYPE_RECEIVED), downloaded(false)
		{}

		MsgIdentification(const AcntIdDb &ai, const MsgId &mi, bool v,
		    enum MessageDb::MessageType t, bool d)
		    : acntIdDb(ai), msgId(mi), isVodz(v), msgType(t),
		    downloaded(d)
		{}

		AcntIdDb acntIdDb; /*!< Account and database set. */
		MsgId msgId; /*!< Message identifier. */
		bool isVodz; /*!< Whether high-volume message. */
		enum MessageDb::MessageType msgType; /*!< Whether received or sent message. */

		bool downloaded; /*!< Complete message is downloaded. */
	};

	/*!
	 * @brief Constructor.
	 *
	 * @param[in] parent Parent object.
	 */
	explicit MsgFoundModel(QObject *parent = Q_NULLPTR);

	/*!
	 * @brief Returns number of rows under given parent.
	 *
	 * @param[in] parent Parent index.
	 * @return Number of rows.
	 */
	virtual
	int rowCount(const QModelIndex &parent = QModelIndex()) const
	    Q_DECL_OVERRIDE;

	/*!
	 * @brief Returns number of columns (for the children of given parent).
	 *
	 * @param[in] parent Parent index.
	 * @return Number of columns.
	 */
	virtual
	int columnCount(const QModelIndex &parent = QModelIndex()) const
	    Q_DECL_OVERRIDE;

	/*!
	 * @brief Returns the data stored under the given role.
	 *
	 * @param[in] index Position.
	 * @param[in] role  Role if the position.
	 * @return Data or invalid QVariant if no matching data found.
	 */
	virtual
	QVariant data(const QModelIndex &index,
	    int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Obtains header data.
	 *
	 * @param[in] section     Position.
	 * @param[in] orientation Orientation of the header.
	 * @param[in] role        Role of the data.
	 * @return Data or invalid QVariant in no matching data found.
	 */
	virtual
	QVariant headerData(int section, Qt::Orientation orientation,
	    int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Set append data to the model.
	 *
	 * @paran[in] acntIdDb Account identifier.
	 * @param[in] entryList List of entries to append to the model.
	 */
	void appendData(const AcntIdDb &acntIdDb,
	    const QList<MessageDb::SoughtMsg> &entryList);

	/*!
	 * @brief Set message detail text.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] msgId Message identifier. Delivery time is ignored.
	 * @param[in] detail Detail text.
	 */
	void setMessageDetail(const AcntId &acntId, const MsgId &msgId,
	    const QString &detail);

	/*!
	 * @brief Clears the model content.
	 */
	void removeAllRows(void);

	/*!
	 * @brief Remove first entry matching the supplied identifiers.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] msgId Message identifier. Delivery time is ignored.
	 */
	void remove(const AcntId &acntId, const MsgId &msgId);

	/*!
	 * @brief Get list of identifiers matching the supplied message ID.
	 *
	 * @param[in] dmId Message ID.
	 * @return List of message identifiers.
	 */
	QList<MsgIdentification> listIdentifications(qint64 dmId) const;

	/*!
	 * @brief Get list of identifiers for downloaded/not downloaded messages.
	 *
	 * @param[in] downloaded Whether to list downloaded or not downloaded messages.
	 * @return List of message identifiers.
	 */
	QList<MsgIdentification> listDownloaded(bool downloaded) const;

	/*!
	 * @brief Override message as having its attachments having downloaded.
	 *
	 * @note Emits dataChanged signal.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] msgId Message identifier. Delivery time is ignored.
	 * @param[in] forceDownloaded Set whether to force attachments
	 *                            downloaded state.
	 * @return True on success.
	 */
	bool overrideDownloaded(const AcntId &acntId, const MsgId &msgId,
	   bool forceDownloaded = true);

	/*!
	 * @brief Get identification of message at given row.
	 *
	 * @param[in] row Message row.
	 * @return Message identification.
	 */
	const MsgIdentification &msgIdentification(int row) const;

	/*!
	 * @brief Add a new custom column beyond the standard ones.
	 *
	 * @param[in] columnName Name of the newly added column.
	 * @return -1 on error, new column number else (starts from MAX_COLNUM).
	 */
	int addCustomColumn(const QString &columnName);

	/*!
	 * @brief Change value in custom column.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] msgId Message identifier. Delivery time is ignored.
	 * @param[in] col Custom column index.
	 * @param[in] forceVal New value.
	 * @return True on success.
	 */
	bool overrideCustomColumnVal(const AcntId &acntId, const MsgId &msgId,
	    int col, const QString &forceVal);

	/*!
	 * @brief Get custom column value.
	 *
	 * @param[in] row Message row.
	 * @param[in] col Custom column index.
	 * @return Custom column value like string.
	 */
	QString getCustomColumnVal(int row, int col) const;

private:
	/*!
	 * @brief Find first row according to supplied entries.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] dmId Message ID.
	 * @return Non-negative row number if found, -1 else.
	 */
	int findRow(const AcntId &acntId, qint64 dmId) const;

	/*!
	 * @brief Describes model entries.
	 */
	class Entry {
	public:
		Entry(const AcntIdDb &ai, const MsgId &mi, bool v,
		    enum MessageDb::MessageType t, bool d, const QString &a,
		    const QString &s, const QString &r, const QDateTime &ati,
		    const QString &md)
		    : identif(ai, mi, v, t, d), dmAnnotation(a), dmSender(s),
		    dmRecipient(r), dmAcceptanceTime(ati), msgDetail(md),
		    customValues()
		{}

		MsgIdentification identif; /*!< Message identification. */
		QString dmAnnotation; /*!< Message annotation. */
		QString dmSender; /*!< Message sender. */
		QString dmRecipient; /*!< Recipient. */
		QDateTime dmAcceptanceTime; /*!< Acceptance time. */

		QString msgDetail; /*!< Detailed message description. */

		QMap<int, QString> customValues; /*!< Starts indexing from MAX_COLNUM. */
	};

	QList<Entry> m_entries; /*!< List of model entries. */
	QList<QString> m_customColumnHeaders; /*< Header of custom added column. */
};
