/* MISC */
#include "cbor.h"
#include "esp_log.h"

/* APP */
#include "app_bkp_ram.h"
#include "app_mqtt.h"
#include "app_ui.h"

#define APP_CITY_ID (CONFIG_APP_WX_CITY_ID)

#define APP_LOG_TAG "WX"

static char *s_condition_icon_table_day[] = {
    [1] = "\U0000F00D",  [2] = "\U0000F00D",  [3] = "\U0000F00D",  [4] = "\U0000F00D",  [5] = "\U0000F00D", /* 晴 */
    [6] = "\U0000F00C",  [7] = "\U0000F00C", /* 大部晴朗 */
    [8] = "\U0000F002",  [9] = "\U0000F002",  [10] = "\U0000F002", [11] = "\U0000F002", [80] = "\U0000F002", /* 多云 */
    [81] = "\U0000F002", [82] = "\U0000F002",                                           /* 多云 */
    [12] = "\U0000F00C",                                                                /* 少云 */
    [13] = "\U0000F013", [14] = "\U0000F013", [36] = "\U0000F013", [85] = "\U0000F013", /* 阴 */
    [15] = "\U0000F008", [16] = "\U0000F008", [17] = "\U0000F008", [18] = "\U0000F008", [19] = "\U0000F008", /* 阵雨 */
    [86] = "\U0000F008",                                                                /* 阵雨 */
    [20] = "\U0000F008", [21] = "\U0000F008", [22] = "\U0000F008", [23] = "\U0000F008", /* 局部阵雨 小阵雨 强阵雨 */
    [51] = "\U0000F008", [52] = "\U0000F008", [53] = "\U0000F008", [54] = "\U0000F008", /* 小中大雨 */
    [69] = "\U0000F008", [70] = "\U0000F008",                                           /* 大暴雨 */
    [55] = "\U0000F008", [56] = "\U0000F008", [57] = "\U0000F008",                      /* 暴雨 大 特大 */
    [66] = "\U0000F008", [67] = "\U0000F008", [68] = "\U0000F008",                      /* 小中大雨 */
    [78] = "\U0000F008",                                                                /* 雨 */
    [91] = "\U0000F008", [92] = "\U0000F008", [93] = "\U0000F008",                      /* 小-中-大-暴雨 */
    [24] = "\U0000F00A", [25] = "\U0000F00A",                                           /* 阵雪 小阵雪 */
    [58] = "\U0000F00A", [59] = "\U0000F00A", [60] = "\U0000F00A", [61] = "\U0000F00A", /* 小中暴雪 */
    [62] = "\U0000F00A",                                                                /* 暴雪 */
    [71] = "\U0000F00A", [72] = "\U0000F00A", [73] = "\U0000F00A",                      /* 小雪 */
    [74] = "\U0000F00A", [75] = "\U0000F00A", [76] = "\U0000F00A", [77] = "\U0000F00A", /* 大雪 雪 */
    [94] = "\U0000F00A",                                                                /* 小-中雪 */
    [26] = "\U0000F003", [27] = "\U0000F003", [28] = "\U0000F003", [83] = "\U0000F003", [84] = "\U0000F003", /* 雾 冻 */
    [29] = "\U0000F082",                                           /* 沙尘暴 */
    [30] = "\U0000F063", [31] = "\U0000F063", [32] = "\U0000F063", /* 浮尘 尘卷风 扬沙 */
    [33] = "\U0000F082",                                           /* 强沙尘暴 */
    [34] = "\U0000F0B6", [35] = "\U0000F0B6", [79] = "\U0000F0B6", /* 霾 */
    [37] = "\U0000F010", [38] = "\U0000F010", [39] = "\U0000F010", [40] = "\U0000F010", [41] = "\U0000F010", /* 雷雨 */
    [87] = "\U0000F010", [88] = "\U0000F010", [89] = "\U0000F010", [90] = "\U0000F010", /* 雷阵雨 */
    [42] = "\U0000F005",                                                                /* 雷电 */
    [43] = "\U0000F005",                                                                /* 雷暴 */
    [44] = "\U0000F068", [45] = "\U0000F068",                                           /* 雷阵雨伴有冰雹  */
    [46] = "\U0000F004", [47] = "\U0000F004", [48] = "\U0000F004",                      /* 冰雹 冰针 冰粒*/
    [49] = "\U0000F006", [50] = "\U0000F006",                                           /* 雨夹雪 */
    [64] = "\U0000F006", [65] = "\U0000F006",                                           /* 冻雨 */
};

static char *s_wind_levels[] = {
    "\U0000F0B7", "\U0000F0B8", "\U0000F0B9", "\U0000F0BA", /* 0-3 */
    "\U0000F0BB", "\U0000F0BC", "\U0000F0BD", "\U0000F0BE", /* 4-7 */
    "\U0000F0BF", "\U0000F0C0", "\U0000F0C1", "\U0000F0C2", /* 8-11 */
    "\U0000F0C3"                                            /* 12 */
};

typedef struct {
    app_ui_cmd_t cmd;
    char        *key;
} app_weather_condition_kv_t;

static app_weather_condition_kv_t s_condition_kv[] = {
    {.cmd = APP_UI_CMD_SUNRISE, .key = "sunRise"},
    {.cmd = APP_UI_CMD_SUNSET, .key = "sunSet"},
    {.cmd = APP_UI_CMD_UVI, .key = "uvi"},
    {.cmd = APP_UI_CMD_UPDATED, .key = "updatetime"},
    {.cmd = APP_UI_CMD_TEMP, .key = "temp"},
    {.cmd = APP_UI_CMD_HUMID, .key = "humidity"},
    {.cmd = APP_UI_CMD_TEMP_REAL, .key = "realFeel"},
    {.cmd = APP_UI_CMD_WIND_SPEED, .key = "windSpeed"},
    {.cmd = APP_UI_CMD_WIND_LEVEL, .key = "windLevel"},
    {.cmd = APP_UI_CMD_COND, .key = "conditionId"},
};

static int       wxapi_request(char *api_type);
static CborError get_condition_string(CborValue *it, char *key, char **value, uint32_t *size);
static int       windlevel_to_font(char *input, char **output);
static int       condition_to_font(char *input, char **output);

int app_weather_update(void) {
    char    *topic;
    uint8_t *payload;
    uint32_t payload_len;

    app_mqtt_subscribe("iot/weather/ESP32_Weather/response");

    if (wxapi_request("condition") < 0) {
        return -1;
    }

    if (app_mqtt_poll(&topic, &payload, &payload_len, 10000) != 0) {
        ESP_LOGE(APP_LOG_TAG, "failed to receive payload in time, bail out");

        return -2;
    }

    ESP_LOGI(APP_LOG_TAG, "Received %ld bytes from topic %s", payload_len, topic);

    CborParser root_parser;
    CborValue  it;
    CborError  cbor_ret;

    cbor_ret = cbor_parser_init(payload, payload_len, 0, &root_parser, &it);
    if (cbor_ret != CborNoError) {
        ESP_LOGE(APP_LOG_TAG, "CBOR Parse 1");
        return -3;
    }

    char    *str;
    uint32_t size;

    for (uint32_t i = 0; i < sizeof(s_condition_kv) / sizeof(app_weather_condition_kv_t); i++) {
        if (get_condition_string(&it, s_condition_kv[i].key, &str, &size) == CborNoError) {
            if (s_condition_kv[i].cmd == APP_UI_CMD_WIND_LEVEL) {
                windlevel_to_font(str, &str);
            } else if (s_condition_kv[i].cmd == APP_UI_CMD_COND) {
                condition_to_font(str, &str);
            }

            app_ui_update(s_condition_kv[i].cmd, str);
            free(str);
        }
    }

    free(topic);
    free(payload);

    return 0;
}

static int wxapi_request(char *api_type) {
    CborEncoder root_encoder;
    uint8_t    *tx_buf = malloc(100);
    if (tx_buf == NULL) {
        ESP_LOGE(APP_LOG_TAG, "Failed to allocate tx buffer");

        return -1;
    }

    cbor_encoder_init(&root_encoder, tx_buf, 100, 0);

    CborEncoder map_encoder;
    cbor_encoder_create_map(&root_encoder, &map_encoder, 2);

    cbor_encode_text_stringz(&map_encoder, "type");
    cbor_encode_text_stringz(&map_encoder, api_type);

    cbor_encode_text_stringz(&map_encoder, "city_id");
    cbor_encode_uint(&map_encoder, APP_CITY_ID);

    cbor_encoder_close_container(&root_encoder, &map_encoder);

    uint32_t data_size = cbor_encoder_get_buffer_size(&root_encoder, tx_buf);

    ESP_LOGI(APP_LOG_TAG, "Encoded CBOR message %ld bytes.", data_size);

    app_mqtt_publish("iot/weather/ESP32_Weather/request", tx_buf, data_size);

    free(tx_buf);

    return 0;
}

static int condition_to_font(char *input, char **output) {
    *output = malloc(4);
    if (*output == NULL) return -1;

    int wx_id = strtol(input, NULL, 10);

    char *unicode = s_condition_icon_table_day[wx_id % sizeof(s_condition_icon_table_day)];

    snprintf(*output, 4, "%s", unicode);
    free(input);

    return 0;
}

static int windlevel_to_font(char *input, char **output) {
    *output = malloc(4);
    if (*output == NULL) return -1;

    int   level   = strtol(input, NULL, 10);
    char *unicode = s_wind_levels[level % sizeof(s_wind_levels)];

    snprintf(*output, 4, "%s", unicode);
    free(input);
    return 0;
}

static CborError get_condition_string(CborValue *it, char *key, char **value, uint32_t *size) {
    CborError ret;

    CborValue data;
    ret = cbor_value_map_find_value(it, "data", &data);
    if (ret != CborNoError) {
        ESP_LOGE(APP_LOG_TAG, "CBOR Parse 2");
        return ret;
    }

    if (cbor_value_get_type(&data) != CborMapType) {
        ESP_LOGE(APP_LOG_TAG, "CBOR Parse 3");
        return ret;
    }

    CborValue condition;
    ret = cbor_value_map_find_value(&data, "condition", &condition);
    if (ret != CborNoError) {
        ESP_LOGE(APP_LOG_TAG, "CBOR Parse 4");
        return ret;
    }

    if (cbor_value_get_type(&condition) != CborMapType) {
        ESP_LOGE(APP_LOG_TAG, "CBOR Parse 5");
        return ret;
    }

    CborValue result;
    ret = cbor_value_map_find_value(&condition, key, &result);
    if (ret != CborNoError) {
        ESP_LOGE(APP_LOG_TAG, "CBOR Parse 6");
        return ret;
    }

    if (cbor_value_is_text_string(&result)) {
        cbor_value_dup_text_string(&result, value, (size_t *)&size, &result);
    }

    return ret;
}