#include <stdio.h>
#include "meteofrance.h"
#include "eventsManager.h"
#include "lvgl.h"
#include "esp_log.h"

#include "esp_heap_caps.h"
extern EventGroupHandle_t domotic_event_group;

/**
 * 32-bit and 16-bit colors are currently supported by QEMU RGB Panel
 */
#if CONFIG_LV_COLOR_DEPTH_32
#define CURRENT_COLOR_DEPTH RGB_QEMU_BPP_32
#elif CONFIG_LV_COLOR_DEPTH_16
#define CURRENT_COLOR_DEPTH RGB_QEMU_BPP_16
#else
#error "QEMU RGB Panel only supports 32-bit and 16-bit colors, please enable LV_COLOR_DEPTH_32 or LV_COLOR_DEPTH_16"
#endif

// The pixel number in horizontal and vertical
#define EXAMPLE_LCD_H_RES 800
#define EXAMPLE_LCD_V_RES 480

#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
#define EXAMPLE_LVGL_TASK_STACK_SIZE (10 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY 2
static const char *TAG = "example";

SemaphoreHandle_t lvgl_mux;

static void example_lvgl_flush_cb(lv_display_t *drv, const lv_area_t *area, unsigned char * color_map)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
    int offsetx1 = area->x1;
    int offsetx2 = area->x2;
    int offsety1 = area->y1;
    int offsety2 = area->y2;
    // pass the draw buffer to the driver
    esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
    lv_disp_flush_ready(drv);
}

static void example_increase_lvgl_tick(void *arg)
{
    /* Tell LVGL how many milliseconds has elapsed */
    lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

bool example_lvgl_lock(int timeout_ms)
{
    // Convert timeout in milliseconds to FreeRTOS ticks
    // If `timeout_ms` is set to -1, the program will block until the condition is met
    const TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
    return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}

void example_lvgl_unlock(void)
{
    xSemaphoreGiveRecursive(lvgl_mux);
}

static void example_lvgl_port_task(void *arg)
{
    ESP_LOGI(TAG, "Starting LVGL task");
    uint32_t task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
    while (1)
    {
        // Lock the mutex due to the LVGL APIs are not thread-safe
        if (example_lvgl_lock(-1))
        {
            task_delay_ms = lv_timer_handler();
            // Release the mutex
            example_lvgl_unlock();
        }
        if (task_delay_ms > EXAMPLE_LVGL_TASK_MAX_DELAY_MS)
        {
            task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
        }
        else if (task_delay_ms < EXAMPLE_LVGL_TASK_MIN_DELAY_MS)
        {
            task_delay_ms = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;
        }
        vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
    }
}

static size_t example_lvgl_get_buffers(esp_lcd_panel_handle_t panel_handle, void **buf1, void **buf2)
{
    if (buf2)
    {
        *buf2 = NULL;
    }
#if CONFIG_EXAMPLE_QEMU_RGB_PANEL_DEDIC_FB
    ESP_LOGI(TAG, "Use QEMU dedicated frame buffer as LVGL draw buffer");
    ESP_ERROR_CHECK(esp_lcd_rgb_qemu_get_frame_buffer(panel_handle, buf1));
    return EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES;
#else
    ESP_LOGI(TAG, "Allocate separate LVGL draw buffer");
    /* Allocate 10 horizontal lines as the frame buffer */
    *buf1 = malloc(EXAMPLE_LCD_H_RES * 10 * sizeof(lv_color_t));
    assert(*buf1);
    return EXAMPLE_LCD_H_RES * 10;
#endif // CONFIG_EXAMPLE_DOUBLE_FB
}

void app_main()
{
    printf(">>> app_main() from test_main.c under Linux\n");
    startEvtManager();

    ESP_LOGI(TAG, "Install RGB LCD panel driver");
    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_rgb_qemu_config_t panel_config = {
        .width = EXAMPLE_LCD_H_RES,
        .height = EXAMPLE_LCD_V_RES,
        .bpp = CURRENT_COLOR_DEPTH,
    };
    ESP_ERROR_CHECK(esp_lcd_new_rgb_qemu(&panel_config, &panel_handle));

    ESP_LOGI(TAG, "Initialize RGB LCD panel");
    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));

    ESP_LOGI(TAG, "Free internal heap: %d", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
    ESP_LOGI(TAG, "Free SPIRAM heap: %d", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));

    ESP_LOGI(TAG, "Initialize LVGL library");
    lv_init();
    lv_display_t *disp = lv_display_create(EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES);
    disp->user_data=panel_handle;
    lv_display_set_flush_cb(disp, example_lvgl_flush_cb);
    void *buf1 = NULL;
    void *buf2 = NULL;
    const size_t buf_pixels = example_lvgl_get_buffers(panel_handle, &buf1, &buf2);
    lv_display_set_buffers(disp, buf1, buf2, EXAMPLE_LCD_H_RES * 10 * sizeof(lv_color_t), LV_DISP_RENDER_MODE_PARTIAL);

    lvgl_mux = xSemaphoreCreateRecursiveMutex();
    assert(lvgl_mux);

/*    lv_sdl_mouse_create();
    lv_sdl_keyboard_create();
    lv_sdl_mousewheel_create();
    lv_sdl_mousewheel_create();
    */

    ESP_LOGI(TAG, "Install LVGL tick timer");
    // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &example_increase_lvgl_tick,
        .name = "lvgl_tick"};
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));

    ESP_LOGI(TAG, "Create LVGL task");
    xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);

    xTaskCreate(drawIhm, "LVGL", 128 * 1024, getIHMQueueHandle(), 3, NULL);
    //initialise_weather_data_retrieval(20, domotic_event_group);
    printf("<<< end app_main()\n");
}