//! \~\file pimqttclient.h
//! \~\ingroup MQTT
//! \~\brief
//! \~english
//! \~russian
/*
	PIP - Platform Independent Primitives
	MQTT Client
	Ivan Pelipenko peri4ko@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef pimqttclient_h
#define pimqttclient_h

#include "piliterals_time.h"
#include "pimqtttypes.h"
#include "pip_mqtt_client_export.h"
#include "pithreadpoolworker.h"


namespace PIMQTT {


class PIP_MQTT_CLIENT_EXPORT Client: public PIObject {
	PIOBJECT(PIMQTT::Client)

public:
	Client();
	virtual ~Client();

	//! \~english Request handler used by registered routes and fallback processing.
	//! \~russian Обработчик запроса, используемый зарегистрированными маршрутами и fallback-обработкой.
	using MessageFunction = std::function<void(const PIMQTT::MessageConst &)>;

	void setConnectTimeout(PISystemTime time) { connect_timeout = time; }

	void connect(const PIString & address, const PIString & client, const PIString & username = {}, const PIString & password = {});
	void disconnect();

	void subscribe(const PIString & topic, MessageFunction functor, QoS qos = QoS::Level1);

	template<typename T>
	void
	subscribe(const PIString & topic, T * o, PIMQTT::MessageMutable (T::*function)(const PIMQTT::MessageConst &), QoS qos = QoS::Level1) {
		subscribe(topic, [o, function](const PIMQTT::MessageConst & m) { return (o->*function)(m); }, qos);
	}

	void unsubscribe(const PIString & topic);
	void unsubscribeAll();

	void publish(const PIString & topic, const PIByteArray & msg, QoS qos = QoS::Level0);
	void publish(const MessageConst & msg);

	bool isConnecting() const { return m_status == Connecting; }
	bool isConnected() const { return m_status == Connected; }

	PIStringList usedTopics() const;

	EVENT0(connected);
	EVENT1(disconnected, PIMQTT::Error, code);
	EVENT1(receivedUnhandled, PIMQTT::MessageConst, message);

	struct Endpoint;

private:
	NO_COPY_CLASS(Client)

	PRIVATE_DECLARATION(PIP_MQTT_CLIENT_EXPORT)

	enum Status {
		Idle,
		Connecting,
		Connected,
	};

	struct ConnectInfo {
		PIString address;
		PIString clientID;
		PIString username;
		PIString password;
	};
	struct Subscribe {
		PIString topic;
		MessageFunction functor;
		QoS qos;
	};

	void mqtt_connectionLost();
	void mqtt_deliveryComplete(int token);
	void mqtt_messageArrived(MessageMutable & msg);

	PIString registerSubscribe(const Subscribe & sub);
	PIString unregisterSubscribe(const PIString & mqtt_topic);
	void unregisterAll();

	void connectInternal(const ConnectInfo & ci);
	void disconnectInternal();
	void publishInternal(const MessageConst & m);
	void subscribeInternal(const Subscribe & sub);
	void unsubscribeInternal(const PIString & mqtt_topic);
	void destroy();
	void changeStatus(Status s);

	void run();

	// from HTTP format
	static PIString convertTopic2MQTT(const PIString & topic);

	// from MQTT format
	static PIString convertTopic2HTTP(const PIString & topic);

	std::atomic_int m_status      = {Idle};
	std::atomic_bool is_destoying = {false};
	PISystemTime connect_timeout  = 10_s;
	PIThreadPoolWorker * worker   = nullptr;
};


}; // namespace PIMQTT


#endif
