/*
 * Copyright (C) 2014-2026 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 <QHash>
#include <QList>

#include "src/io/message_db.h"
#include "src/io/timestamp_db.h" /* TstEntry, TstValidityHash */

#define RESTAMPING_INTERVAL_SUGGESTED 152 /* ~5 months */
#define RESTAMPING_INTERVAL_NEEDED 30 /* ~1 month */

class QColor; /* Forward declaration. */
namespace Json {
	class MsgId2List; /* Forward declaration. */
}

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

public:
	/*!
	 * @brief Additional roles.
	 */
	enum UserRoles {
		ROLE_PLAIN_DISPLAY = (Qt::UserRole + 1), /*!< Low level data. */
		ROLE_PROXYSORT /*!< 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_EXPIRATION_TIME, /*!< Expiration time. */
		COL_ERROR, /*!< Contains error description. */

		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 Restamping suggestion status.
	 */
	enum RestampingStatus {
		RS_NORMAL = 0x00, /*< No suggestion/warning. */
		RS_SUGGESTED = 0x01, /*!< Suggested restamping. */
		RS_NEEDED = 0x02, /*!< Needed restamping. */
		RS_EXPIRED = 0x04 /*!< Expired timestamp. */
	};

	/*!
	 * @brief Constructor.
	 *
	 * @param[in] parent Parent object.
	 */
	explicit MsgTstListingModel(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] tstEntries List of timestamp entries.
	 * @param[in] msgEntries Message-related data organised according to
	 *                       accounts and message identifiers.
	 */
	void appendData(const QList<TstEntry> &tstEntries,
	    const QHash <AcntId, QHash<MsgId, MessageDb::SoughtMsg> > &msgEntries);

	/*!
	 * @brief Remove first entries matching the supplied identifiers.
	 *
	 * @param[in] msgIds Message identifiers.
	 */
	void remove(const Json::MsgId2List &msgIds);

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

	/*!
	 * @brief Update error description.
	 *
	 * @param[in] anctId Account identifier.
	 * @param[in] msgId Message identifier.
	 * @param[in] error Error description.
	 */
	void updateError(const AcntId &anctId, const MsgId &msgId,
	    const QString &error);

	/*!
	 * @brief Update timestamp expiration data.
	 *
	 * @param[in] values New expiration values.
	 */
	void updateExpiration(const TstValidityHash &values);

	/*!
	 * @brief Return row containing given \a anctId and \a msgId.
	 *
	 * @param[in] anctId Account identifier.
	 * @param[in] msgId Message identifier.
	 * @return Row number if found, -1 if no corresponding entry held.
	 */
	int rowOf(const AcntId &anctId, const MsgId &msgId) const;

	/*!
	 * @brief Get identification at given row.
	 *
	 * @param[in] row Message row.
	 * @return Timestamp identification.
	 */
	const TstEntry &tstEntry(int row) const;

	/*!
	 * @brief Get message data at given row.
	 *
	 * @param[in] row Message row.
	 * @return Message data.
	 */
	const MessageDb::SoughtMsg &msgEntry(int row) const;

	/*!
	 * @brief Return suggested notification colour.
	 *
	 * @param[in] restampingStatus Flags, enum RestampingStatus.
	 * @return Colour depending on \a restampingStatus, may be invalid.
	 */
	static
	const QColor &notificationColour(int restampingStatus);

	/*!
	 * @brief Normalise the value to be in the range of the interval
	 *     <1, RESTAMPING_INTERVAL_SUGGESTED>.
	 *
	 * @param restampNeededDays Value to be normalised.
	 * @return Normalised value.
	 */
	static
	qint64 normaliseRestampingNeeded(qint64 restampNeededDays);

	/*!
	 * @brief Set the value of \a m_restampNeededDays.
	 *
	 * @note New value is normalised before being set into the model.
	 *
	 * @param[in] restampNeededDays New value of days to notify ahead.
	 */
	void setRestampingNeededDays(qint64 restampNeededDays);

public Q_SLOTS:
	/*!
	 * @brief Set current time as a reference time for the model.
	 */
	void updateCurrentTime(void);

private:
	/*!
	 * @brief Find first row according to supplied entries.
	 *
	 * @param[in] msgId Message ID.
	 * @return Non-negative row number if found, -1 else.
	 */
	int findRow(const Json::MsgId1 &msgId) const;
	int findRow(const Json::MsgId2 &msgId) const;

	/*!
	 * @brief Describes model entries.
	 */
	class Entry {
	public:
		Entry(const TstEntry &te, const MessageDb::SoughtMsg &me,
		     const QString &er)
		    : tstEntry(te), msgEntry(me), error(er)
		{ }

		TstEntry tstEntry;
		MessageDb::SoughtMsg msgEntry;
		QString error; /*!< Restamping error description. */
	};

	QList<Entry> m_entries; /*!< List of model entries. */

	qint64 m_restampNeededDays; /*!< Days ahead to notify about needed restamping. */
	QDateTime m_currentTime; /*!< Current time. */
	QDateTime m_timeSuggestesRestamping; /*!< Computed against \a m_currentTime. */
	QDateTime m_timeNeededRestamping; /*!< Computed against \a m_currentTime. */
};
