#if LV_BUILD_TEST
#include "../lvgl.h"
#include "../../lvgl_private.h"

#include "unity/unity.h"

#if LV_USE_FREETYPE

#include "rnd_unicodes/lv_rnd_unicodes.h"

#ifndef NON_AMD64_BUILD
#define TEST_FREETYPE_ASSERT_EQUAL_SCREENSHOT(INDEX) \
    lv_snprintf(buf, sizeof(buf), "libs/font_stress/snapshot_%0d.lp64.png", (INDEX));\
    TEST_ASSERT_EQUAL_SCREENSHOT(buf)
#else
#define TEST_FREETYPE_ASSERT_EQUAL_SCREENSHOT(INDEX) \
    lv_snprintf(buf, sizeof(buf), "libs/font_stress/snapshot_%0d.lp32.png", (INDEX));\
    TEST_ASSERT_EQUAL_SCREENSHOT(buf)
#endif

/**********************
 *      TYPEDEFS
 **********************/

typedef struct {
    const char ** font_name_arr;
    int font_cnt;
    int label_cnt;
    int loop_cnt;
} lvx_font_stress_config_t;

typedef struct {
    lv_obj_t * par;
    lv_obj_t ** label_arr;
    lvx_font_stress_config_t config;
} stress_test_ctx_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/

static void update_cb(void);

/**********************
 *  STATIC VARIABLES
 **********************/

static stress_test_ctx_t g_ctx = { 0 };
static const char * font_name_arr[] = {
    "./src/test_files/fonts/noto/NotoSansSC-Regular.ttf",
    "../src/libs/freetype/arial.ttf",
    "../demos/multilang/assets/fonts/Montserrat-Bold.ttf",
    "UNKNOWN_FONT_NAME"
};
static const uint16_t font_style[] = {
    LV_FREETYPE_FONT_STYLE_NORMAL,
    LV_FREETYPE_FONT_STYLE_ITALIC,
    LV_FREETYPE_FONT_STYLE_BOLD,
};

/**********************
 *      MACROS
 **********************/

#define RND_START_SEED 0x114514
#define CAPTURE_SKIP_FRAMES 17

#define MAX_FONT_SIZE 128
#define MAX_LABEL_CNT 32
#define MAX_LOOP_CNT 200
#define MAX_LABEL_CONTENT_LEN 16

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/**********************
 *   STATIC FUNCTIONS
 **********************/

static lv_obj_t * label_create(const char * font_name, lv_obj_t * par, int size, int x, int y)
{
    uint32_t index = lv_rand(0, sizeof(font_style) / sizeof(uint16_t) - 1);
    uint32_t r = lv_rand(0, 0xFF);
    uint32_t g = lv_rand(0, 0xFF);
    uint32_t b = lv_rand(0, 0xFF);
    lv_opa_t opa = lv_rand(0, LV_OPA_COVER);
    lv_color_t color = lv_color_make(r, g, b);

    lv_font_t * font = lv_freetype_font_create(font_name, LV_FREETYPE_FONT_RENDER_MODE_BITMAP, size, font_style[index]);
    if(!font) {
        return NULL;
    }

    lv_obj_t * label = lv_label_create(par);
    lv_obj_set_style_text_font(label, font, 0);
    lv_obj_set_style_text_opa(label, opa, 0);
    lv_obj_set_style_text_color(label, color, 0);
    lv_obj_set_pos(label, x, y);
    lv_obj_set_style_outline_color(label, color, 0);
    lv_obj_set_style_outline_width(label, 1, 0);

    uint8_t str[128];
    lv_random_utf8_chars(str, sizeof(str), LV_RND_UNICODE_ALPHANUM_AND_CJK_TABLE, LV_RND_UNICODE_ALPHANUM_AND_CJK_TABLE_LEN,
                         MAX_LABEL_CONTENT_LEN);
    lv_label_set_text(label, (char *)str);
    return label;
}
static void label_delete(lv_obj_t * label)
{
    const lv_font_t * font = lv_obj_get_style_text_font(label, 0);
    LV_ASSERT_NULL(font);
    lv_freetype_font_delete((lv_font_t *)font);
    lv_obj_del(label);
}
static void label_delete_all(stress_test_ctx_t * ctx)
{
    for(int i = 0; i < ctx->config.label_cnt; i++) {
        lv_obj_t * label = ctx->label_arr[i];
        if(label) {
            label_delete(label);
            ctx->label_arr[i] = NULL;
        }
    }
}
static void update_cb(void)
{
    stress_test_ctx_t * ctx = &g_ctx;
    uint32_t label_index = lv_rand(0, ctx->config.label_cnt - 1);
    uint32_t font_index = lv_rand(0, ctx->config.font_cnt - 1);
    uint32_t font_size = lv_rand(0, MAX_FONT_SIZE);
    uint32_t label_x = lv_rand(0, LV_HOR_RES) - LV_HOR_RES / 2;
    uint32_t label_y = lv_rand(0, LV_VER_RES);

    lv_obj_t * label = ctx->label_arr[label_index];
    if(label) {
        label_delete(label);
        ctx->label_arr[label_index] = NULL;
    }
    else {
        const char * pathname = ctx->config.font_name_arr[font_index];
        LV_ASSERT_NULL(pathname);
        label = label_create(
                    pathname,
                    ctx->par,
                    (int)font_size,
                    (int)label_x,
                    (int)label_y);
        ctx->label_arr[label_index] = label;
    }
}

void setUp(void)
{
    lv_freetype_init(LV_FREETYPE_CACHE_FT_GLYPH_CNT);

    g_ctx.par = lv_scr_act();

    g_ctx.config.loop_cnt = MAX_LOOP_CNT;
    g_ctx.config.label_cnt = MAX_LABEL_CNT;
    g_ctx.config.font_name_arr = font_name_arr;
    g_ctx.config.font_cnt = sizeof(font_name_arr) / sizeof(font_name_arr[0]);

    size_t arr_size = sizeof(lv_obj_t *) * g_ctx.config.label_cnt;
    g_ctx.label_arr = lv_malloc(arr_size);
    LV_ASSERT_MALLOC(g_ctx.label_arr);
    lv_memzero(g_ctx.label_arr, arr_size);

    lv_rand_set_seed(RND_START_SEED);
}

void tearDown(void)
{
    label_delete_all(&g_ctx);
    lv_freetype_uninit();

    lv_free(g_ctx.label_arr);
}

void test_font_stress(void)
{
    for(uint32_t i = 0; g_ctx.config.loop_cnt > 0; g_ctx.config.loop_cnt--) {
        update_cb();
        lv_refr_now(NULL);

        if(g_ctx.config.loop_cnt % CAPTURE_SKIP_FRAMES == 0) {
            char buf[64];
            TEST_FREETYPE_ASSERT_EQUAL_SCREENSHOT(i);
            i++;
        }
    }
}

#else

void setUp(void)
{
}

void tearDown(void)
{
}

void test_font_stress(void)
{
}

#endif

#endif
