/*
 * Copyright (C) 2014-2024 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 <QHash>
#include <QNetworkReply>
#include <QObject>
#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>

class QUrl; /* Forward declaration. */
class QUrlQuery; /* Forward declaration. */

class MatomoReporterPrivate;
/*!
 * @brief Holds internal account identifier.
 */
class MatomoReporter : public QObject {
	Q_OBJECT
	Q_DECLARE_PRIVATE(MatomoReporter)

private:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
	Q_DISABLE_COPY_MOVE(MatomoReporter)
#else /* < Qt-5.13 */
	Q_DISABLE_COPY(MatomoReporter)
#endif /* >= Qt-5.13 */

public:
	explicit MatomoReporter(QObject *parent = Q_NULLPTR);
	~MatomoReporter(void);

	/*!
	 * @brief Tracker URL.
	 *
	 * @note E.g. 'http://localhost/matomo',
	 *     without trailing '/matomo.php' or '/piwik.php'.
	 */
	const QUrl &trackerUrl(void) const;
	void setTrackerUrl(const QUrl &u);
#ifdef Q_COMPILER_RVALUE_REFS
	void setTrackerUrl(QUrl &&u);
#endif /* Q_COMPILER_RVALUE_REFS */

	/*!
	 * @brief Value of 'idsite' GET parameter.
	 */
	int siteId(void) const;
	void setSiteId(int si);

	/*!
	 * @brief Whether to generate a random 'rand' GET parameter value.
	 */
	bool useRand(void) const;
	void setUseRand(bool ur);

	/*!
	 * @brief Used to construct the 'url' GET parameter.
	 */
	const QString &webAppName(void) const;
	void setWebAppName(const QString &an);
#ifdef Q_COMPILER_RVALUE_REFS
	void setWebAppName(QString &&an);
#endif /* Q_COMPILER_RVALUE_REFS */

	/*!
	 * @brief Value of '_id' and 'cid' GET parameters.
	 *
	 * @note Must be exactly 16 characters long hexadecimal string.
	 */
	const QString &clientId(void) const;
	void setClientId(const QString &cid);
#ifdef Q_COMPILER_RVALUE_REFS
	void setClientId(QString &&cid);
#endif /* Q_COMPILER_RVALUE_REFS */

	/*!
	 * @brief Value of 'uid' GET parameter.
	 */
	const QString &userId(void) const;
	void setUserId(const QString &uid);
#ifdef Q_COMPILER_RVALUE_REFS
	void setUserId(QString &&uid);
#endif /* Q_COMPILER_RVALUE_REFS */

	/*!
	 * @brief Sends a ping request together with custom dimensions.
	 *
	 * @note Emits requestFinished() when successful.
	 */
	void sendPing(void);

	/*!
	 * @brief Sends a visit request together with custom dimensions and
	 *     custom visit variables.
	 *
	 * @note Emits requestFinished() when successful.
	 *
	 * @param[in] path Visit path. Used to construct the 'url' GET parameter.
	 * @paran[in] actionName Value of 'action_name' GET parameter.
	 */
	void sendVisit(const QString &path, const QString &actionName = QString());

	/*!
	 * @brief Sends an event request together with custom dimensions.
	 *
	 * @note Emits requestFinished() when successful.
	 *
	 * @param[in] path Visit path. Used to construct the 'url' GET parameter.
	 * @param[in] eventCategory Event category. Value of 'e_c' GET parameter.
	 * @param[in] eventAction Event action. Value of 'e_a' GET parameter.
	 * @param[in] eventName Event name. Value of 'e_n' GET parameter.
	 * @param[in] eventValue Event value. Value of 'e_v' GET parameter.
	 */
	void sendEvent(const QString &path, const QString &eventCategory,
	    const QString &eventAction, const QString &eventName = QString(),
	    int eventValue = 0);

	/*!
	 * @brief Sets a custom dimension.
	 *
	 * @param[in] id Dimension identifier.
	 * @param[in] val Dimension value.
	 */
	void setCustomDimension(int id, const QString &val);
#ifdef Q_COMPILER_RVALUE_REFS
	void setCustomDimension(int id, QString &&val);
#endif /* Q_COMPILER_RVALUE_REFS */

	/*!
	 * @brief Set custom variables.
	 *
	 * @param[in] key The name of the custom variable to set (key).
	 * @param[in] val The value to set for this custom variable.
	 */
	void setCustomVisitVariables(const QString &key, const QString &val);
#ifdef Q_COMPILER_RVALUE_REFS
	void setCustomVisitVariables(const QString &key, QString &&val);
#endif /* Q_COMPILER_RVALUE_REFS */

Q_SIGNALS:
	/*!
	 * @brief Emitted when any request finished.
	 *
	 * @param[in] url Used URL.
	 */
	void requestFinished(const QUrl &url);

private Q_SLOTS:
	void onReplyFinished(QNetworkReply *reply);
	void onReplyError(QNetworkReply::NetworkError code);

private:
	/*!
	 * @brief Gathers basic information about the client host system.
	 */
	void gatherAndSetInfo(void);

	const QString &screenResolution(void) const;
	const QString &userAgent(void) const;
	const QString &language(void) const;
	const QHash<int, QString> &customDimentsions(void) const;
	const QHash<QString, QString> &customVariables(void) const;

	/*!
	 * @brief Prepares the common query elements for the tracking request.
	 */
	QUrlQuery prepareUrlQuery(const QString &path) const;

	/*!
	 * @brief Creates a string encoding custom visit variables.
	 */
	QString collectVisitVariables(void) const;

	/*!
	 * @brief Performs a GET request.
	 *
	 * @param[in] url Complete URL to be sent.
	 */
	void sendGetRequest(const QUrl &url);

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