#ifndef __MQTT_PLUGIN__
#define __MQTT_PLUGIN__

#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STD_FUNCTION

#include <WiFiClient.h>
#include <PubSubClient.h>
#include <Plugin.h>
#include "utils/print.h"
#include "MqttConfig.h"

using namespace std;
using namespace std::placeholders;

class MqttPlugin : public Plugin
{
public:
  PubSubClient *client;
   Scheduler* scheduler;
   
  MqttPlugin(MqttConfig cfg, bool bindUp = true, bool bindDown = true);

  /**
     * Connects to queue and triggers connect and process task.
     * Binds downstream handler to MQTT client.
     */
  void activate(Scheduler *scheduler);

private:
  WiFiClient wifiClient;
  Task connectTask;
  Task processTask;
  bool subscribed = false;
  bool bindUpstream = false;
  bool bindDownstream = false;
  String brokerHost;
  int brokerPort;
  String clientName;
  String topicRoot;
  String user;
  String pass;
  int connectTries = 0;
  int maxConnectTries = 5; // TODO add to config

  void applyConfig(MqttConfig cfg);
  void applyConfigFromFile(const char *fileName);
  void enableConnectTask(Scheduler *scheduler);
  void enableProcessTask(Scheduler *scheduler);
  /**
     * Connects to MQTT server and subscribes all topics available in the eventChannel 
     * to the corresponding topic on the remote queue by prefixing the topic with inRootTopic.
     * Creates shadow subscriptions of every local topic to propagate messages to the remote queue via upstreamHandler.
     */
  virtual void connect();

  /**
     * The upstream handler is bound to every local subscription.
     * It passes the message to the remote queue by prefixing the outRootTopic.
     */
  virtual void upstreamHandler(String topic, String msg);

  /**
     * The downstream handler is bound to the remote counterpart of local subscriptions,
     * prefixed with inRootTopic.
     * Everything after the prefix is used as local topic and dispatched to local subscriptions.
     */
  virtual void downstreamHandler(char *topic, uint8_t *payload, unsigned int length);

  virtual void loop();
};

#endif