//
// Created by magino on 11. 3. 2020.
//

#ifndef MQTTCLIENT_H
#define MQTTCLIENT_H

#include <mqtt/client.h>
#include <configuration/MqttCfg.h>
#include "UserCallback.h"

class RfidMqttClient {
private:
    mqtt::client _client;
    UserCallback _cb;
    mqtt::connect_options _connOpts;
    MqttCfg _cfg;
public:
    RfidMqttClient(MqttCfg& cfg): _cfg(cfg), _client(cfg.brokerIpAddress, cfg.clientId) {
        std::cout << "Initializing MQTT client." << std::endl;

        _client.set_callback(_cb);
        _connOpts.set_keep_alive_interval(20);
        _connOpts.set_clean_session(true);

        //std::cout << "OK" << std::endl;
    }

    bool Connect() {
        try {
            std::cout << "Connecting." << std::endl;
            mqtt::connect_response connResp = _client.connect(_connOpts);
            //std::cout << "OK" << std::endl;
        }
        catch (const mqtt::persistence_exception& exc) {
            std::cerr << "Persistence Error: " << exc.what() << " [" << exc.get_reason_code() << "]" << std::endl;
            return false;
        }
        catch (const mqtt::exception& exc) {
            std::cerr << exc.what() << std::endl;
            return false;
        }
        return true;
    }

    bool TryReconnect() {
        int attempt = 0;
        std::cout << std::endl << "Reconnecting... Attempt:[" << ++attempt << "]" << std::endl;
        while (!_client.is_connected()) {
            try {
                _client.reconnect();
                std::cout << "Reconnecting successfull!"<< std::endl;
                return true;
            }
            catch (const mqtt::exception&) {
                std::this_thread::sleep_for(std::chrono::seconds(2));
            }
        }
        return false;
    }

    bool Disconnect() {
        try{
            std::cout << std::endl << "Disconnecting." << std::endl;
            _client.disconnect();
            //std::cout << "OK" << std::endl << std::endl;
        } catch (const mqtt::exception& exc) {
            std::cerr << exc.what() << std::endl;
            return false;
        }
        return true;
    }

    bool SendPtrMessage(const std::string& data) {
        try {
            //std::cout << "Sending message..." << std::endl;
            //std::cout << data << std::endl;

            auto pubmsg = mqtt::make_message(_cfg.rfidWriteDataTopic, data);
            pubmsg->set_qos(_cfg.qos);
            _client.publish(pubmsg);

            //std::cout << "...OK" << std::endl;
        } catch (const mqtt::exception& exc) {
            std::cerr << exc.what() << std::endl;
            return false;
        }
        return true;
    }

    bool SendMessage(const std::string& data) {
        mqtt::client tempclient(_cfg.brokerIpAddress, _cfg.clientId + "temp" );
        tempclient.connect(_connOpts);

        //std::cout << "\nSending message..." << std::endl;
        //std::cout << data << std::endl;

        tempclient.publish(mqtt::message(_cfg.rfidWriteDataTopic, data, _cfg.qos, false));
        //std::cout << "OK" << std::endl;
        tempclient.disconnect();
        return true;
    }

    mqtt::client& GetClient() { return _client; }
};

#endif //MQTTCLIENT_H