#ifdef MQTT
#include <esp8266.h>
#include "cgiwifi.h"
#include "config.h"
#include "mqtt.h"


#ifdef MQTTCLIENT_DBG
#define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0)
#else
#define DBG(format, ...) do { } while(0)
#endif

MQTT_Client mqttClient; // main mqtt client used by meta-id

static MqttCallback connected_cb;
static MqttCallback disconnected_cb;
static MqttCallback published_cb;
static MqttDataCallback data_cb;

#define MQTT_STATUS_INTERVAL (60*1000)

static ETSTimer mqttStatusTimer;

void ICACHE_FLASH_ATTR
mqttConnectedCb(MQTT_Client* client) {
  DBG("MQTT Client: Connected\n");
  //MQTT_Subscribe(client, "system/time", 0); // handy for testing
  if (connected_cb)
    connected_cb(client);
}

void ICACHE_FLASH_ATTR
mqttDisconnectedCb(MQTT_Client* client) {
  DBG("MQTT Client: Disconnected\n");
  if (disconnected_cb)
    disconnected_cb(client);
}

void ICACHE_FLASH_ATTR
mqttPublishedCb(MQTT_Client* client) {
  DBG("MQTT Client: Published\n");
  if (published_cb)
    published_cb(client);
}

void ICACHE_FLASH_ATTR
mqttDataCb(MQTT_Client* client, const char* topic, uint32_t topic_len,
    const char *data, uint32_t data_len)
{
#ifdef MQTTCLIENT_DBG
  char *topicBuf = (char*)os_zalloc(topic_len + 1);
  char *dataBuf = (char*)os_zalloc(data_len + 1);

  os_memcpy(topicBuf, topic, topic_len);
  topicBuf[topic_len] = 0;

  os_memcpy(dataBuf, data, data_len);
  dataBuf[data_len] = 0;

  os_printf("MQTT Client: Received topic: %s, data: %s\n", topicBuf, dataBuf);
  os_free(topicBuf);
  os_free(dataBuf);
#endif

  if (data_cb)
    data_cb(client, topic, topic_len, data, data_len);
}

void ICACHE_FLASH_ATTR
wifiStateChangeCb(uint8_t status)
{
  if (flashConfig.mqtt_enable) {
    if (status == wifiGotIP && mqttClient.connState < TCP_CONNECTING) {
      MQTT_Connect(&mqttClient);
    }
    else if (status == wifiIsDisconnected && mqttClient.connState == TCP_CONNECTING) {
      MQTT_Disconnect(&mqttClient);
    }
  }
}

int ICACHE_FLASH_ATTR
mqttStatusMsg(char *buf) {
  sint8 rssi = wifi_station_get_rssi();
  if (rssi > 0) rssi = 0; // not connected or other error
  //os_printf("timer rssi=%d\n", rssi);

  // compose MQTT message
  return os_sprintf(buf,
    "{\"rssi\":%d, \"heap_free\":%ld}",
    rssi, (unsigned long)system_get_free_heap_size());
}


// Timer callback to send an RSSI update to a monitoring system
static void ICACHE_FLASH_ATTR mqttStatusCb(void *v) {
  if (!flashConfig.mqtt_status_enable || os_strlen(flashConfig.mqtt_status_topic) == 0 ||
    mqttClient.connState != MQTT_CONNECTED)
    return;

  char buf[128];
  mqttStatusMsg(buf);
  MQTT_Publish(&mqttClient, flashConfig.mqtt_status_topic, buf, os_strlen(buf), 1, 0);
}

void ICACHE_FLASH_ATTR
mqtt_client_init()
{
  MQTT_Init(&mqttClient, flashConfig.mqtt_host, flashConfig.mqtt_port, 0, flashConfig.mqtt_timeout,
    flashConfig.mqtt_clientid, flashConfig.mqtt_username, flashConfig.mqtt_password,
    flashConfig.mqtt_keepalive);

  MQTT_OnConnected(&mqttClient, mqttConnectedCb);
  MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb);
  MQTT_OnPublished(&mqttClient, mqttPublishedCb);
  MQTT_OnData(&mqttClient, mqttDataCb);

  // Don't connect now, wait for a wifi status change callback
  //if (flashConfig.mqtt_enable && strlen(flashConfig.mqtt_host) > 0)
  //  MQTT_Connect(&mqttClient);

  wifiAddStateChangeCb(wifiStateChangeCb);
  os_timer_disarm(&mqttStatusTimer);
  os_timer_setfn(&mqttStatusTimer, mqttStatusCb, NULL);
  os_timer_arm(&mqttStatusTimer, MQTT_STATUS_INTERVAL, 1); // recurring timer

}

void ICACHE_FLASH_ATTR
mqtt_client_on_connected(MqttCallback connectedCb) {
  connected_cb = connectedCb;
}

void ICACHE_FLASH_ATTR
mqtt_client_on_disconnected(MqttCallback disconnectedCb) {
  disconnected_cb = disconnectedCb;
}

void ICACHE_FLASH_ATTR
mqtt_client_on_published(MqttCallback publishedCb) {
  published_cb = publishedCb;
}

void ICACHE_FLASH_ATTR
mqtt_client_on_data(MqttDataCallback dataCb) {
  data_cb = dataCb;
}

#endif // MQTT
