#include "mqtt.h"

void mqttHA::merge(JsonVariant &dst, JsonVariantConst src)
{
  if (src.is<JsonObjectConst>())
  {
    for (JsonPairConst kvp : src.as<JsonObjectConst>())
    {
      if (dst[kvp.key()]) 
        merge(dst[kvp.key()], kvp.value());
      else
        dst[kvp.key()] = kvp.value();
    }
  }
  else
  {
    dst.set(src);
  }
}

String mqttHA::slugify(String input) {
  return input.toLowerCase().replace(" ", "_");
}

void mqttHA::publishTemperature(DynamicJsonDocument &device, String &vessle) {

  name_slug = slugify(device["name"]);
  vessel_slug = slugify(vessel);

  String topic = "homeassistant/sensor/" + name_slug + "_" + chipid + "/" + vessel_slug + "_temp/config";

  DynamicJsonDocument entity(200);
  entity["uniq_id"] = chipid + "_" + vessel_slug + "_temp";
  entity["dev_cla"] =  "temperature";
  entity["name"] =  vessle + " Temperature";
  entity["unit_of_meas"] =  "°" + TEMP_UNIT;
  entity["val_tpl"] = "{{ value_json }}";
  entity["stat_t"] = "damn_yankee/" + name_slug + "/" + vessel_slug + "_temp";

  merge(entity, device)

  String payload;
  serializeJson(entity, payload)

  mqtt_discovery(topic, payload);  
}

void mqttHA::mqtt_discovery(const String topic, const String payload) {

  bool response = ConnectMQTT(MQTT_BROKER, MQTT_NAME, MQTT_USER, MQTT_PASSWORD);
  if (response)
  {
    mqtt_client.setBufferSize(512);
    mqtt_client.publish(topic.c_str(), payload.c_str(), true);
                        
    /*
    // Passive Sensors
    mqtt_client.publish((topic + "boil_temperature/config").c_str(),
                        ("{\"uniq_id\": \"" + chipid + "_boil_temp\"," +
                         "\"dev_cla\": \"temperature\"," +
                         "\"name\": \"Boil Temperature\"," +
                         "\"unit_of_meas\": \"°" + TEMP_UNIT + "\"," +
                         "\"val_tpl\": \"{{ value_json }}\"," +
                         "\"stat_t\": \"brewhouse/" + DEVNAME + "/boil_temperature\"," +
                         device + "}")
                            .c_str(),
                        true);
    mqtt_client.publish((topic + "mash_temperature/config").c_str(),
                        ("{\"uniq_id\": \"" + chipid + "_mash_temp\"," +
                         "\"dev_cla\": \"temperature\"," +
                         "\"name\": \"Mash Temperature\"," +
                         "\"unit_of_meas\": \"°" + TEMP_UNIT + "\"," +
                         "\"val_tpl\": \"{{ value_json }}\"," +
                         "\"stat_t\": \"brewhouse/" + DEVNAME + "/mash_temperature\"," +
                         device + "}")
                            .c_str(),
                        true);

    // User Changeable Settings
    topic = "homeassistant/number/brewhouse_" + chipid + "/";
    mqtt_client.publish((topic + "mash_setpoint/config").c_str(),
                        ("{\"uniq_id\": \"" + chipid + "_mash_setpoint\"," +
                         "\"dev_cla\": \"temperature\"," +
                         "\"name\": \"mash Setpoint\"," +
                         "\"val_tpl\": \"{{ value_json }}\"," +
                         "\"cmd_tpl\": \"{{ value_json }}\"," +
                         "\"stat_t\": \"brewhouse/" + DEVNAME + "/mash_setpoint\"," +
                         "\"cmd_t\": \"brewhouse/" + DEVNAME + "/mash_set_temp\"," +
                         device + "}")
                            .c_str(),
                        true);

    mqtt_client.publish((topic + "boil_setpoint/config").c_str(),
                        ("{\"uniq_id\": \"" + chipid + "_boil_setpoint\"," +
                         "\"dev_cla\": \"temperature\"," +
                         "\"name\": \"Boil Setpoint\"," +
                         "\"val_tpl\": \"{{ value_json }}\"," +
                         "\"cmd_tpl\": \"{{ value_json }}\"," +
                         "\"stat_t\": \"brewhouse/" + DEVNAME + "/boil_setpoint\"," +
                         "\"cmd_t\": \"brewhouse/" + DEVNAME + "/boil_set_temp\"," +
                         device + "}")
                            .c_str(),
                        true);
    mqtt_client.publish((topic + "boil_pwm/config").c_str(),
                        ("{\"uniq_id\": \"" + chipid + "_boil_pwm\"," +
                         "\"dev_cla\": \"temperature\"," +
                         "\"name\": \"Boil pwm\"," +
                         "\"val_tpl\": \"{{ value_json }}\"," +
                         "\"cmd_tpl\": \"{{ value_json }}\"," +
                         "\"stat_t\": \"brewhouse/" + DEVNAME + "/boil_pwm\"," +
                         "\"cmd_t\": \"brewhouse/" + DEVNAME + "/boil_set_pwm\"," +
                         device + "}")
                            .c_str(),
                        true);

    topic = "homeassistant/select/brewhouse_" + chipid + "/";
    mqtt_client.publish((topic + "boil_mode/config").c_str(),
                        ("{\"uniq_id\": \"" + chipid + "_boil_mode\"," +
                         "\"name\": \"Boil Mode\"," +
                         "\"ops\": [\"PWM\",\"PID\"]," +
                         "\"val_tpl\": \"{{ value_json }}\"," +
                         "\"cmd_tpl\": \"{{ value_json }}\"," +
                         "\"stat_t\": \"brewhouse/" + DEVNAME + "/boil_mode\"," +
                         "\"cmd_t\": \"brewhouse/" + DEVNAME + "/boil_set_mode\"," +
                         device + "}")
                            .c_str(),
                        true);
    */
    mqtt_client.loop();
}

bool mqttHA::ConnectMQTT(const String &server, const String &name, const String &user, const String &password) {

                                  Serial.println("Setup MQTT client.");
  mqtt_client.begin(server.c_str(), _net);
  mqtt_client.onMessage(MessageReceived);

  Serial.println("connecting MQTT...");

  byte i = 0;

  while (!mqtt_client.connected() && (i < 3)) {
    boolean ret;
    ret = mqtt_client.connect(name.c_str(), user.c_str(), password.c_str())
    if (ret) {
      Serial.println("Connected to MQTT");

      mqtt_client.subscribe("brewery/setpoint/bk");

      return true;

    } else {
      int Status = mqtt_client.state();

      switch (Status)
      {
        case -4:
          Serial.println(F("Connection timeout"));
          break;

        case -3:
          Serial.println(F("Connection lost"));
          break;

        case -2:
          Serial.println(F("Connect failed"));
          break;

        case -1:
          Serial.println(F("Disconnected"));
          break;

        case 1:
          Serial.println(F("Bad protocol"));
          break;

        case 2:
          Serial.println(F("Bad client ID"));
          break;

        case 3:
          Serial.println(F("Unavailable"));
          break;

        case 4:
          Serial.println(F("Bad credentials"));
          break;

        case 5:
          Serial.println(F("Unauthorized"));
          break;
      }

    }

    Serial.print(".");
    i++;
    delay(5000);
  }

  return false

}

void mqttHA::MessageReceived(String &topic, String &payload) {
  Serial.println("incoming: " + topic + " - " + payload);

  /** JSON Parser Setup */
  StaticJsonDocument<200> doc;

  // Deserialize the JSON document
  DeserializationError error = deserializeJson(doc, payload);

  // Test if parsing succeeds.
  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());
    return;
  }
  char buf[30];
  strcpy(buf,TOPIC_PREFIX);
  strcat(buf,BOIL_SETPOINT_TOPIC);
  if (topic == buf) {
    // Update PWM setpoint.
    String name = doc["entity"];
    String setting = doc["setpoint"];

    KettleDuty = setting.toInt();
    String unit = doc["units"];

    Serial.println("Updating setpoint for " + name + " to " + setting + " " + unit);
  }
}

static void mqttHA::SendSensorData() {
  Serial.println("Sending data...");

  // NOTE: max message length is 250 bytes.
  StaticJsonDocument<200> doc;

  doc["entity"] = "boil_kettle";
  doc["setpoint"] = KettleDuty;
  doc["units"] = "%";

  String jstr;
  serializeJson(doc, jstr);

  String topic = TOPIC_PREFIX;
  topic += "sensor/boil_kettle";

  mqtt_client.publish(topic, jstr);

}
