#include <stdio.h>

#include "sdkconfig.h"

/**/
#include "esp_log.h"
#include "esp_system.h"

/* FreeRTOS */
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"

/* LVGL */
#include "lvgl.h"

/* Private */
#include "app_lvgl.h"
#include "app_ui.h"

#define UI_TASK_HEAP 4096
#define APP_LOG_TAG  "UI_WX"

typedef struct {
    app_ui_cmd_t cmd;
    char        *data;
} app_ui_queue_item_t;

LV_FONT_DECLARE(noto_sans_24)
LV_FONT_DECLARE(noto_sans_96)
LV_FONT_DECLARE(noto_sans_150)
LV_FONT_DECLARE(noto_sans_240)
LV_FONT_DECLARE(weather_icon_24)
LV_FONT_DECLARE(weather_icon_96)
LV_FONT_DECLARE(weather_icon_150)
LV_FONT_DECLARE(weather_icon_240)

static lv_style_t s_epd_style;
static lv_theme_t s_epd_theme;

static QueueHandle_t s_app_ui_update_queue;

static void app_ui_theme_apply(lv_theme_t *th, lv_obj_t *obj);
static void app_ui_task(void *pvParameters);

int app_ui_init(void) {
    s_app_ui_update_queue = xQueueCreate(4, sizeof(app_ui_queue_item_t));
    if (s_app_ui_update_queue == NULL) {
        return -1;
    }

    if (xTaskCreate(app_ui_task, "UI_WX", UI_TASK_HEAP, NULL, 3, NULL) != pdPASS) {
        ESP_LOGE(APP_LOG_TAG, "UI WX task failed to create.");
        return -2;
    }

    ESP_LOGI(APP_LOG_TAG, "UI WX initialized.");

    return 0;
}

int app_ui_update(app_ui_cmd_t cmd, char *data) {
    app_ui_queue_item_t item;
    item.cmd  = cmd;
    item.data = malloc(strlen(data));

    if (item.data == NULL) {
        return -1;
    }

    strcpy(item.data, data);

    if (xQueueSend(s_app_ui_update_queue, &item, portMAX_DELAY) != pdPASS) {
        return -2;
    }

    return 0;
}

static void app_ui_task(void *pvParameters) {
    lv_obj_t *screen_cond = NULL;

    /* Top bar */
    lv_obj_t *container_topbar = NULL;

    lv_obj_t *label_sunrise      = NULL;
    lv_obj_t *label_sunrise_time = NULL;
    lv_obj_t *label_sunset       = NULL;
    lv_obj_t *label_sunset_time  = NULL;
    lv_obj_t *label_uvi          = NULL;
    lv_obj_t *label_uvi_value    = NULL;
    lv_obj_t *label_updated      = NULL;
    lv_obj_t *label_updated_time = NULL;

    /* Seperation line */
    lv_obj_t *line_topbar = NULL;

    /* Middle display */
    lv_obj_t *container_main = NULL;

    lv_obj_t *label_wxicon                = NULL;
    lv_obj_t *label_temperature           = NULL;
    lv_obj_t *label_temperature_hi        = NULL;
    lv_obj_t *label_temperature_lo        = NULL;
    lv_obj_t *label_temperature_real      = NULL;
    lv_obj_t *label_temperature_real_desc = NULL;
    lv_obj_t *label_temperature_unit      = NULL;
    lv_obj_t *label_temperature_hi_unit   = NULL;
    lv_obj_t *label_temperature_lo_unit   = NULL;
    lv_obj_t *label_temperature_real_unit = NULL;
    lv_obj_t *label_humidity              = NULL;
    lv_obj_t *label_humidity_unit         = NULL;
    lv_obj_t *label_wind_level            = NULL;
    lv_obj_t *label_wind_speed            = NULL;
    lv_obj_t *label_wind_direction        = NULL;

    lv_point_t points_line_topbar[] = {{0, 37}, {959, 37}};

    if (app_lvgl_lock(portMAX_DELAY) == 0) {
        lv_style_init(&s_epd_style);
        lv_style_set_pad_all(&s_epd_style, 0);
        lv_style_set_radius(&s_epd_style, 0);
        lv_style_set_border_width(&s_epd_style, 0);

        lv_theme_set_apply_cb(&s_epd_theme, app_ui_theme_apply);

        lv_disp_set_theme(NULL, &s_epd_theme);

        screen_cond = lv_obj_create(NULL);

        container_topbar = lv_obj_create(screen_cond);

        label_sunrise      = lv_label_create(container_topbar);
        label_sunrise_time = lv_label_create(container_topbar);
        label_sunset       = lv_label_create(container_topbar);
        label_sunset_time  = lv_label_create(container_topbar);
        label_uvi          = lv_label_create(container_topbar);
        label_uvi_value    = lv_label_create(container_topbar);
        label_updated      = lv_label_create(container_topbar);
        label_updated_time = lv_label_create(container_topbar);

        line_topbar    = lv_line_create(screen_cond);
        container_main = lv_obj_create(screen_cond);

        label_wxicon                = lv_label_create(container_main);
        label_temperature           = lv_label_create(container_main);
        label_temperature_hi        = lv_label_create(container_main);
        label_temperature_lo        = lv_label_create(container_main);
        label_temperature_real      = lv_label_create(container_main);
        label_temperature_real_desc = lv_label_create(container_main);
        label_temperature_unit      = lv_label_create(container_main);
        label_temperature_hi_unit   = lv_label_create(container_main);
        label_temperature_lo_unit   = lv_label_create(container_main);
        label_temperature_real_unit = lv_label_create(container_main);
        label_humidity              = lv_label_create(container_main);
        label_humidity_unit         = lv_label_create(container_main);
        label_wind_level            = lv_label_create(container_main);
        label_wind_direction        = lv_label_create(container_main);
        label_wind_speed            = lv_label_create(container_main);

        /* Top bar parts */
        lv_obj_set_style_text_font(label_sunrise, &weather_icon_24, 0U);
        lv_obj_set_style_text_font(label_sunset, &weather_icon_24, 0U);
        lv_obj_set_style_text_font(label_uvi, &weather_icon_24, 0U);
        lv_obj_set_style_text_font(label_updated, &weather_icon_24, 0U);

        lv_obj_set_style_text_font(label_sunrise_time, &noto_sans_24, 0U);
        lv_obj_set_style_text_font(label_sunset_time, &noto_sans_24, 0U);
        lv_obj_set_style_text_font(label_uvi_value, &noto_sans_24, 0U);
        lv_obj_set_style_text_font(label_updated_time, &noto_sans_24, 0U);

        lv_label_set_text(label_sunrise, "\U0000F051");
        lv_label_set_text(label_sunset, "\U0000F052");
        lv_label_set_text(label_uvi, "\U0000F00D");
        lv_label_set_text(label_updated, "\U0000F04C");

        lv_label_set_text(label_sunrise_time, "----/--/-- --:--:--");
        lv_label_set_text(label_sunset_time, "----/--/-- --:--:--");
        lv_label_set_text(label_uvi_value, "-");
        lv_label_set_text(label_updated_time, "----/--/-- --:--:--");

        lv_obj_set_size(container_topbar, 960, 36);

        lv_obj_set_width(label_sunrise, 32);
        lv_obj_set_width(label_sunset, 32);
        lv_obj_set_width(label_uvi, 32);
        lv_obj_set_width(label_updated, 32);

        lv_obj_set_style_text_align(label_sunrise, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_sunset, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_uvi, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_updated, LV_TEXT_ALIGN_CENTER, 0);

        lv_obj_set_style_text_align(label_sunrise_time, LV_TEXT_ALIGN_LEFT, 0);
        lv_obj_set_style_text_align(label_sunset_time, LV_TEXT_ALIGN_LEFT, 0);
        lv_obj_set_style_text_align(label_uvi_value, LV_TEXT_ALIGN_LEFT, 0);
        lv_obj_set_style_text_align(label_updated_time, LV_TEXT_ALIGN_LEFT, 0);

        lv_obj_align(container_topbar, LV_ALIGN_TOP_LEFT, 0, 0);

        lv_obj_align(label_sunrise, LV_ALIGN_LEFT_MID, 0, 0);
        lv_obj_align(label_sunset, LV_ALIGN_LEFT_MID, 300, 0);
        lv_obj_align(label_uvi, LV_ALIGN_LEFT_MID, 600, 0);
        lv_obj_align(label_updated, LV_ALIGN_LEFT_MID, 680, 0);

        lv_obj_align(label_sunrise_time, LV_ALIGN_LEFT_MID, 40, 3);
        lv_obj_align(label_sunset_time, LV_ALIGN_LEFT_MID, 340, 3);
        lv_obj_align(label_uvi_value, LV_ALIGN_LEFT_MID, 640, 3);
        lv_obj_align(label_updated_time, LV_ALIGN_LEFT_MID, 720, 3);

        lv_line_set_points(line_topbar, points_line_topbar, 2);
        lv_obj_set_style_line_width(line_topbar, 1, 0);

        /* Main parts */
        lv_obj_set_style_text_font(label_wxicon, &weather_icon_150, 0);
        lv_obj_set_style_text_font(label_temperature_unit, &weather_icon_96, 0);
        lv_obj_set_style_text_font(label_humidity_unit, &weather_icon_96, 0);
        lv_obj_set_style_text_font(label_temperature_hi_unit, &weather_icon_96, 0);
        lv_obj_set_style_text_font(label_temperature_lo_unit, &weather_icon_96, 0);
        lv_obj_set_style_text_font(label_temperature_real_unit, &weather_icon_96, 0);
        lv_obj_set_style_text_font(label_wind_direction, &weather_icon_96, 0);
        lv_obj_set_style_text_font(label_wind_level, &weather_icon_96, 0);

        lv_obj_set_style_text_font(label_temperature, &noto_sans_150, 0);
        lv_obj_set_style_text_font(label_temperature_hi, &noto_sans_96, 0);
        lv_obj_set_style_text_font(label_temperature_lo, &noto_sans_96, 0);
        lv_obj_set_style_text_font(label_temperature_real, &noto_sans_96, 0);
        lv_obj_set_style_text_font(label_temperature_real_desc, &noto_sans_24, 0);
        lv_obj_set_style_text_font(label_humidity, &noto_sans_96, 0);
        lv_obj_set_style_text_font(label_wind_speed, &noto_sans_96, 0);

        lv_label_set_text(label_wxicon, "\U0000F07B");
        lv_label_set_text(label_temperature_unit, "\U0000F03C");
        lv_label_set_text(label_temperature_hi_unit, "\U0000F058");
        lv_label_set_text(label_temperature_lo_unit, "\U0000F044");
        lv_label_set_text(label_temperature_real_unit, "\U0000F03C");
        lv_label_set_text(label_humidity_unit, "\U0000F07A");
        lv_label_set_text(label_wind_direction, "\U0000F0B1");
        lv_label_set_text(label_wind_level, "\U0000F0BA");

        lv_label_set_text(label_temperature, "--");
        lv_label_set_text(label_temperature_hi, "--");
        lv_label_set_text(label_temperature_lo, "--");
        lv_label_set_text(label_temperature_real, "--");
        lv_label_set_text(label_temperature_real_desc, "Real\nFeel");
        lv_label_set_text(label_humidity, "---");
        lv_label_set_text(label_wind_speed, "----");

        lv_obj_set_size(container_main, 960, 370);
        lv_obj_set_size(label_temperature_unit, 96, 96);
        lv_obj_set_size(label_temperature_hi_unit, 96, 96);
        lv_obj_set_size(label_temperature_lo_unit, 96, 96);
        lv_obj_set_size(label_temperature_real_unit, 96, 96);
        lv_obj_set_size(label_humidity_unit, 96, 150);
        lv_obj_set_size(label_wind_direction, 96, 96);
        lv_obj_set_size(label_wind_level, 96, 96);

        lv_obj_set_width(label_wxicon, 200);
        lv_obj_set_width(label_temperature, 200);
        lv_obj_set_width(label_temperature_hi, 150);
        lv_obj_set_width(label_temperature_lo, 150);
        lv_obj_set_width(label_temperature_real, 150);
        lv_obj_set_width(label_temperature_real_desc, 50);
        lv_obj_set_width(label_humidity, 200);
        lv_obj_set_width(label_wind_speed, 220);

        lv_obj_set_style_text_align(label_wxicon, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_temperature, LV_TEXT_ALIGN_RIGHT, 0);
        lv_obj_set_style_text_align(label_temperature_hi, LV_TEXT_ALIGN_RIGHT, 0);
        lv_obj_set_style_text_align(label_temperature_lo, LV_TEXT_ALIGN_RIGHT, 0);
        lv_obj_set_style_text_align(label_temperature_real, LV_TEXT_ALIGN_RIGHT, 0);
        lv_obj_set_style_text_align(label_temperature_real_desc, LV_TEXT_ALIGN_LEFT, 0);
        lv_obj_set_style_text_align(label_temperature_unit, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_temperature_hi_unit, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_temperature_lo_unit, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_temperature_real_unit, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_humidity, LV_TEXT_ALIGN_RIGHT, 0);
        lv_obj_set_style_text_align(label_humidity_unit, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_wind_direction, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_wind_level, LV_TEXT_ALIGN_CENTER, 0);
        lv_obj_set_style_text_align(label_wind_speed, LV_TEXT_ALIGN_RIGHT, 0);

        lv_obj_align(container_main, LV_ALIGN_TOP_LEFT, 0, 38);

        lv_obj_align(label_wxicon, LV_ALIGN_LEFT_MID, 0, 0);
        lv_obj_align(label_temperature_unit, LV_ALIGN_TOP_LEFT, 400, -20);
        lv_obj_align(label_temperature_hi_unit, LV_ALIGN_TOP_LEFT, 200, 115);
        lv_obj_align(label_temperature_lo_unit, LV_ALIGN_TOP_LEFT, 200, 235);

        lv_obj_align(label_temperature, LV_ALIGN_TOP_LEFT, 200, 0);
        lv_obj_align(label_temperature_hi, LV_ALIGN_TOP_LEFT, 250, 150);
        lv_obj_align(label_temperature_lo, LV_ALIGN_TOP_LEFT, 250, 250);
        lv_obj_align(label_temperature_real, LV_ALIGN_TOP_LEFT, 650, 130);
        lv_obj_align(label_temperature_real_desc, LV_ALIGN_TOP_LEFT, 585, 165);
        lv_obj_align(label_humidity, LV_ALIGN_TOP_LEFT, 600, 10);
        lv_obj_align(label_humidity_unit, LV_ALIGN_TOP_LEFT, 800, -20);
        lv_obj_align(label_temperature_real_unit, LV_ALIGN_TOP_LEFT, 800, 120);
        lv_obj_align(label_wind_direction, LV_ALIGN_TOP_LEFT, 500, 230);
        lv_obj_align(label_wind_speed, LV_ALIGN_TOP_LEFT, 580, 250);
        lv_obj_align(label_wind_level, LV_ALIGN_TOP_LEFT, 815, 240);

        lv_scr_load(screen_cond);
        app_lvgl_unlock();
    }

    app_ui_queue_item_t item;

    for (;;) {
        /* Receive update requests. */
        if (xQueueReceive(s_app_ui_update_queue, &item, portMAX_DELAY) == pdPASS) {
            if (app_lvgl_lock(portMAX_DELAY) == 0) {
                lv_obj_t *label = NULL;

                switch (item.cmd) {
                    case APP_UI_CMD_SUNRISE:
                        label = label_sunrise_time;
                        break;
                    case APP_UI_CMD_SUNSET:
                        label = label_sunset_time;
                        break;
                    case APP_UI_CMD_UVI:
                        label = label_uvi_value;
                        break;
                    case APP_UI_CMD_COND:
                        label = label_wxicon;
                        break;
                    case APP_UI_CMD_TEMP:
                        label = label_temperature;
                        break;
                    case APP_UI_CMD_TEMP_HI:
                        label = label_temperature_hi;
                        break;
                    case APP_UI_CMD_TEMP_LO:
                        label = label_temperature_lo;
                        break;
                    case APP_UI_CMD_TEMP_REAL:
                        label = label_temperature_real;
                        break;
                    case APP_UI_CMD_HUMID:
                        label = label_humidity;
                        break;
                    case APP_UI_CMD_UPDATED:
                        label = label_updated_time;
                        break;
                    case APP_UI_CMD_WIND_SPEED:
                        label = label_wind_speed;
                        break;
                    case APP_UI_CMD_WIND_LEVEL:
                        label = label_wind_level;
                        break;
                    default:
                        break;
                }

                lv_label_set_text(label, item.data);
                app_lvgl_unlock();
            }

            free(item.data);
        }
    }
}

static void app_ui_theme_apply(lv_theme_t *th, lv_obj_t *obj) {
    lv_obj_add_style(obj, &s_epd_style, 0);
}
