/*
 * 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 <QDateTime>
#include <QMap>
#include <QHash>
#include <QObject>
#include <QString>
#include <QStringList>

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

#define DB_UNDERSCORES "___"
#define DB_MARKER_PROD "0" /* Production environment data. */
#define DB_MARKER_TEST "1" /* Testing environment data. */
#define DB_SUFFIX ".db"
#define PRIMARY_KEY_RE "[^_]+"
#define SINGLE_FILE_SEC_KEY ""
#define YEARLY_SEC_KEY_RE "[0-9][0-9][0-9][0-9]"
#define YEARLY_SEC_KEY_INVALID MessageDb::invalidYearName

/*!
 * Organises database files according to secondary keys.
 */
class MessageDbSet : public QObject, private QMap<QString, MessageDb *> {
	Q_OBJECT

public:
	/*!
	 * Flags used when accessing (creating new) database files.
	 */
	enum AccessFlag {
		NO_OPTIONS = 0x00, /*!< No option specified. */
		TESTING_ACCOUNT = 0x01, /*!< Create a testing database. */
		CREATE_FILE = 0x02, /*!< Create file if does not exist. */
		CHECK_QUICK = 0x04, /*!< Perform a quick database check. */
		CHECK_INTEGRITY = 0x08 /*!< Perform a full integrity check. */
	};
	Q_DECLARE_FLAGS(AccessFlags, AccessFlag)

	/*!
	 * Error codes returned when accessing/creating new database file.
	 */
	enum Error {
		MDSERR_OK = 0, /*!< No error. */
		MDSERR_MISSFILE = 1, /*!< Database file does not exist. */
		MDSERR_NOTAFILE = 2, /*!< Database file is not a file. */
		MDSERR_ACCESS = 3, /*!< Error reading/writing database file. */
		MDSERR_CREATE = 4, /*!< Error creating database file. */
		MDSERR_DATA = 5, /*!< Data corrupted or not a database file. */
		MDSERR_MULTIPLE = 6 /*!< Multiple differently organised databases reside in the same location. */
	};

	/*!
	 * Style of database organisation.
	 */
	enum Organisation {
		DO_UNKNOWN = 0, /*!< Unsupported type. */
		DO_SINGLE_FILE, /*!< Content is stored within a single file. */
		DO_YEARLY /*!< Content is split according to delivery year. */
	};

	/*!
	 * How to access/create databases.
	 */
	enum CreationManner {
		CM_MUST_EXIST, /*!<
		                * At least one database file must already exist.
		                */
		CM_CREATE_EMPTY_CURRENT, /*!<
		                          * Create at least one empty file
		                          * according to current time if no
		                          * file exists.
		                          */
		CM_CREATE_ON_DEMAND /*!<
		                     * The first file is going to be created on
		                     * database write.
		                     */
	};

	/*!
	 * What to do with the abandoned files.
	 */
	enum AbandonManner {
		AM_LEAVE, /*!< Leave abandoned files. */
		AM_DELETE /*!< Delete abandoned files. */
	};

	/*!
	 * When gathering database file sizes.
	 */
	enum SizeComputation {
		SC_SUM, /*!< Compute the sum of all available database files. */
		SC_LARGEST /*!< Return the size of the largest database file. */
	};

	/* Constructor is private. */

	/*!
	 * @brief Destructor.
	 */
	~MessageDbSet(void);

	/*!
	 * @brief Opens a location.
	 *     Files in new location are opened if any exist.
	 *
	 * @note The new location must exist.
	 *
	 * @param[in] newLocDir New location directory.
	 * @paran[in] organisation Organisation type.
	 * @param[in] creationManner How to treat files when opening database.
	 * @param[in] abandonManner How to treat closed files.
	 * @reurn True if database was opened.
	 */
	bool openLocation(const QString &newLocDir,
	   enum Organisation organisation, enum CreationManner creationManner,
	   enum AbandonManner abandonManner);

	/*!
	 * @brief Creates a copy of the current database into a given new
	 *     directory.
	 *
	 * @param[in] newLocDir New location directory.
	 * @return True if database was copied and re-opened.
	 */
	bool copyToLocation(const QString &newLocDir);

	/*!
	 * @brief Move message database into a new directory.
	 *
	 * @param[in] newLocDir New location directory.
	 * @return True if database was moved and re-opened.
	 */
	bool moveToLocation(const QString &newLocDir);

	/*!
	 * @brief Re-open a new empty database file.
	 *     The new location is emptied.
	 *
	 * @note The CM_MUST_EXIST value of `manner` parameter is ignored.
	 *
	 * @param[in] newLocDir New location directory.
	 * @paran[in] organisation Organisation type.
	 * @param[in] creationManner How to treat files when opening database.
	 * @param[in] abandonManner How to treat closed files.
	 * @return True if database was re-opened.
	 */
	bool reopenLocation(const QString &newLocDir,
	    enum Organisation organisation, enum CreationManner creationManner,
	    enum AbandonManner abandonManner);

	/*!
	 * @brief Delete associated message db files in currently used location.
	 *
	 * @return True on success.
	 */
	bool deleteLocation(void);

	/*!
	 * @brief Computes the secondary key from supplied date according to
	 *     container settings.
	 *
	 * @param[in] time Time to be converted.
	 * @return Secondary key or null string on error.
	 */
	QString secondaryKey(const QDateTime &time) const;

	/*!
	 * @brief Computes the secondary key fitting the supplied date range
	 *     according to container settings.
	 *
	 * @param[in] fromTime Start time, ignored when invalid.
	 * @param[in] toTime End time, ignored when converted.
	 * @return List of secondary keys or empty list on error. Returns all
	 *     secondary keys when both limits are invalid.
	 */
	QStringList secondaryKeys(const QDateTime &fromTime,
	    const QDateTime &toTime) const;

	/*!
	 * @brief Access already existent message database.
	 *
	 * @param secondaryKey Secondary key.
	 * @return Message database or zero pointer if database does not exist.
	 */
	MessageDb *constAccessMessageDb(const QDateTime &deliveryTime) const;

	/*!
	 * @brief Accesses message database file matching the secondary key.
	 *
	 * @param secondaryKey Secondary key.
	 * @param writeNew     Whether the database should be written if missing.
	 * @return Message database or zero pointer on error.
	 */
	MessageDb *accessMessageDb(const QDateTime &deliveryTime, bool writeNew);

	/*!
	 * @brief Returns location where database files reside.
	 *
	 * @return Path do database directory, empty string if database is in memory.
	 */
	QString locationDirectory(void) const;

	/*!
	 * @brief Returns list of file paths where the database files reside.
	 *
	 * @note If the database is stored in memory, then a list containing
	 *     a single string indicating memory location is returned.
	 *
	 * @return List of file locations.
	 */
	QStringList fileNames(void) const;

	/*!
	 * @brief Return list of directory paths associated files reside.
	 *
	 * @note If the database is stored in memory, then a list containing
	 *     a single string indicating memory location is returned.
	 *
	 * @return List of directory locations.
	 */
	QStringList assocFileDirNames(void) const;

	/*!
	 * @brief Change the primary key for the database.
	 *
	 * @param[in] newPrimaryKey Primary key, usually the username.
	 */
	bool changePrimaryKey(const QString &newPrimaryKey);

	/*!
	 * @brief Return list of existing database files in location that match
	 *    the criteria.
	 *
	 * @param[in] locDir Location directory.
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] organisation Database organisation type,
	 *                         use DO_UNKNOWN if you don't care.
	 * @param[in] testing Set true if account is in testing environment.
	 */
	static
	QStringList fileNamesAvailable(const QString &locDir,
	    const QString &primaryKey, enum Organisation organisation,
	    bool testing);

	/*!
	 * @brief Return list of existing associated directories in location
	 *    that match the criteria.
	 *
	 * @param[in] locDir Location directory.
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] organisation Database organisation type,
	 *                         use DO_UNKNOWN if you don't care.
	 * @param[in] testing Set true if account is in testing environment.
	 */
	static
	QStringList assocFileDirNamesAvailable(const QString &locDir,
	    const QString &primaryKey, enum Organisation organisation,
	    bool testing);

	/*!
	 * @brief Returns the database organisation.
	 * @return Database organisation.
	 */
	Organisation organisation(void) const;

	/*!
	 * @brief Returns the year identifier from supplied time.
	 *
	 * @param time Time to be converted.
	 * @return Year identifier, or invalid identifier if time invalid.
	 */
	static
	QString yearFromDateTime(const QDateTime &time);

	/*!
	 * @brief Creates a new object.
	 *
	 * @param[in] locDir Directory that holds the database files.
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] testing True if this is a testing account.
	 * @param[in] organisation How to organise the database files.
	 * @param[in] connectionPrefix Prefix of the connection name.
	 * @param[in] onDisk Use false to create database in memory.
	 * @param[in] manner How to treat files when opening database.
	 * @return Pointer to new container or Q_NULLPTR on error.
	 */
	static
	MessageDbSet *createNew(const QString &locDir,
	    const QString &primaryKey, bool testing,
	    enum Organisation organisation, bool onDisk,
	    const QString &connectionPrefix, enum CreationManner manner);

	/*!
	 * @brief Get the organisation type of the database.
	 *
	 * @param[in] locDir     Directory where the database resides.
	 * @param[in] primaryKey Primary key.
	 * @param[in] testing    True if a testing account.
	 * @return    Organisation type.
	 */
	static
	enum Organisation dbOrganisation(const QString &locDir,
	    const QString &primaryKey, bool testing);

	/*!
	 * @brief Returns the secondary key from a file name.
	 *
	 * @param[in] fileName     File name (without path).
	 * @param[in] organisation Organisation type.
	 * @return Secondary key, null string on error.
	 */
	static
	QString secondaryKeyFromFileName(const QString &fileName,
	    enum Organisation organisation);

	/*!
	 * @brief Returns the database file names stored in the location.
	 *
	 * @note If DO_UNKNOWN is passed via `organisation` then all possible
	 *     matching file names are returned.
	 *
	 * @param[in] locDir       Directory where the database should reside.
	 * @param[in] primaryKey   Primary key.
	 * @param[in] testing      True if a testing account.
	 * @paran[in] organisation Organisation type.
	 * @param[in] fileOnly     True if only files desired (ignores directories etc.).
	 * @return List of database files in the location.
	 */
	static
	QStringList existingDbFileNamesInLocation(const QString &locDir,
	    const QString &primaryKey, bool testing,
	    enum Organisation organisation, bool filesOnly);

	/*!
	 * @brief Construct key from primary and secondary key.
	 *
	 * @param[in] primaryKey   Primary key.
	 * @param[in] secondaryKey Secondary key.
	 * @paran[on] organisation Organisation type.
	 * @return Key or null string on error.
	 */
	static
	QString constructKey(const QString &primaryKey,
	    const QString &secondaryKey, enum Organisation organisation);

	/*!
	 * @brief Check existing databases for basic faults.
	 *
	 * @param[in] primaryKey ISDS user name.
	 * @param[in] locDir Directory where to store the file.
	 * @param[in] flags Flags to be passed.
	 * @param[in] onDisk Use false to reference database in memory.
	 * @return Error code.
	 */
	static
	enum Error checkExistingDbFile(const QString &locDir,
	    const QString &primaryKey, AccessFlags flags, bool onDisk);

	/*!
	 * @brief Returns file name according to primary and secondary key.
	 *
	 * @param[in] locDir       Directory where the database should reside.
	 * @param[in] primaryKey   Primary key.
	 * @param[in] secondaryKey Secondary key.
	 * @param[in] testing      True if a testing account.
	 * @paran[on] organisation Organisation type.
	 * @return Full file path or empty string on error.
	 */
	static
	QString constructDbFileName(const QString &locDir,
	    const QString &primaryKey, const QString &secondaryKey,
	    bool testing, enum Organisation organisation);

	/*!
	 * @brief Check database filename validity.
	 *
	 * @param[in]  fileName Database filename.
	 * @param[out] dbUserName Username.
	 * @paran[out] dbYear Year entry if exists, null string else.
	 * @paran[out] dbTestingFlag True when account is testing, false else.
	 * @paran[out] errMsg Error message which can be displayed to the user.
	 * @return True if database filename is correct.
	 */
	static
	bool isValidDbFileName(const QString &fileName, QString &dbUserName,
	    QString &dbYear, bool &dbTestingFlag, QString &errMsg);

Q_SIGNALS:
	/*!
	 * @brief Emitted when a database file has been added into the container.
	 *
	 * @note Added database may or may not be already opened.
	 *
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] testing Whether the database is related to a testing account.
	 */
	void madeAvailable(const QString &primaryKey, bool testing);

	/*!
	 * @brief Emitted when a database file is opened.
	 *
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] testing Whether the database is related to a testing account.
	 */
	void opened(const QString &primaryKey, bool testing);

	/*!
	 * @brief Emitted when the attempt to open a database file fails.
	 *
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] testing Whether the database is related to a testing account.
	 * @param[in] fileName Database file name.
	 */
	void openingFailed(const QString &primaryKey, bool testing,
	    const QString &fileName);

	/*!
	 * @brief Emitted when the locally read flag changes.
	 *
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] testing Whether the database is related to a testing account.
	 * @param[in] msgIds Message identifiers.
	 * @param[in] read New locally read status.
	 */
	void changedLocallyRead(const QString &primaryKey, bool testing,
	    const QList<MsgId> &msgIds, bool read);

	/*!
	 * @brief Emitted when the process state changes.
	 *
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] testing Whether the database is related to a testing account.
	 * @param[in] msgIds Message identifiers.
	 * @param[in] state New message state.
	 */
	void changedProcessState(const QString &primaryKey, bool testing,
	    const QList<MsgId> &msgIds, enum MessageDb::MessageProcessState state);

	/*!
	 * @brief Emitted when complete message written into the database.
	 *
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] testing Whether the database is related to a testing account.
	 * @param[in] msgId Message identifier.
	 * @param[in] direction Sent or received messages
	 *                      (enum MessageDirection).
	 */
	void messageInserted(const QString &primaryKey, bool testing,
	    const MsgId &msgId, int direction);

	/*!
	 * @brief Emitted when all message data deleted from database.
	 *
	 * @param[in] primaryKey Primary key, usually the username.
	 * @param[in] testing Whether the database is related to a testing account.
	 * @param[in] msgId Message identifier.
	 */
	void messageDataDeleted(const QString &primaryKey, bool testing,
	    const MsgId &msgId);

private Q_SLOTS:
	/*!
	 * @brief This slot must be connected to every database created inside
	 *     this container.
	 *
	 * @param[in] fileName Database file name.
	 */
	void watchOpened(const QString &fileName);

	/*!
	 * @brief This slot must be connected to every database created inside
	 *     this container.
	 *
	 * @param[in] fileName Database file name.
	 */
	void watchOpeningFailed(const QString &fileName);

	/*!
	 * @brief This slot must be connected to every database created inside
	 *     this container.
	 *
	 * @param[in] msgIds Message identifiers.
	 * @param[in] read New locally read status.
	 */
	void watchChangedLocallyRead(const QList<MsgId> &msgIds, bool read);

	/*!
	 * @brief This slot must be connected to every database created inside
	 *     this container.
	 *
	 * @param[in] msgIds Message identifiers.
	 * @param[in] state New message state.
	 */
	void watchChangedProcessState(const QList<MsgId> &msgIds,
	    enum MessageDb::MessageProcessState state);

	/*!
	 * @brief This slot must be connected to every database created inside
	 *     this container.
	 *
	 * @param[in] msgId Message identifier.
	 * @param[in] direction Sent or received messages
	 *                      (enum MessageDirection).
	 */
	void watchMessageInserted(const MsgId &msgId, int direction);

	/*!
	 * @brief This slot must be connected to every database created inside
	 *     this container.
	 *
	 * @param[in] msgId Message identifier.
	 */
	void watchMessageDataDeleted(const MsgId &msgId);

private:
	/*!
	 * @brief Creates a database set.
	 *
	 * @param[in] locDir Directory that holds the database files.
	 * @param[in] primaryKey Primary key, usually the user name.
	 * @param[in] testing True if this is a testing account.
	 * @param[in] organisation How to organise the database files.
	 * @param[in] onDisk Use false to create database in memory.
	 * @param[in] connectionPrefix Prefix of the connection name.
	 */
	MessageDbSet(const QString &locDir, const QString &primaryKey,
	    bool testing, enum Organisation organisation, bool onDisk,
	    const QString &connectionPrefix);

	/*!
	 * @brief Accesses message database file matching the secondary key.
	 *
	 * @param[in] secondaryKey Secondary key.
	 * @param[in] create       Whether to create the file.
	 * @return Message database or Q_NULLPTR on error.
	 */
	MessageDb *_accessMessageDb(const QString &secondaryKey, bool create);

	/*!
	 * @brief Deletes the related database file/directories stored in the
	 *     location.
	 *
	 * @note If DO_UNKNOWN is passed via `organisation` then all possible
	 *     matching files and directories are deleted.
	 *
	 * @param[in] locDir Directory where the database should reside.
	 * @param[in] primaryKey Primary key.
	 * @param[in] testing True if a testing account.
	 * @paran[in] organisation Organisation type.
	 * @return List of database files in the location.
	 */
	static
	void deleteRelatedInLocation(const QString &locDir,
	    const QString &primaryKey, bool testing,
	    enum Organisation organisation);

	/*!
	 * @brief Test given file.
	 *
	 * @param[in] filePath Full file path.
	 * @param[in] flags Flags.
	 * @return Error code.
	 */
	static
	enum Error checkGivenDbFile(const QString &filePath, AccessFlags flags);

	const QString m_connectionPrefix; /*!< Used for connection naming. */
	QString m_primaryKey; /*!< Used for accessing the database. */
	const bool m_testing; /*!< Whether those are a testing databases. */
	QString m_locDir; /*!< Directory where the files reside. */
	enum Organisation m_organisation; /*!< How the database is organised. */
	const bool m_onDisk; /*!< True if database is generated within a file system, false if it is held within memory. */

public: /* Database function that have been delegated to the container. */
	/*!
	 * @brief Call vacuum on all managed databases.
	 */
	bool vacuum(void);

	/*!
	 * @brief Backs up the database into a file.
	 *
	 * @param[in] dirName Directory name where to store the files into.
	 *                    The name must contain slashes ('/') as directory
	 *                    separators.
	 * @return False on error.
	 */
	bool backup(const QString &dirName);

	/*!
	 * @brief Return sum of sizes of all managed databases;
	 *
	 * @param[in] forceAccess Whether to connect all databases before
	 *                        querying their size.
	 *
	 * @return Size in bytes or -1 on error.
	 */
	qint64 dbSize(bool forceAccess = false);

	/*!
	 * @brief Returns the size of the underlying database files.
	 *
	 * @param sc Determines the way how to compute the database size.
	 * @return Database size. Returns -1 if database resides in memory.
	 *     Returns 0 if not files exist or have not been written yet.
	 */
	qint64 fileSize(enum SizeComputation sc) const;

	/*!
	 * @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) const;

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

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

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

	/*!
	 * @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 MessageDb::MessageType type,
	    enum Sorting sorting) const;

	/*!
	 * @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 MessageDb::MessageType type, enum Sorting sorting) const;

	/*!
	 * @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 MessageDb::MessageType type) const;

	/*!
	 * @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, -2 if database is not opened.
	 */
	int msgsUnreadInYear(enum MessageDb::MessageType type,
	    const QString &year) const;

	/*!
	 * @brief Returns the number of messages which are in an unaccepted state.
	 *
	 * @param[in] type Whether to obtain sent or received messages.
	 * @return Number of messages in unaccepted state.
	 */
	int msgsUnaccepted(enum MessageDb::MessageType type) const;

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

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

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

	/*!
	 * @brief Set message read locally for all received messages.
	 *
	 * @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.
	 *
	 * @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.
	 *
	 * @param[in] read  New read status.
	 * @return True on success.
	 */
	bool smsgdtSetWithin90DaysReceivedLocallyRead(bool read = true);

	/*!
	 * @brief Set process state of received messages.
	 *
	 * @param[in] state  Message state to be set.
	 * @return True if operation successful.
	 */
	bool setReceivedMessagesProcessState(
	    enum MessageDb::MessageProcessState state);

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

	/*!
	 * @brief Set process state of recently received messages.
	 *
	 * @param[in] state  Message state to be set.
	 * @return True if operation successful.
	 */
	bool smsgdtSetWithin90DaysReceivedProcessState(
	    enum MessageDb::MessageProcessState state);

	/*!
	 * @brief Returns message identifier of message with given id number.
	 *
	 * @note This method is quite expensive as it scans all database files,
	 *     don't use it if not absolutely necessary.
	 *
	 * @paran[in] dmId Message identification number.
	 * @return Message identifier containing the seeked id number.
	 *     If no such message is found then a message identifier
	 *     containing -1 dmId returned.
	 */
	MsgId msgsMsgId(qint64 dmId) const;

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

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

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

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

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

	/*!
	 * @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 MessageDb::MessageType messageType) const;

	/*!
	 * @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<MessageDb::SoughtMsg> msgsSearch(const Isds::Envelope &envel,
	    enum MessageDirection msgDirect, const QString &attachPhrase,
	    bool logicalAnd, const QDate &fromDate, const QDate &toDate) const;

	/*!
	 * @brief Get message envelope data from id.
	 *
	 * @return message data for message id.
	 */
	MessageDb::SoughtMsg msgsGetMsgDataFromId(const qint64 msgId) const;

	bool msgsGetMsgDataFromIds(const QList<MsgId> &msgIds,
	    QHash<MsgId, MessageDb::SoughtMsg> &soughtMsgEntries) const;

private:
	/*!
	 * @brief Return list of seconday keys that may be involed in last 90
	 *     days.
	 *
	 * @return List of keys.
	 */
	QStringList _yrly_secKeysIn90Days(void) const;

	inline bool _sf_isVodz(qint64 dmId, bool *ok) const;
	inline bool _yrly_isVodz(qint64 dmId, bool *ok) const;

	inline QList<MessageDb::RcvdEntry> _sf_msgsRcvdEntries(void) const;
	inline QList<MessageDb::RcvdEntry> _yrly_msgsRcvdEntries(void) const;

	inline QList<MessageDb::RcvdEntry> _sf_msgsRcvdEntriesWithin90Days(void) const;
	static
	inline QList<MessageDb::RcvdEntry> _yrly_2dbs_msgsRcvdEntriesWithin90Days(MessageDb &db0, MessageDb &db1);
	inline QList<MessageDb::RcvdEntry> _yrly_msgsRcvdEntriesWithin90Days(void) const;

	inline QList<MessageDb::RcvdEntry> _sf_msgsRcvdEntriesInYear(const QString &year) const;
	inline QList<MessageDb::RcvdEntry> _yrly_msgsRcvdEntriesInYear(const QString &year) const;

	inline QStringList _sf_msgsYears(enum MessageDb::MessageType type, enum Sorting sorting) const;
	inline QStringList _yrly_msgsYears(enum MessageDb::MessageType type, enum Sorting sorting) const;

	inline QList< QPair<QString, int> > _sf_msgsYearlyCounts(enum MessageDb::MessageType type, enum Sorting sorting) const;
	inline QList< QPair<QString, int> > _yrly_msgsYearlyCounts(enum MessageDb::MessageType type, enum Sorting sorting) const;

	inline int _sf_msgsUnreadWithin90Days(enum MessageDb::MessageType type) const;
	inline int _yrly_msgsUnreadWithin90Days(enum MessageDb::MessageType type) const;

	inline int _sf_msgsUnreadInYear(enum MessageDb::MessageType type, const QString &year) const;
	inline int _yrly_msgsUnreadInYear(enum MessageDb::MessageType type, const QString &year) const;

	inline int _sf_msgsUnaccepted(enum MessageDb::MessageType type) const;
	inline int _yrly_msgsUnaccepted(enum MessageDb::MessageType type) const;

	inline QList<MessageDb::SntEntry> _sf_msgsSntEntries(void) const;
	inline QList<MessageDb::SntEntry> _yrly_msgsSntEntries(void) const;

	inline QList<MessageDb::SntEntry> _sf_msgsSntEntriesWithin90Days(void) const;
	static
	inline QList<MessageDb::SntEntry> _yrly_2dbs_msgsSntEntriesWithin90Days(MessageDb &db0, MessageDb &db1);
	inline QList<MessageDb::SntEntry> _yrly_msgsSntEntriesWithin90Days(void) const;

	inline QList<MessageDb::SntEntry> _sf_msgsSntEntriesInYear(const QString &year) const;
	inline QList<MessageDb::SntEntry> _yrly_msgsSntEntriesInYear(const QString &year) const;

	inline bool _sf_smsgdtSetAllReceivedLocallyRead(bool read);
	inline bool _yrly_smsgdtSetAllReceivedLocallyRead(bool read);

	inline bool _sf_smsgdtSetReceivedYearLocallyRead(const QString &year, bool read);
	inline bool _yrly_smsgdtSetReceivedYearLocallyRead(const QString &year, bool read);

	inline bool _sf_smsgdtSetWithin90DaysReceivedLocallyRead(bool read);
	inline bool _yrly_smsgdtSetWithin90DaysReceivedLocallyRead(bool read);

	inline bool _sf_msgSetAllReceivedProcessState(enum MessageDb::MessageProcessState state);
	inline bool _yrly_msgSetAllReceivedProcessState(enum MessageDb::MessageProcessState state);

	inline bool _sf_smsgdtSetReceivedYearProcessState(const QString &year, enum MessageDb::MessageProcessState state);
	inline bool _yrly_smsgdtSetReceivedYearProcessState(const QString &year, enum MessageDb::MessageProcessState state);

	inline bool _sf_smsgdtSetWithin90DaysReceivedProcessState(enum MessageDb::MessageProcessState state);
	inline bool _yrly_smsgdtSetWithin90DaysReceivedProcessState(enum MessageDb::MessageProcessState state);

	MsgId _sf_msgsMsgId(qint64 dmId) const;
	MsgId _yrly_msgsMsgId(qint64 dmId) const;

	inline QList<MessageDb::ContactEntry> _sf_uniqueContacts(void) const;
	inline QList<MessageDb::ContactEntry> _yrly_uniqueContacts(void) const;

	inline QSet<MsgId> _sf_getAllMsgIds(void) const;
	inline QSet<MsgId> _yrly_getAllMsgIds(void) const;

	inline QSet<MsgId> _sf_getAllMsgIds(enum MessageDb::MessageType messageType) const;
	inline QSet<MsgId> _yrly_getAllMsgIds(enum MessageDb::MessageType messageType) const;

	inline QList<qint64> _sf_getAllMessageIDsWithoutAttach(void) const;
	inline QList<qint64> _yrly_getAllMessageIDsWithoutAttach(void) const;

	inline QList<qint64> _sf_getAllMessageIDs(enum MessageDb::MessageType messageType) const;
	inline QList<qint64> _yrly_getAllMessageIDs(enum MessageDb::MessageType messageType) const;

	inline QList<MsgId> _sf_msgsDateInterval(const QDate &fromDate, const QDate &toDate, enum MessageDb::MessageType messageType) const;
	inline QList<MsgId> _yrly_msgsDateInterval(const QDate &fromDate, const QDate &toDate, enum MessageDb::MessageType messageType) const;

	inline QList<MessageDb::SoughtMsg> _sf_msgsSearch(const Isds::Envelope &envel, enum MessageDirection msgDirect, const QString &attachPhrase, bool logicalAnd, const QDate &fromDate, const QDate &toDate) const;
	inline QList<MessageDb::SoughtMsg> _yrly_msgsSearch(const Isds::Envelope &envel, enum MessageDirection msgDirect, const QString &attachPhrase, bool logicalAnd, const QDate &fromDate, const QDate &toDate) const;

	inline MessageDb::SoughtMsg _sf_msgsGetMsgDataFromId(const qint64 msgId) const;
	inline MessageDb::SoughtMsg _yrly_msgsGetMsgDataFromId(const qint64 msgId) const;

	inline bool _sf_msgsGetMsgDataFromIds(const QList<MsgId> &msgIds, QHash<MsgId, MessageDb::SoughtMsg> &soughtMsgEntries) const;
	inline bool _yrly_msgsGetMsgDataFromIds(const QList<MsgId> &msgIds, QHash<MsgId, MessageDb::SoughtMsg> &soughtMsgEntries) const;
};
