/*
 * 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 <QCoreApplication> /* Q_DECLARE_TR_FUNCTIONS */
#include <QDateTime>
#include <QJsonDocument>
#include <QList>
#include <QPair>
#include <QSet>
#include <QStringList>
#include <QString>
#include <QVector>

#include "src/common.h"
#include "src/datovka_shared/isds/message_interface.h"
#include "src/datovka_shared/isds/types.h"
#include "src/identifiers/message_id.h"
#include "src/io/sqlite/delayed_access_db.h"

#define DB2 "db2"

namespace Isds {
	/* Forward class declaration. */
	class DmMessageAuthor;
}

enum Sorting {
	UNSORTED = 0,
	ASCENDING,
	DESCENDING
};

/*!
 * @brief Encapsulates message database.
 *
 * DelayedAccessSQLiteDb inherits from QObject.
 */
class MessageDb : public DelayedAccessSQLiteDb {
	Q_OBJECT

public:
	/*
	 * Don't forget to declare types in order to make them work with
	 * the meta-object system.
	 */
	static
	void declareTypes(void);

	/*!
	 * @brief Name of invalid year.
	 */
	static const QString invalidYearName;

	/*!
	 * @brief Message processing state. */
	enum MessageProcessState {
		UNSETTLED = 0,
		IN_PROGRESS = 1,
		SETTLED = 2
	};

	/*!
	 * @brief Used to distinguish between sent and received messages in db.
	 *
	 * @note This value cannot be changed without breaking backward
	 *     compatibility.
	 */
	enum MessageType {
		TYPE_RECEIVED = 1, /*!< One is received. */
		TYPE_SENT = 2 /*!< Two is sent. */
	};

	/*!
	 * @brief Message verification result.
	 */
	enum MsgVerificationResult {
		MSG_NOT_PRESENT = 1, /*!< No complete message in db. */
		MSG_SIG_OK = 2, /*!< Message was verified and signature is OK. */
		MSG_SIG_BAD = 3  /*!< Message was verified and signature is bad. */
	};

	/*!
	 * @brief Basic message information for search dialogue.
	 */
	class SoughtMsg {
	public:
		MsgId mId; /*!< Message identifier. */
		bool isVodz; /*!< Whether is high-volume message. */
		enum MessageType type; /*!< Received or sent. */
		QString dmAnnotation; /*!< Message annotation. */
		QString dmSender; /*!< Message sender. */
		QString dmRecipient; /*!< Recipient. */
		QDateTime dmAcceptanceTime; /*!< Acceptance time. */
		bool isDownloaded; /*!< True if complete message has been downloaded. */

		SoughtMsg(void)
		    : mId(), isVodz(false), type(TYPE_RECEIVED), dmAnnotation(),
		    dmSender(), dmRecipient(), dmAcceptanceTime(),
		    isDownloaded(false)
		{ }
		SoughtMsg(const SoughtMsg &other)
		    : mId(other.mId), isVodz(other.isVodz), type(other.type),
		    dmAnnotation(other.dmAnnotation), dmSender(other.dmSender),
		    dmRecipient(other.dmRecipient),
		    dmAcceptanceTime(other.dmAcceptanceTime),
		    isDownloaded(other.isDownloaded)
		{ }
		SoughtMsg(const MsgId &id, bool v, enum MessageType t,
		    const QString &annot, const QString &sen,
		    const QString &rec, const QDateTime &ati, bool d)
		    : mId(id), isVodz(v), type(t), dmAnnotation(annot),
		    dmSender(sen), dmRecipient(rec), dmAcceptanceTime(ati),
		    isDownloaded(d)
		{ }
		SoughtMsg(qint64 id, const QDateTime &dTime, bool v,
		    enum MessageType t, const QString &annot, const QString &sen,
		    const QString &rec, const QDateTime &ati, bool d)
		    : mId(id, dTime), isVodz(v), type(t), dmAnnotation(annot),
		    dmSender(sen), dmRecipient(rec), dmAcceptanceTime(ati),
		    isDownloaded(d)
		{ }
		~SoughtMsg(void)
		{ }

		SoughtMsg &operator=(const SoughtMsg &other) Q_DECL_NOTHROW
		{
			mId = other.mId;
			isVodz = other.isVodz;
			type = other.type;
			dmAnnotation = other.dmAnnotation;
			dmSender = other.dmSender;
			dmRecipient = other.dmRecipient;
			dmAcceptanceTime = other.dmAcceptanceTime;
			isDownloaded = other.isDownloaded;
			return *this;
		}

#ifdef Q_COMPILER_RVALUE_REFS
		inline
		SoughtMsg &operator=(SoughtMsg &&other) Q_DECL_NOTHROW
		{
			/* qSwap() is obsolete */
			::std::swap(mId, other.mId);
			::std::swap(isVodz, other.isVodz);
			::std::swap(type, other.type);
			::std::swap(dmAnnotation, other.dmAnnotation);
			::std::swap(dmSender, other.dmSender);
			::std::swap(dmRecipient, other.dmRecipient);
			::std::swap(dmAcceptanceTime, other.dmAcceptanceTime);
			::std::swap(isDownloaded, other.isDownloaded);
			return *this;
		}
#endif /* Q_COMPILER_RVALUE_REFS */

		bool isValid(void) const
		{
			return mId.isValid() &&
			    ((type == TYPE_RECEIVED) || (type == TYPE_SENT)) &&
			    (!dmAnnotation.isEmpty()) &&
			    (!dmSender.isEmpty()) && (!dmRecipient.isEmpty());
		}
	};

	/*!
	 * @brief Received entries.
	 */
	class RcvdEntry {
	public:
		qint64 dmId; /*!< Message identifier. */
		bool dmPersonalDelivery; /*!< Personal delivery. */
		QString dmAnnotation; /*!< Message annotation. */
		QString dmSender; /*!< Message sender. */
		QString dmDeliveryTime; /*!< Delivery time as stored in the database. */
		QString dmAcceptanceTime; /*!< Acceptance time as stored in the database. */
		bool readLocally; /*!< True if locally read. */
		enum Isds::Type::DmState dmMessageStatus; /*!< Brief message status. */
		bool isDownloaded; /*!< True if complete message has been downloaded. */
		int processStatus; /*!< Brief processing status. */

		RcvdEntry(qint64 i, bool pd, const QString &a, const QString &s,
		    const QString &dt, const QString &at, bool rl,
		    enum Isds::Type::DmState ms, bool id, int ps)
		    : dmId(i), dmPersonalDelivery(pd), dmAnnotation(a),
		    dmSender(s), dmDeliveryTime(dt), dmAcceptanceTime(at),
		    readLocally(rl), dmMessageStatus(ms), isDownloaded(id),
		    processStatus(ps)
		{ }
	};

	/*!
	 * @brief Sent entries.
	 */
	class SntEntry {
	public:
		qint64 dmId; /*!< Message identifier. */
		QString dmAnnotation; /*!< Message annotation. */
		QString dmRecipient; /*!< Message recipient. */
		QString dmDeliveryTime; /*!< Delivery time as stored in the database. */
		QString dmAcceptanceTime; /*!< Acceptance time as stored in the database. */
		enum Isds::Type::DmState dmMessageStatus; /*!< Brief message status. */
		bool isDownloaded; /*!< True if complete message has been downloaded. */

		SntEntry(qint64 i, const QString &a, const QString &r,
		    const QString &dt, const QString &at,
		    enum Isds::Type::DmState ms, bool id)
		    : dmId(i), dmAnnotation(a), dmRecipient(r),
		    dmDeliveryTime(dt), dmAcceptanceTime(at),
		    dmMessageStatus(ms), isDownloaded(id)
		{ }
	};

	/*!
	 * @brief Attachment data used to fill attachment model.
	 */
	class AttachmentEntry {
	public:
		qint64 id; /*!< Entry identifier. */
		qint64 messageId; /*!< Identifier of the message which the attachment belong to. */
		QByteArray binaryContent; /*!< Raw (non-base64-encoded) attachment content. */
		QString dmFileDescr; /*!< Attachment file name. */
		QString dmMimeType; /*!< String holding the mime type. */

		AttachmentEntry(void)
		    : id(0), messageId(0), binaryContent(), dmFileDescr(),
		    dmMimeType()
		{ }
		AttachmentEntry(qint64 i, qint64 mi, const QByteArray &bc,
		    const QString &dfd, const QString &dmt)
		    : id(i), messageId(mi), binaryContent(bc),
		    dmFileDescr(dfd), dmMimeType(dmt)
		{ }

		bool isValid(void) const
		{
			return (!dmFileDescr.isEmpty()) &&
			    (!binaryContent.isEmpty());
		}
	};

	class ContactEntry {
	public:
		qint64 dmId; /*!< Message id. */
		QString boxId;
		QString name;
		QString address;
	};

	/*!
	 * @brief Constructor.
	 *
	 * @param[in] connectionName Connection name.
	 */
	explicit MessageDb(const QString &connectionName);

	/*!
	 * @brief Return path to directory where associated files can be stored.
	 *
	 * @note The path may actually not exist if no files are stored there.
	 *
	 * @return String containing a directory path.
	 */
	QString assocFileDirPath(void) const;

	/*!
	 * @brief Get message envelope info for reply/forward dialogue.
	 *
	 * @param[in] dmId Message id.
	 * @return Message envelope structure. Returns empty structure if failure.
	 */
	Isds::Envelope getMessageEnvelope(qint64 dmId);

	/*!
	 * @brief Return message type (sent or received).
	 *
	 * @param[in] dmId Message id.
	 * @return Message type value, negative value -1 on error.
	 */
	int getMessageType(qint64 dmId);

	/*!
	 * @brief Returns whether message is verified.
	 *
	 * @param[in] dmId Message identifier.
	 * @return Return message sign verification status.
	 */
	enum MsgVerificationResult isMessageVerified(qint64 dmId);

	/*!
	 * @brief Returns whether message was read locally.
	 *
	 * @param[in]  dmId Message ID.
	 * @param[out] ok Set to true on success.
	 * @return False if not read or on failure.
	 */
	bool messageLocallyRead(qint64 dmId, bool *ok = Q_NULLPTR);

	/*!
	 * @brief Set message read locally status.
	 *
	 * @note Emits changedLocallyRead() when stored value changed.
	 *
	 * @param[in] msgIds Message identifiers.
	 * @param[in] read New read status.
	 * @return True on success.
	 */
	bool setMessagesLocallyRead(const QList<MsgId> &msgIds, bool read = true);

	/*!
	 * @brief Check whether message with given ID is a high-volume message.
	 *
	 * @param[in]  dmId Message ID.
	 * @param[out] ok Set to true on success.
	 * @return True if message message entry found and is VoDZ.
	 */
	bool isVodz(qint64 dmId, bool *ok = Q_NULLPTR);

	/*!
	 * @brief Number of days remaining since acceptance to a date from
	 *     a given limit of given days.
	 *
	 * @param[in] acceptanceTime Message acceptance time.
	 * @param[in] toDate Current date after acceptance.
	 * @param[in] givenLimit Limit of days to subtract the difference from.
	 * @note Returns 0 if message has already deleted from ISDS or -1
	 *       if the acceptance time is invalid.
	 */
	static
	int daysRemainingSinceAcceptance(const QDateTime &acceptanceTime,
	    const QDate &toDate = QDate::currentDate(), int givenLimit = 91);

	/*!
	 * @brief Get message acceptance time.
	 *
	 * @param[in] dmId Message identifier.
	 * @return Acceptance time, null value if unknown or on error.
	 */
	QDateTime messageAcceptanceTime(qint64 dmId);

	/*!
	 * @brief Insert message envelope into database.
	 *
	 * @param[in] envelope Message envelope structure.
	 * @param[in] _origin Internal download identifier.
	 * @param[in] msgDirect Message orientation.
	 * @param[in] transaction True to create a transaction.
	 * @return True on success.
	 */
	bool insertMessageEnvelope(const Isds::Envelope &envelope,
	    const QString &_origin, enum MessageDirection msgDirect,
	    bool transaction);

	/*!
	 * @brief Update message envelope in database.
	 *
	 * @param[in] envelope Message envelope structure.
	 * @param[in] _origin Internal download identifier.
	 * @param[in] msgDirect Message orientation.
	 * @param[in] transaction True to create a transaction.
	 * @return True on success.
	 */
	bool updateMessageEnvelope(const Isds::Envelope &envelope,
	    const QString &_origin, enum MessageDirection msgDirect,
	    bool transaction);

	/*!
	 * @brief Get message dmType.
	 *
	 * @param[in] dmId Message identifier.
	 * @return Message type character or null value if message doesn't exist
	 *     in the database.
	 */
	QChar getDmType(qint64 dmId);

	/*!
	 * @brief Get message status.
	 *
	 * @param[in] dmId Message identifier.
	 * @return Message state or MS_NULL if message does not exist in the database.
	 */
	enum Isds::Type::DmState getMessageStatus(qint64 dmId);

	/*!
	 * @brief Update message envelope delivery information.
	 *
	 * @param[in] dmId Message identifier.
	 * @param[in] dmDeliveryTime Delivery time in database format.
	 * @param[in] dmAcceptanceTime Acceptance time in database format.
	 * @return True on success.
	 */
	bool msgsUpdateMessageState(qint64 dmId,
	    const QDateTime &dmDeliveryTime, const QDateTime &dmAcceptanceTime,
	    enum Isds::Type::DmState dmMessageStatus);

	/*!
	 * @brief Insert/update message files into files table.
	 *
	 * @param[in] storeRawInDb Whether to store raw data in database file.
	 * @param[in] dmId Message identifier.
	 * @param[in] documents List of documents.
	 * @param[in] transaction True to create a transaction.
	 * @return True on success.
	 */
	bool insertOrUpdateMessageAttachments(bool storeRawInDb, qint64 dmId,
	    const QList<Isds::Document> &documents, bool transaction);

	/*!
	 * @brief Delete all files related to message with given id.
	 *
	 * @param[in] dmId Message identifier.
	 * @return True on success.
	 */
	bool deleteMessageAttachments(qint64 dmId);

	/*!
	 * @brief Insert/update message event into events table.
	 *
	 * @param[in] dmId  Message identifier.
	 * @param[in] event Event structure.
	 * @return True on success.
	 */
	bool insertOrUpdateMessageEvent(qint64 dmId, const Isds::Event &event);

	/*!
	 * @brief Insert or replace complete message data including raw data,
	 *     attachments, certificate info, etc.
	 *
	 * @note Emits messageInserted() when successful.
	 * @note This operation uses transactions and performs a roll-back on
	 *     failure.
	 *
	 * @param[in] storeRawInDb Whether to store raw data in database file.
	 * @param[in] msg Complete message.
	 * @param[in] msgDirect Message orientation.
	 * @param[in] verified Whether the message has been verified.
	 * @param[in] transaction True to create a transaction.
	 * @return True on success.
	 */
	bool insertOrReplaceCompleteMessage(bool storeRawInDb,
	    const Isds::Message &msg,
	    enum MessageDirection msgDirect, bool verified,
	    bool transaction);

	/*!
	 * @brief Return all IDs of messages without attachment.
	 *
	 * @return Message identifier list.
	 */
	QList<qint64> getAllMessageIDsWithoutAttach(void);

	/*!
	 * @brief Return all message IDs from database.
	 *
	 * @param[in] messageType Specifies sent or received messages.
	 * @return Message identifier list.
	 */
	QList<qint64> getAllMessageIDs(enum MessageType messageType);

	/*!
	 * @brief Check if complete message is in the database.
	 *
	 * @param[in] dmId Message identifier.
	 * @return True if complete message is in the database.
	 */
	bool isCompleteMessageInDb(qint64 dmId);

	/*!
	 * @brief Get base64 encoded raw message data.
	 *
	 * @param[in] dmId Message identifier.
	 * @return Empty byte array on error.
	 */
	QByteArray getCompleteMessageBase64(qint64 dmId);

	/*!
	 * @brief Get message data in DER (raw) format.
	 *
	 * @param[in] dmId Message identifier.
	 * @return Empty byte array on error.
	 */
	QByteArray getCompleteMessageRaw(qint64 dmId);

	/*!
	 * @brief Get base64-encoded delivery info from
	 *     raw_delivery_info_data table.
	 *
	 * @param[in] dmId  Message identifier.
	 * @return Empty byte array on error.
	 */
	QByteArray getDeliveryInfoBase64(qint64 dmId);

	/*!
	 * @brief Get delivery info data in DER (raw) format.
	 *
	 * @param[in] dmId Message identifier.
	 * @return Empty byte array on error.
	 */
	QByteArray getDeliveryInfoRaw(qint64 dmId);

	/*!
	 * @brief Return attachments related to given message.
	 *
	 * @param[in] dmId Message identifier.
	 * @return List of attachment structure.
	 */
	QList<Isds::Document> getMessageAttachments(qint64 dmId);

	/*!
	 * @brief Return list of attachment entries related to given message.
	 *
	 * @param[in] dmId Message identifier.
	 * @param[in] withContent True to include attachment content,
	 *                        false for no contant data.
	 * @return List of attachment entries.
	 */
	QList<AttachmentEntry> attachEntries(qint64 dmId, bool withContent);

	/*!
	 * @brief Get attachment binary content.
	 *
	 * @param[in] fileId File identifier.
	 * @return Return attachment binary content.
	 */
	QByteArray getAttachmentContent(qint64 fileId);

	/*!
	 * @brief Insert/update raw (DER) delivery info into
	 *     raw_delivery_info_data.
	 *
	 * @param[in] storeRawInDb Whether to store raw data in database file.
	 * @param[in] dmId Message identifier.
	 * @param[in] raw Raw (in DER format) delivery information.
	 * @return True on success.
	 */
	bool insertOrReplaceDeliveryInfoRaw(bool storeRawInDb, qint64 dmId,
	    const QByteArray &raw);

	/*!
	 * @brief Update information about author (sender).
	 *
	 * @param[in] dmId Message identifier.
	 * @param[in] msgAuthor Message author info.
	 * @return True on success.
	 */
	bool updateMessageAuthorInfo2(qint64 dmId,
	    const Isds::DmMessageAuthor &msgAuthor);

	/*!
	 * @brief Return hash data of message from db.
	 *
	 * @param[in] dmId  Message identifier.
	 * @return Message hash structure.
	 */
	const Isds::Hash getMessageHash(qint64 dmId);

	/*!
	 * @brief Delete all message records from db.
	 *
	 * @note Emits messageDataDeleted() when at least envelope data deleted.
	 *
	 * @param[in] dmId Message ID.
	 * @param[in] transaction True to create a transaction.
	 * @return True on success.
	 */
	bool msgsDeleteMessageData(qint64 dmId, bool transaction);

	/*!
	 * @brief Return some message items in order to export correspondence
	 *     to HTML.
	 *
	 * @param[in] dmId  Message identifier.
	 * @return String list containing sender, recipient, annotation, ...
	 *    Empty list is returned on error.
	 */
	QStringList getMessageForHtmlExport(qint64 dmId);

	/*!
	 * @brief Return some message items for export correspondence to csv.
	 *
	 * @param[in] dmId  Message identifier.
	 * @return String containing message status, message type, ...
	 *    Empty list is returned on error.
	 */
	QStringList getMessageForCsvExport(qint64 dmId);

	/*!
	 * @brief Set process state of received message.
	 *
	 * @note Emits changedProcessState() when stored value changed.
	 *
	 * @param[in] msgIds Message identifiers.
	 * @param[in] state Message state to be set.
	 * @return True if update/insert was successful.
	 */
	bool setMessagesProcessState(const QList<MsgId> &msgIds,
	    enum MessageProcessState state);

	/*!
	 * @brief Get process state of received message.
	 *
	 * @param[in]  dmId Message ID.
	 * @param[out] ok Set to true on success.
	 * @return Message processing state, UNSETTLED on error.
	 */
	enum MessageProcessState getMessageProcessState(qint64 dmId,
	    bool *ok = Q_NULLPTR);

	/*!
	 * @brief Returns time stamp in raw (DER) format.
	 *
	 * @param[in] dmId  Message identifier.
	 * @return Qualified time stamp in DER format.
	 *     Empty byte array on error.
	 */
	QByteArray getMessageTimestampRaw(qint64 dmId);

	/*!
	 * @brief Copy message data to account database from source database.
	 *
	 * @param[in] sourceDbPath Source db path.
	 * @param[in] dmId Message identifier.
	 * @param[in] transaction True to create a transaction.
	 * @return True if copy of message data was success.
	 */
	bool copyCompleteMsgDataToAccountDb(const QString &sourceDbPath,
	    qint64 dmId, bool transaction);

	/*!
	 * @brief Copy all messages correspond with
	 *        year and their records from tables into new db.
	 *
	 * @param[in] newDbFileName New database file name.
	 * @param[in] year Database year.
	 * @param[in] transaction True to create a transaction.
	 * @return Return success or fail.
	 */
	bool copyRelevantMsgsToNewDb(const QString &newDbFileName,
	   const QString &year, bool transaction);

	/*!
	 * @brief Return message author data in JSON format.
	 *
	 * @param[in] dmId Message identifier.
	 * @return Message author JSON data.
	 */
	QByteArray messageAuthorJsonStr(qint64 dmId);

	/*!
	 * @brief Copy some message data into new single database.
	 *
	 * @param[in] dstMsgDbPath Path to destination message database.
	 * @return True on success.
	 */
	bool copyMessageDataToSingleDb(const QString &dstMsgDbPath);

protected: /* These function are used from within a database container. */

	/*!
	 * @brief Return path to directory where associated files can be stored.
	 *
	 * @note The path may actually not exist if no files are stored there.
	 *
	 * @param[in] fileName Database file name.
	 * @return String containing a directory path.
	 */
	static
	QString _assocFileDirPath(QString fileName);

	/*!
	 * @brief Copy db.
	 *
	 * @param[in] newFileName New file path.
	 * @param[in] flag Can be NO_OPTIONS or CREATE_MISSING.
	 * @return True on success.
	 *
	 * @note The copy is continued to be used. Original is closed.
	 *     The copy is immediately opened.
	 */
	bool copyDb(const QString &newFileName, enum SQLiteDb::OpenFlag flag);

	/*!
	 * @brief Appends to received entry list data received from SQL query.
	 *
	 * @param[in,out] entryList List to add entries to.
	 * @param[in] query Query to read data from.
	 */
	static
	void appendRcvdEntryList(QList<RcvdEntry> &entryList, QSqlQuery &query);

	/*!
	 * @brief Return entries for all received messages.
	 *
	 * @return List of entries, empty list on failure.
	 */
	QList<RcvdEntry> msgsRcvdEntries(void);

	/*!
	 * @brief Return entries for received messages within past 90 days.
	 *
	 * @return List of entries, empty list on failure.
	 */
	QList<RcvdEntry> msgsRcvdEntriesWithin90Days(void);

	/*!
	 * @brief Return entries for received messages within given year.
	 *
	 * @param[in] year         Year number.
	 * @return List of entries, empty list on failure.
	 */
	QList<RcvdEntry> msgsRcvdEntriesInYear(const QString &year);

	/*!
	 * @brief Return list of years (strings) in database.
	 *
	 * @param[in] type    Whether to obtain sent or received messages.
	 * @param[in] sorting Sorting.
	 * @return List of years.
	 */
	QStringList msgsYears(enum MessageType type, enum Sorting sorting);

	/*!
	 * @brief Return list of years and number of messages in database.
	 *
	 * @param[in] type    Whether to obtain sent or received messages.
	 * @param[in] sorting Sorting.
	 * @return List of years and counts.
	 */
	QList< QPair<QString, int> > msgsYearlyCounts(enum MessageType type,
	    enum Sorting sorting);

	/*!
	 * @brief Return number of unread messages received within past 90
	 *     days.
	 *
	 * @param[in] type Whether to obtain sent or received messages.
	 * @return Number of unread messages, -1 on error.
	 */
	int msgsUnreadWithin90Days(enum MessageType type);

	/*!
	 * @brief Return number of unread received messages in year.
	 *
	 * @param[in] type Whether to obtain sent or received messages.
	 * @param[in] year Year number.
	 * @return Number of unread messages, -1 on error.
	 */
	int msgsUnreadInYear(enum MessageType type, const QString &year);

	/*!
	 * @brief Returns the number of messages which are in an unaccepted state.
	 *
	 * @note Unaccepted state is when (state < Isds::Type::MS_ACCEPTED_FICT).
	 *
	 * @param[in] type Whether to obtain sent or received messages.
	 * @return Number of messages in unaccepted state.
	 */
	int msgsUnaccepted(enum MessageType type);

	/*!
	 * @brief Appends to sent entry list data received from SQL query.
	 *
	 * @param[in,out] entryList List to add entries to.
	 * @param[in] query Query to read data from.
	 */
	static
	void appendSntEntryList(QList<SntEntry> &entryList, QSqlQuery &query);

	/*!
	 * @brief Return entries for all sent messages.
	 *
	 * @return List of entries, empty list on failure.
	 */
	QList<SntEntry> msgsSntEntries(void);

	/*!
	 * @brief Return entries for all sent messages within past 90 days.
	 *
	 * @return List of entries, empty list on failure.
	 */
	QList<SntEntry> msgsSntEntriesWithin90Days(void);

	/*!
	 * @brief Return entries for sent messages within given year.
	 *
	 * @param[in] year         Year number.
	 * @return List of entries, empty list on failure.
	 */
	QList<SntEntry> msgsSntEntriesInYear(const QString &year);

	/*!
	 * @brief Set message read locally for all received messages.
	 *
	 * @note Emits changedLocallyRead() when stored value changed.
	 *
	 * @param[in] read New read status.
	 * @return True on success.
	 */
	bool smsgdtSetAllReceivedLocallyRead(bool read = true);

	/*!
	 * @brief Set message read locally for received messages in given year.
	 *
	 * @note Emits changedLocallyRead() when stored value changed.
	 *
	 * @param[in] year Year number.
	 * @param[in] read New read status.
	 * @return True on success.
	 */
	bool smsgdtSetReceivedYearLocallyRead(const QString &year,
	    bool read = true);

	/*!
	 * @brief Set message read locally for recently received messages.
	 *
	 * @note Emits changedLocallyRead() when stored value changed.
	 *
	 * @param[in] read New read status.
	 * @return True on success.
	 */
	bool smsgdtSetWithin90DaysReceivedLocallyRead(bool read = true);

	/*!
	 * @brief Set process state of received messages.
	 *
	 * @note Emits changedLocallyRead() when stored value changed.
	 *
	 * @param[in] state  Message state to be set.
	 * @return True if operation successful.
	 */
	bool setReceivedMessagesProcessState(enum MessageProcessState state);

	/*!
	 * @brief Set process state of received messages in given year.
	 *
	 * @note Emits changedLocallyRead() when stored value changed.
	 *
	 * @param[in] year   Year.
	 * @param[in] state  Message state to be set.
	 * @return True if operation successful.
	 */
	bool smsgdtSetReceivedYearProcessState(const QString &year,
	    enum MessageProcessState state);

	/*!
	 * @brief Set process state of recently received messages.
	 *
	 * @note Emits changedLocallyRead() when stored value changed.
	 *
	 * @param[in] state  Message state to be set.
	 * @return True if operation successful.
	 */
	bool smsgdtSetWithin90DaysReceivedProcessState(
	    enum MessageProcessState state);

	/*!
	 * @brief Returns message identifier of message with given id number.
	 *
	 * @paran[in] dmId Message identification number.
	 * @return Message identifier containing the sought ID number.
	 *     If no such message is found then a message identifier
	 *     containing -1 dmId returned.
	 */
	MsgId msgsMsgId(qint64 dmId);

	/*!
	 * @brief Return contacts from message db.
	 *
	 * @return List of vectors containing recipientId, recipientName,
	 *     recipientAddress.
	 */
	QList<ContactEntry> uniqueContacts(void);

	/*!
	 * @brief Return all message IDs from database.
	 *
	 * @return MsgId set.
	 */
	QSet<MsgId> getAllMsgIds(void);

	/*!
	 * @brief Return all message IDs from database.
	 *
	 * @param[in] messageType Specifies sent or received messages.
	 * @return MsgId set.
	 */
	QSet<MsgId> getAllMsgIds(enum MessageType messageType);

	/*!
	 * @brief Get list of all message IDs corresponding with year.
	 *
	 * @return Return message ID list.
	 */
	QList<qint64> getAllMessageIDsEqualWithYear(const QString &year);

	/*!
	 * @brief Return list of message ids corresponding to given date
	 *     interval.
	 *
	 * @param[in] fromDate Start date. Use invalid value to ignore this limit.
	 * @param[in] toDate Stop date. Use invalid value to ignore this limit.
	 * @param[in] messageType Specifies sent or received messages.
	 * @return List of message ids. Empty list on error.
	 */
	QList<MsgId> msgsDateInterval(const QDate &fromDate,
	    const QDate &toDate, enum MessageType messageType);

	/*!
	 * @brief Message search according to envelope data.
	 *
	 * @param[in] envel Message envelope data to search for.
	 * @param[in] msgDirect Message orientation.
	 * @param[in] attachPhrase Phrase to search in attachment names.
	 * @param[in] logicalAnd Set to true if found messages should match all criteria,
	 *                       set to false if found messages should match any criteria.
	 * @param[in] fromDate Start date. Use invalid value to ignore this limit.
	 * @param[in] toDate Stop date. Use invalid value to ignore this limit.
	 * @return Found message data.
	 */
	QList<SoughtMsg> msgsSearch(const Isds::Envelope &envel,
	    enum MessageDirection msgDirect, const QString &attachPhrase,
	    bool logicalAnd, const QDate &fromDate, const QDate &toDate);

	/*!
	 * @brief Get message envelope data from id.
	 *
	 * @paran[in] dmId Message identification number.
	 * @return Message envelope data.
	 */
	SoughtMsg msgsGetMsgDataFromId(const qint64 dmId);

	/*!
	 * @brief Get message envelope data from identifiers.
	 *
	 * @paran[in] msgIds Message identifierd.
	 * @param[out] soughtMsgEntries Found entries assigned to sought keys.
	 * @return True on success.
	 */
	bool msgsGetMsgDataFromIds(const QList<MsgId> &msgIds,
	    QHash<MsgId, SoughtMsg> &soughtMsgEntries);

	/*!
	 * @brief Test if imported message is relevant to account db.
	 *
	 * @param[in] dmId  Message identifier.
	 * @param[in] databoxId  Databox ID where message should be imported.
	 * @return Message is relevant for import to db or not.
	 */
	bool isRelevantMsgForImport(qint64 dmId, const QString &databoxId);

	/*!
	 * @brief Query received messages within past 90 days.
	 *
	 * @param[in,out] query Query already assigned to a database.
	 * @return True on success.
	 */
	static
	bool msgsRcvdWithin90DaysQuery(QSqlQuery &query);

	/*!
	 * @brief Query received messages within past 90 days.
	 *
	 * @param[in,out] query Query already assigned to a database.
	 * @return True on success.
	 */
	static
	bool msgsSntWithin90DaysQuery(QSqlQuery &query);

	/*!
	 * @brief Returns list of tables.
	 *
	 * @return List of pointers to tables.
	 */
	virtual
	QList<class SQLiteTbl *> listOfTables(void) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Fixes some database states that may have been caused by buggy
	 *     application versions.
	 *
	 * @return True on success.
	 */
	virtual
	bool assureConsistency(void) Q_DECL_OVERRIDE;

signals:
	/*!
	 * @brief Emitted when the locally read flag changes.
	 *
	 * @param[in] msgIds Message identifiers.
	 * @param[in] read New locally read status.
	 */
	void changedLocallyRead(const QList<MsgId> &msgIds, bool read);

	/*!
	 * @brief Emitted when the process state changes.
	 *
	 * @param[in] msgIds Message identifiers.
	 * @param[in] state New message state.
	 */
	void changedProcessState(const QList<MsgId> &msgIds,
	    enum MessageDb::MessageProcessState state);

	/*!
	 * @brief Emitted when complete message written into the database.
	 *
	 * @param[in] msgId Message identifier.
	 * @param[in] direction Sent or received messages
	 *                      (enum MessageDirection).
	 */
	void messageInserted(const MsgId &msgId, int direction);

	/*!
	 * @brief Emitted when all message data deleted from database.
	 *
	 * @param[in] msgId Message identifier.
	 */
	void messageDataDeleted(const MsgId &msgId);

public:
	/*
	 * TODO -- Use static methods returning reference to static constant
	 * vector.
	 */
	static
	const QVector<QString> rcvdItemIds;
	static
	const QVector<QString> sntItemIds;
	static
	const QVector<QString> fileItemIds;

private:
	/*!
	 * @brief Returns verification date (in local time).
	 *
	 * @param[in] dmId  Message identifier.
	 * @return Message verification date. Invalid value is returned on
	 *     error.
	 */
	QDateTime msgsVerificationDate(qint64 dmId);

	/*!
	 * @brief Read data from supplementary message data table.
	 *
	 * @brief dmId Message identifier.
	 * @return Stored json document data. Returns empty document on error.
	 */
	QJsonDocument getMessageCustomData(qint64 dmId);

	friend class MessageDbSet;
	friend class MessageDbSingle;
};

/* QML passes its arguments via QVariant. */
Q_DECLARE_METATYPE(MessageDb::MessageProcessState)
