/*
 * 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 <QDateTime>
#include <QJsonArray>
#include <QJsonValue>
#include <QString>
#include <QStringList>

#include "src/datovka_shared/isds/types.h"

class QJsonObject; /* Forward declaration. */

namespace Json {

	/*!
	 * @brief JSON conversion helper functions.
	 */
	namespace Helper {

		enum PresenceFlag {
			ACCEPT_VALID = 0x00, /*!< Value must be present valid and non-null. */
			ACCEPT_NULL = 0x01, /*!< Accept null values. */
			ACCEPT_MISSING = 0x02, /*!< Accept missing values. */
			ACCEPT_INVALID = 0x04 /*!< Accept invalid values. */
		};
		Q_DECLARE_FLAGS(PresenceFlags, PresenceFlag)

		/*!
		 * @brief Reads a JSON object which comprises the document.
		 *
		 * @param[in]  json JSON data.
		 * @param[out] jsonObj JSON object from the document.
		 * @return True on success, false else.
		 */
		bool readRootObject(const QByteArray &json,
		    QJsonObject &jsonObj);

		/*!
		 * @brief Reads a JSON aray which comprises the document.
		 *
		 * @param[in]  json JSON data.
		 * @param[out] jsonArr JSON array from the document.
		 * @return True on success, false else.
		 */
		bool readRootArray(const QByteArray &json,
		    QJsonArray &jsonArr);

		/*!
		 * @brief Searches for a value on JSON object.
		 *
		 * @param[in]  jsonObject Object to search in.
		 * @param[in]  key Key to search for.
		 * @param[out] val Found value.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True if key found, false else.
		 */
		bool readValue(const QJsonObject &jsonObj,
		    const QString &key, QJsonValue &val, int flags,
		    const QJsonValue &defVal = QJsonValue());

		/*!
		 * @brief Converts supplied JSON value to a boolean value.
		 *
		 * @param[in]  jsonVal JSON value.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept null and/or invalid values.
		 * @param[in]  defVal Default value if null or invalid.
		 * @return True on success, false else.
		 */
		bool valToBool(const QJsonValue &jsonVal, bool &val,
		    PresenceFlags flags, bool defVal = false);

		/*!
		 * @brief Reads a boolean value from supplied JSON object.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[in]  key Key identifying the string.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True on success, false else.
		 */
		bool readBool(const QJsonObject &jsonObj, const QString &key,
		    bool &val, PresenceFlags flags, bool defVal = false);

		/*!
		 * @brief Converts supplied JSON value to a boolean value.
		 *
		 * @param[in]  jsonVal JSON value.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept null and/or invalid values.
		 * @param[in]  defVal Default value if null or invalid.
		 * @return True on success, false else.
		 */
		bool valToNilBool(const QJsonValue &jsonVal,
		    enum Isds::Type::NilBool &val, PresenceFlags flags,
		    enum Isds::Type::NilBool defVal = Isds::Type::BOOL_NULL);

		/*!
		 * @brief Reads a boolean value from supplied JSON object.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[in]  key Key identifying the string.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True on success, false else.
		 */
		bool readNilBool(const QJsonObject &jsonObj, const QString &key,
		    enum Isds::Type::NilBool &val, PresenceFlags flags,
		    enum Isds::Type::NilBool defVal = Isds::Type::BOOL_NULL);

		/*!
		 * @brief Converts supplied JSON value to an integer value.
		 *
		 * @param[in]  jsonVal JSON value.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept null and/or invalid values.
		 * @param[in]  defVal Default value if null or invalid.
		 * @return True on success, false else.
		 */
		bool valToInt(const QJsonValue &jsonVal, int &val,
		    PresenceFlags flags, int defVal = -1);

		/*!
		 * @brief Reads an integer value from supplied JSON object.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[in]  key Key identifying the string.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True on success, false else.
		 */
		bool readInt(const QJsonObject &jsonObj, const QString &key,
		    int &val, PresenceFlags flags, int defVal = -1);

		/*!
		 * @brief Converts supplied JSON value to a qint64 value.
		 *
		 * @note The number is stored as a string because of the
		 *     limitations occurring when storing qint64 in a QJsonValue.
		 *
		 * @param[in]  jsonVal JSON value.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept null and/or invalid values.
		 * @param[in]  defVal Default value if null or invalid.
		 * @return True on success, false else.
		 */
		bool valToQint64String(const QJsonValue &jsonVal, qint64 &val,
		    PresenceFlags flags, qint64 defVal = -1);

		/*!
		 * @brief Reads a qint64 value from supplied JSON object.
		 *
		 * @note The number is stored as a string because of the
		 *     limitations occurring when storing qint64 in a QJsonValue.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[in]  key Key identifying the string.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True on success, false else.
		 */
		bool readQint64String(const QJsonObject &jsonObj,
		    const QString &key, qint64 &val, PresenceFlags flags,
		    qint64 defVal = -1);

		/*!
		 * @brief Reads Base64-encoded-string data from supplied JSON
		 *     object.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[in]  key Key identifying the string.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True on success, false else.
		 */
		bool readBase64DataString(const QJsonObject &jsonObj,
		    const QString &key, QByteArray &val, PresenceFlags flags,
		    const QByteArray &defVal = QByteArray());

		/*!
		 * @brief Converts supplied JSON value to a string.
		 *
		 * @param[in]  jsonVal JSON value.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept null and/or invalid values.
		 * @param[in]  defVal Default value if null or invalid.
		 * @return True on success, false else.
		 */
		bool valToString(const QJsonValue &jsonVal, QString &val,
		    PresenceFlags flags, const QString &defVal = QString());

		/*!
		 * @brief Reads a string value from supplied JSON object.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[in]  key Key identifying the string.
		 * @param[out] val Value to be stored.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True on success, false else.
		 */
		bool readString(const QJsonObject &jsonObj, const QString &key,
		    QString &val, PresenceFlags flags,
		    const QString &defVal = QString());

		/*!
		 * @brief Reads an array value from supplied JSON object.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[in]  key Key identifying the string.
		 * @param[out] arr Array to be stored.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True on success, false else.
		 */
		bool readArray(const QJsonObject &jsonObj, const QString &key,
		    QJsonArray &arr, PresenceFlags flags,
		    const QJsonArray &defVal = QJsonArray());

		/*!
		 * @brief Reads a string list from supplied JSON object.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[in]  key Key identifying the string list.
		 * @param[out] val Values to be stored.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True on success, false else.
		 */
		bool readStringList(const QJsonObject &jsonObj,
		    const QString &key, QStringList &val, PresenceFlags flags,
		    const QStringList &defVal = QStringList());

		/*!
		 * @brief Reads a date and time value from an ISO 8601 encoded
		 *     string.
		 *
		 * @param[in]  jsonObj JSON object.
		 * @param[in]  key Key identifying the string list.
		 * @param[out] val Values to be stored.
		 * @param[in]  flags Whether to accept missing and/or null values.
		 * @param[in]  defVal Default value if missing, null or invalid.
		 * @return True on success, false else.
		 */
		bool readQDateTimeString(const QJsonObject &jsonObj,
		    const QString &key, QDateTime &val, PresenceFlags flags,
		    const QDateTime &defVal = QDateTime());

		/*!
		 * @brief Writes JSON data to a byte array.
		 *
		 * @param[in] jsonVal JSON value to be written.
		 * @param[in] indent Whenter to enable or disable indentation.
		 * @return Byte array containing JSON data.
		 */
		QByteArray toJsonData(const QJsonValue &jsonVal, bool indent);

		/*!
		 * @brief Converts JSON document to indented data.
		 *
		 * @param[in]  json JSON data.
		 * @param[in]  indent Whether to enable or disable indentation.
		 * @param[out] ok Set to false on error.
		 * @retunr Byte array containing JSON data if input JSON data
		 *     could be read.
		 */
		QByteArray reindent(const QByteArray &json, bool indent,
		    bool *ok = Q_NULLPTR);

	}

}
