//-----------------------------------------------------------------------------
// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//-----------------------------------------------------------------------------

#ifndef __MQTT_H__
#define __MQTT_H__

#ifdef ESP8266
    #include <ESP8266WiFi.h>
#elif defined(ESP32)
    #include <WiFi.h>
#endif

#if defined(ESP32) && defined(F)
  #undef F
  #define F(sl) (sl)
#endif
#include <PubSubClient.h>
#include "defines.h"

class mqtt {
    public:
        mqtt() {
            mClient     = new PubSubClient(mEspClient);
            mAddressSet = false;

            memset(mDevName, 0, DEVNAME_LEN);
        }

        ~mqtt() { }

        void setup(mqttConfig_t *cfg, const char *devname) {
            DPRINTLN(DBG_VERBOSE, F("mqtt.h:setup"));
            mAddressSet = true;

            mCfg = cfg;
            snprintf(mDevName, DEVNAME_LEN,    "%s", devname);

            mClient->setServer(mCfg->broker, mCfg->port);
            mClient->setBufferSize(MQTT_MAX_PACKET_SIZE);
        }

        void setCallback(MQTT_CALLBACK_SIGNATURE){
            mClient->setCallback(callback);
        }

        void sendMsg(const char *topic, const char *msg) {
            //DPRINTLN(DBG_VERBOSE, F("mqtt.h:sendMsg"));
            char top[64];
            snprintf(top, 64, "%s/%s", mCfg->topic, topic);
            sendMsg2(top, msg, false);
        }

        void sendMsg2(const char *topic, const char *msg, boolean retained) {
            if(mAddressSet) {
                if(!mClient->connected())
                    reconnect();
                if(mClient->connected())
                    mClient->publish(topic, msg, retained);
            }
        }

        bool isConnected(bool doRecon = false) {
            //DPRINTLN(DBG_VERBOSE, F("mqtt.h:isConnected"));
            if(doRecon && !mClient->connected())
                reconnect();
            return mClient->connected();
        }

        void loop() {
            //DPRINT(F("m"));
            if(!mClient->connected())
                reconnect();
            mClient->loop();
        }

    private:
        void reconnect(void) {
            DPRINTLN(DBG_DEBUG, F("mqtt.h:reconnect"));
            DPRINTLN(DBG_DEBUG, F("MQTT mClient->_state ") + String(mClient->state()) );

            #ifdef ESP8266
                DPRINTLN(DBG_DEBUG, F("WIFI mEspClient.status ") + String(mEspClient.status()) );
            #endif

            boolean resub = false;
            if(!mClient->connected() && (millis() - lastReconnect) > MQTT_RECONNECT_DELAY ) {
                lastReconnect = millis();
                if(strlen(mDevName) > 0) {
                    // der Server und der Port müssen neu gesetzt werden, 
                    // da ein MQTT_CONNECTION_LOST -3 die Werte zerstört hat.
                    mClient->setServer(mCfg->broker, mCfg->port);
                    mClient->setBufferSize(MQTT_MAX_PACKET_SIZE);
                    if((strlen(mCfg->user) > 0) && (strlen(mCfg->pwd) > 0))
                        resub = mClient->connect(mDevName, mCfg->user, mCfg->pwd);
                    else
                        resub = mClient->connect(mDevName);
                                // ein Subscribe ist nur nach einem connect notwendig
                    if(resub) {
                        char topic[MQTT_TOPIC_LEN + 13 ]; // "/devcontrol/#" --> + 6 byte
                        // ToDo: "/devcontrol/#" is hardcoded 
                        snprintf(topic, MQTT_TOPIC_LEN + 13, "%s/devcontrol/#", mCfg->topic);
                        DPRINTLN(DBG_INFO, F("subscribe to ") + String(topic));
                        mClient->subscribe(topic); // subscribe to mTopic + "/devcontrol/#"
                    }
                }
            }
        }

        WiFiClient mEspClient;
        PubSubClient *mClient;
        
        bool mAddressSet;
        mqttConfig_t *mCfg;
        char mDevName[DEVNAME_LEN];
        unsigned long lastReconnect = 0;
};

#endif /*__MQTT_H_*/