/**
 * @file mqtt.cpp
 * @author Radim Buráň (xburan03@stud.fit.vutbr.cz)
 */

#include "mqtt.hpp"

extern EventGroupHandle_t STATUS_GROUP;

extern presetData_t PRESET_DATA;

esp_mqtt_client_handle_t mqttHandle = NULL;

void parseMessage(esp_mqtt_event_handle_t event) {
    char topic[event->topic_len + 1];

    memcpy(topic, event->topic, event->topic_len);
    topic[event->topic_len] = '\0';

    cJSON* data = cJSON_ParseWithLength(event->data, event->data_len);

    ESP_LOGI(LOG_TAG, "TOPIC = %s [%d]", topic, event->topic_len);
    ESP_LOGI(LOG_TAG, "JSON = %s", cJSON_Print(data));

    PRESET_DATA.date = (uint64_t)cJSON_GetObjectItem(data, KEY_JSON_IN_DATE)->valuedouble;
    PRESET_DATA.humidity = cJSON_GetObjectItem(data, KEY_JSON_IN_HUMIDITY)->valueint;
    PRESET_DATA.hysteresis = cJSON_GetObjectItem(data, KEY_JSON_IN_HUMIDITY_HYSTERESIS)->valueint;
    PRESET_DATA.pumpCycle = cJSON_GetObjectItem(data, KEY_JSON_IN_PUMP_CYCLE)->valueint;
    PRESET_DATA.ledUV = cJSON_GetObjectItem(data, KEY_JSON_IN_LIGHT_UV)->valueint;
    PRESET_DATA.ledBlue = cJSON_GetObjectItem(data, KEY_JSON_IN_LIGHT_BLUE)->valueint;
    PRESET_DATA.ledGreen = cJSON_GetObjectItem(data, KEY_JSON_IN_LIGHT_GREEN)->valueint;
    PRESET_DATA.ledYellow = cJSON_GetObjectItem(data, KEY_JSON_IN_LIGHT_YELLOW)->valueint;
    PRESET_DATA.ledRed = cJSON_GetObjectItem(data, KEY_JSON_IN_LIGHT_RED)->valueint;
    PRESET_DATA.ledDeepRed = cJSON_GetObjectItem(data, KEY_JSON_IN_LIGHT_FAR_RED)->valueint;
    PRESET_DATA.ledIR = cJSON_GetObjectItem(data, KEY_JSON_IN_LIGHT_IR)->valueint;

    //write new values to NVS - used if device can't connect to WiFi
    if (readU8("pump_hyst") != PRESET_DATA.hysteresis) {
        writeU8("pump_hyst", PRESET_DATA.hysteresis);
        ESP_LOGI(LOG_TAG, "Updating pump_hyst");
    }
    if (readU8("pump_cycle") != PRESET_DATA.pumpCycle) {
        writeU8("pump_cycle", PRESET_DATA.pumpCycle);
        ESP_LOGI(LOG_TAG, "Updating pump_cycle");
    }
    if (readU8("pump_hum") != PRESET_DATA.humidity) {
        writeU8("pump_hum", PRESET_DATA.humidity);
        ESP_LOGI(LOG_TAG, "Updating pump_hum");
    }

    xEventGroupSetBits(STATUS_GROUP, BIT_PRESET_PARSED);
}

void mqttEventHandler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
    ESP_LOGD(LOG_TAG, "Event dispatched from event loop base=%s, event_id=%ld", base, event_id);
    esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    switch ((esp_mqtt_event_id_t)event_id) {
        case MQTT_EVENT_CONNECTED: {
            ESP_LOGI(LOG_TAG, "MQTT_EVENT_CONNECTED");
            
            char* devID = readString(KEY_NVS_DEVICE_ID);
            char* topic = appendStrings(MQTT_TOPIC_DEVICES, devID);

            ESP_LOGI(LOG_TAG, "Topic: %s", topic);
            msg_id = esp_mqtt_client_subscribe(client, topic, 1);
            free(topic);

            cJSON* jsonData = cJSON_CreateObject();
            cJSON_AddStringToObject(jsonData, "device", devID);
            free(devID);

            mqttPublish(MQTT_TOPIC_ALIVE, cJSON_Print(jsonData), 1);
            cJSON_Delete(jsonData);

            xEventGroupSetBits(STATUS_GROUP, BIT_MQTT_ESTABLISHED);
            break;
        }
        case MQTT_EVENT_DISCONNECTED:
            ESP_LOGI(LOG_TAG, "MQTT_EVENT_DISCONNECTED");
            xEventGroupClearBits(STATUS_GROUP, BIT_MQTT_ESTABLISHED);
            break;
        case MQTT_EVENT_SUBSCRIBED:
            ESP_LOGI(LOG_TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_UNSUBSCRIBED:
            ESP_LOGI(LOG_TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_PUBLISHED:
            ESP_LOGI(LOG_TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_DATA:
            ESP_LOGI(LOG_TAG, "MQTT_EVENT_DATA");
            parseMessage(event);
            break;
        case MQTT_EVENT_ERROR:
            ESP_LOGI(LOG_TAG, "MQTT_EVENT_ERROR");
            break;
        default:
            ESP_LOGI(LOG_TAG, "Other MQTT event id:%d", event->event_id);
            break;
    }
}

void mqttStart() {
    ESP_LOGI(LOG_TAG, "STARTING MQTT");
    esp_mqtt_client_config_t mqttConfig = {
        .broker = {
            .address = {
                .uri = MQTT_URI
            }
        }
    };
    
    mqttHandle = esp_mqtt_client_init(&mqttConfig);
    esp_mqtt_client_register_event(mqttHandle, MQTT_EVENT_ANY, mqttEventHandler, mqttHandle);
    esp_mqtt_client_start(mqttHandle);
}

void mqttStop() {
    if (mqttHandle != NULL) {
        ESP_LOGI(LOG_TAG, "STOPPING MQTT");
        esp_mqtt_client_stop(mqttHandle);
        esp_mqtt_client_destroy(mqttHandle);
        mqttHandle = NULL;
    }
}


/**
 * @brief Send message to MQTT
 * 
 * @param topic Topic name
 * @param json text
 * @param qos QoS
 * @return esp_err_t ESP_OK if OK
 */
esp_err_t mqttPublish(const char* topic, char* json, int qos) {
    ESP_LOGI(LOG_TAG, "Topic: %s | json: %s", topic, json);

    int ret = esp_mqtt_client_publish(mqttHandle, topic, json, 0, qos, 0);

    ESP_LOGI(LOG_TAG, "Send status: %d", ret);

    return ESP_OK;
}
