/*
 * Copyright (C) 2014-2022 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 <QByteArray>
#include <QList>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
#  include <memory> /* ::std::unique_ptr */
#else /* < Qt-5.12 */
#  include <QScopedPointer>
#endif /* >= Qt-5.12 */
#include <QString>

#include "src/datovka_shared/json/object.h"

class QDateTime; /* Forward declaration. */
class QIODevice; /* Forward declaration. */
class QJsonObject; /* Forward declaration. */

namespace Json {

	class BackupPrivate;
	/*!
	 * @brief Backup description.
	 */
	class Backup : public Object {
		Q_DECLARE_PRIVATE(Backup)

	public:
		enum Type {
			BUT_UNKNOWN, /*!< Unknown backup type. */
			BUT_BACKUP, /*!< Downloaded content backup. */
			BUT_TRANSFER /*!< Configuration transfer. */
		};

		class AppInfoPrivate;
		/*!
		 * @brief Basic application information.
		 */
		class AppInfo : public Object {
			Q_DECLARE_PRIVATE(AppInfo)

		public:
			AppInfo(void);
			AppInfo(const AppInfo &other);
#ifdef Q_COMPILER_RVALUE_REFS
			AppInfo(AppInfo &&other) Q_DECL_NOEXCEPT;
#endif /* Q_COMPILER_RVALUE_REFS */
			~AppInfo(void);

			AppInfo(const QString &n, const QString &var,
			    const QString &ver);

			AppInfo &operator=(const AppInfo &other) Q_DECL_NOTHROW;
#ifdef Q_COMPILER_RVALUE_REFS
			AppInfo &operator=(AppInfo &&other) Q_DECL_NOTHROW;
#endif /* Q_COMPILER_RVALUE_REFS */

			bool operator==(const AppInfo &other) const;
			bool operator!=(const AppInfo &other) const;

			static
			void swap(AppInfo &first, AppInfo &second) Q_DECL_NOTHROW;

			bool isNull(void) const;
			bool isValid(void) const;

			const QString &appName(void) const;
			void setAppName(const QString &n);
#ifdef Q_COMPILER_RVALUE_REFS
			void setAppName(QString &&n);
#endif /* Q_COMPILER_RVALUE_REFS */

			const QString &appVariant(void) const;
			void setAppVariant(const QString &va);
#ifdef Q_COMPILER_RVALUE_REFS
			void setAppVariant(QString &&va);
#endif /* Q_COMPILER_RVALUE_REFS */

			const QString &appVersion(void) const;
			void setAppVersion(const QString &ve);
#ifdef Q_COMPILER_RVALUE_REFS
			void setAppVersion(QString &&ve);
#endif /* Q_COMPILER_RVALUE_REFS */

			/*!
			 * @brief Creates an instance from supplied JSON data.
			 *
			 * @param[in]  jsonVal JSON object value.
			 * @param[out] ok Set to true on success.
			 * @return Invalid value on error a valid structure else.
			 */
			static
			AppInfo fromJsonVal(const QJsonValue &jsonVal, bool *ok = Q_NULLPTR);

			/*!
			 * @brief Writes content of JSON object.
			 *
			 * @note Unspecified values are omitted.
			 *
			 * @param[out] jsonValue JSON value to write to.
			 * @return True on success.
			 */
			virtual
			bool toJsonVal(QJsonValue &jsonVal) const Q_DECL_OVERRIDE;

		private:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
			::std::unique_ptr<AppInfoPrivate> d_ptr;
#else /* < Qt-5.12 */
			QScopedPointer<AppInfoPrivate> d_ptr;
#endif /* >= Qt-5.12 */
		};

		class ChecksumPrivate;
		/*!
		 * @brief Describes data checksum.
		 */
		class Checksum : public Object {
			Q_DECLARE_PRIVATE(Checksum)

		public:
			/*!
			 * @brief Supported hash algorithms.
			 */
			enum Algorithm {
				ALG_UNKNOWN = 0,
				ALG_SHA512
			};

			Checksum(void);
			Checksum(const Checksum &other);
#ifdef Q_COMPILER_RVALUE_REFS
			Checksum(Checksum &&other) Q_DECL_NOEXCEPT;
#endif /* Q_COMPILER_RVALUE_REFS */
			~Checksum(void);

			Checksum(enum Algorithm a, const QByteArray &v);
#ifdef Q_COMPILER_RVALUE_REFS
			Checksum(enum Algorithm a, QByteArray &&v);
#endif /* Q_COMPILER_RVALUE_REFS */

			Checksum &operator=(const Checksum &other) Q_DECL_NOTHROW;
#ifdef Q_COMPILER_RVALUE_REFS
			Checksum &operator=(Checksum &&other) Q_DECL_NOTHROW;
#endif /* Q_COMPILER_RVALUE_REFS */

			bool operator==(const Checksum &other) const;
			bool operator!=(const Checksum &other) const;

			static
			void swap(Checksum &first, Checksum &second) Q_DECL_NOTHROW;

			bool isNull(void) const;
			bool isValid(void) const;

			enum Algorithm algorithm(void) const;
			void setAlgorithm(enum Algorithm a);

			const QByteArray &value(void) const;
			void setValue(const QByteArray &v);
#ifdef Q_COMPILER_RVALUE_REFS
			void setValue(QByteArray &&v);
#endif /* Q_COMPILER_RVALUE_REFS */

			/*!
			 * @brief Compute checksum from supplied data.
			 *
			 * @param[in] data Data to compute the checksum from.
			 * @param[in] algorithm Selected algorithm.
			 * @return Valid checksum object on success,
			 *     null value on error.
			 */
			static
			Checksum computeChecksum(const QByteArray &data,
			    enum Algorithm algorithm);

			/*!
			 * @brief Compute checksum from supplied data.
			 *
			 * @param[in] device I/O device.
			 * @param[in] algorithm Selected algorithm.
			 * @return Valid checksum object on success,
			 *     null value on error.
			 */
			static
			Checksum computeChecksum(QIODevice *device,
			    enum Algorithm algorithm);

			/*!
			 * @brief Creates an instance from supplied JSON data.
			 *
			 * @param[in]  jsonVal JSON object value.
			 * @param[out] ok Set to true on success.
			 * @return Invalid value on error a valid structure else.
			 */
			static
			Checksum fromJsonVal(const QJsonValue &jsonVal, bool *ok = Q_NULLPTR);

			/*!
			 * @brief Writes content of JSON object.
			 *
			 * @note Unspecified values are omitted.
			 *
			 * @param[out] jsonObj JSON object to write to.
			 * @return True on success.
			 */
			virtual
			bool toJsonVal(QJsonValue &jsonVal) const Q_DECL_OVERRIDE;

		private:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
			::std::unique_ptr<ChecksumPrivate> d_ptr;
#else /* < Qt-5.12 */
			QScopedPointer<ChecksumPrivate> d_ptr;
#endif /* >= Qt-5.12 */
		};

		class FilePrivate;
		/*!
		 * @brief Describes an ordinary file backup.
		 */
		class File : public Object {
			Q_DECLARE_PRIVATE(File)

		public:
			File(void);
			File(const File &other);
#ifdef Q_COMPILER_RVALUE_REFS
			File(File &&other) Q_DECL_NOEXCEPT;
#endif /* Q_COMPILER_RVALUE_REFS */
			~File(void);

			File(const QString &f, const Checksum &c, qint64 s);
#ifdef Q_COMPILER_RVALUE_REFS
			File(QString &&f, Checksum &&c, qint64 s);
#endif /* Q_COMPILER_RVALUE_REFS */

			File &operator=(const File &other) Q_DECL_NOTHROW;
#ifdef Q_COMPILER_RVALUE_REFS
			File &operator=(File &&other) Q_DECL_NOTHROW;
#endif /* Q_COMPILER_RVALUE_REFS */

			bool operator==(const File &other) const;
			bool operator!=(const File &other) const;

			static
			void swap(File &first, File &second) Q_DECL_NOTHROW;

			bool isNull(void) const;
			bool isValid(void) const;

			const QString &fileName(void) const;
			void setFileName(const QString &f);
#ifdef Q_COMPILER_RVALUE_REFS
			void setFileName(QString &&f);
#endif /* Q_COMPILER_RVALUE_REFS */

			const Checksum &checksum(void) const;
			void setChecksum(const Checksum &c);
#ifdef Q_COMPILER_RVALUE_REFS
			void setChecksum(Checksum &&c);
#endif /* Q_COMPILER_RVALUE_REFS */

			/* Size in bytes. Returns -1 if size is unknown. */
			qint64 size(void) const;
			void setSize(qint64 s);

			/*!
			 * @brief Creates an instance from supplied JSON data.
			 *
			 * @param[in]  jsonObj JSON object.
			 * @param[out] ok Set to true on success.
			 * @return Invalid value on error a valid structure else.
			 */
			static
			File fromJsonVal(const QJsonValue &jsonVal, bool *ok = Q_NULLPTR);

			/*!
			 * @brief Writes content of JSON object.
			 *
			 * @note Unspecified values are omitted.
			 *
			 * @param[out] jsonObj JSON object to write to.
			 * @return True on success.
			 */
			virtual
			bool toJsonVal(QJsonValue &jsonVal) const Q_DECL_OVERRIDE;

		private:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
			::std::unique_ptr<FilePrivate> d_ptr;
#else /* < Qt-5.12 */
			QScopedPointer<FilePrivate> d_ptr;
#endif /* >= Qt-5.12 */
		};

		class MessageDbPrivate;
		/*!
		 * @brief Describes a message database backup entry.
		 */
		class MessageDb : public Object {
			Q_DECLARE_PRIVATE(MessageDb)

		public:
			MessageDb(void);
			MessageDb(const MessageDb &other);
#ifdef Q_COMPILER_RVALUE_REFS
			MessageDb(MessageDb &&other) Q_DECL_NOEXCEPT;
#endif /* Q_COMPILER_RVALUE_REFS */
			~MessageDb(void);

			MessageDb(bool t, const QString &a, const QString &b,
			    const QString &u, const QString &s, const QList<File> &f);

			MessageDb &operator=(const MessageDb &other) Q_DECL_NOTHROW;
#ifdef Q_COMPILER_RVALUE_REFS
			MessageDb &operator=(MessageDb &&other) Q_DECL_NOTHROW;
#endif /* Q_COMPILER_RVALUE_REFS */

			bool operator==(const MessageDb &other) const;
			bool operator!=(const MessageDb &other) const;

			static
			void swap(MessageDb &first, MessageDb &second) Q_DECL_NOTHROW;

			bool isNull(void) const;
			bool isValid(void) const;

			bool testing(void) const;
			void setTesting(bool t);

			const QString &accountName(void) const;
			void setAccountName(const QString &a);
#ifdef Q_COMPILER_RVALUE_REFS
			void setAccountName(QString &&a);
#endif /* Q_COMPILER_RVALUE_REFS */

			const QString &boxId(void) const;
			void setBoxId(const QString &b);
#ifdef Q_COMPILER_RVALUE_REFS
			void setBoxId(QString &&b);
#endif /* Q_COMPILER_RVALUE_REFS */

			const QString &username(void) const;
			void setUsername(const QString &u);
#ifdef Q_COMPILER_RVALUE_REFS
			void setUsername(QString &&u);
#endif /* Q_COMPILER_RVALUE_REFS */

			const QString &subdir(void) const;
			void setSubdir(const QString &s);
#ifdef Q_COMPILER_RVALUE_REFS
			void setSubdir(QString &&s);
#endif /* Q_COMPILER_RVALUE_REFS */

			const QList<File> &files(void) const;
			void setFiles(const QList<File> &f);
#ifdef Q_COMPILER_RVALUE_REFS
			void setFiles(QList<File> &&f);
#endif /* Q_COMPILER_RVALUE_REFS */

			/*!
			 * @brief Creates an instance from supplied JSON data.
			 *
			 * @param[in]  jsonObj JSON object.
			 * @param[out] ok Set to true on success.
			 * @return Invalid structure on error a valid structure else.
			 */
			static
			MessageDb fromJsonVal(const QJsonValue &jsonVal, bool *ok = Q_NULLPTR);

			/*!
			 * @brief Writes content of JSON object.
			 *
			 * @note Unspecified values are omitted.
			 *
			 * @param[out] jsonObj JSON object to write to.
			 * @return True on success.
			 */
			virtual
			bool toJsonVal(QJsonValue &jsonVal) const Q_DECL_OVERRIDE;

		private:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
			::std::unique_ptr<MessageDbPrivate> d_ptr;
#else /* < Qt-5.12 */
			QScopedPointer<MessageDbPrivate> d_ptr;
#endif /* >= Qt-5.12 */
		};

		Backup(void);
		Backup(const Backup &other);
#ifdef Q_COMPILER_RVALUE_REFS
		Backup(Backup &&other) Q_DECL_NOEXCEPT;
#endif /* Q_COMPILER_RVALUE_REFS */
		~Backup(void);

		Backup &operator=(const Backup &other) Q_DECL_NOTHROW;
#ifdef Q_COMPILER_RVALUE_REFS
		Backup &operator=(Backup &&other) Q_DECL_NOTHROW;
#endif /* Q_COMPILER_RVALUE_REFS */

		bool operator==(const Backup &other) const;
		bool operator!=(const Backup &other) const;

		static
		void swap(Backup &first, Backup &second) Q_DECL_NOTHROW;

		bool isNull(void) const;
		bool isValid(void) const;
		bool isValidMobile(void) const;

		const QDateTime &dateTime(void) const;
		void setDateTime(const QDateTime &t);
#ifdef Q_COMPILER_RVALUE_REFS
		void setDateTime(QDateTime &&t);
#endif /* Q_COMPILER_RVALUE_REFS */

		enum Type type(void) const;
		void setType(enum Type t);

		const AppInfo &appInfo(void) const;
		void setAppInfo(const AppInfo &a);
#ifdef Q_COMPILER_RVALUE_REFS
		void setAppInfo(AppInfo &&a);
#endif /* Q_COMPILER_RVALUE_REFS */

		const QList<MessageDb> &messageDbs(void) const;
		void setMessageDbs(const QList<MessageDb> &m);
#ifdef Q_COMPILER_RVALUE_REFS
		void setMessageDbs(QList<MessageDb> &&m);
#endif /* Q_COMPILER_RVALUE_REFS */

		const File &accountDb(void) const;
		void setAccountDb(const File &a);
#ifdef Q_COMPILER_RVALUE_REFS
		void setAccountDb(File &&a);
#endif /* Q_COMPILER_RVALUE_REFS */

		const File &tagDb(void) const;
		void setTagDb(const File &t);
#ifdef Q_COMPILER_RVALUE_REFS
		void setTagDb(File &&t);
#endif /* Q_COMPILER_RVALUE_REFS */

		const QList<File> &files(void) const;
		void setFiles(const QList<File> &f);
#ifdef Q_COMPILER_RVALUE_REFS
		void setFiles(QList<File> &&f);
#endif /* Q_COMPILER_RVALUE_REFS */

		/*!
		 * @brief Return a message database for specified username.
		 *
		 * @param[in] username Non-empty username.
		 * @param[in] testing Whether this is a testing account.
		 * @return Null message database entry if no such username found.
		 */
		const MessageDb &messageDb(const QString &username, bool testing) const;

		/*!
		 * @brief Creates an instance from supplied JSON document.
		 *
		 * @param[in]  json JSON data.
		 * @param[out] ok Set to true on success.
		 * @return Invalid structure on error a valid structure else.
		 */
		static
		Backup fromJsonData(const QByteArray &json, bool *ok = Q_NULLPTR);

		/*!
		 * @brief Creates an instance from supplied JSON data.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[out] ok Set to true on success.
		 * @return Invalid structure on error a valid structure else.
		 */
		static
		Backup fromJsonVal(const QJsonValue &jsonVal, bool *ok = Q_NULLPTR);

		/*!
		 * @brief Writes content of JSON object.
		 *
		 * @note Unspecified values are omitted.
		 *
		 * @param[out] jsonObj JSON object to write to.
		 * @return True on success.
		 */
		virtual
		bool toJsonVal(QJsonValue &jsonVal) const Q_DECL_OVERRIDE;

	private:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
		::std::unique_ptr<BackupPrivate> d_ptr;
#else /* < Qt-5.12 */
		QScopedPointer<BackupPrivate> d_ptr;
#endif /* >= Qt-5.12 */
	};

}
