#include "step4.h"

__attribute__((unused))
static const char *TAG = "step4";

static lv_obj_t *old_scr;
static lv_obj_t* scr;
static lv_obj_t* img;

static const int height = 22;
static const int width = 10;

static int board[height][width] = {0};
static lv_obj_t* visual_board[height][width] = {0};

static lv_obj_t* line_clear_img;
static lv_style_t game_over_style;
static lv_obj_t* game_over_label;

#define MUSIC_FILE MOUNT_POINT "/piano.wav"

#ifdef TETRIS_USE_FLASH_BG_IMG
LV_IMG_DECLARE(bg);
static const void* BACKGROUND_SRC = (void*) &bg;
#else 
static const void* BACKGROUND_SRC = (void*)"A:" MOUNT_POINT "/bg.bin";
#endif

#ifdef TETRIS_USE_FLASH_IMG
LV_IMG_DECLARE(clear);
LV_IMG_DECLARE(db);
LV_IMG_DECLARE(green);
LV_IMG_DECLARE(lb);
LV_IMG_DECLARE(orange);
LV_IMG_DECLARE(purple);
LV_IMG_DECLARE(red);
LV_IMG_DECLARE(yellow);

static const void* LINE_CLEAR_SRC = (void*) &clear;

static const void* PIECE_IMG_SRC[] = {
    NULL,
    &lb,
    &db,
    &orange,
    &yellow,
    &green,
    &purple,
    &red,
};
#else
static const void* LINE_CLEAR_SRC = (void*)"A:" MOUNT_POINT "/clear.bin";

static const void* PIECE_IMG_SRC[] = {
    NULL,
    "A:" MOUNT_POINT "/lb.bin",
    "A:" MOUNT_POINT "/db.bin",
    "A:" MOUNT_POINT "/orange.bin",
    "A:" MOUNT_POINT "/yellow.bin",
    "A:" MOUNT_POINT "/green.bin",
    "A:" MOUNT_POINT "/purple.bin",
    "A:" MOUNT_POINT "/red.bin",
};
#endif

static bool game = true;

static int target_score = 0;
static int score = 0;

static int piece = 0;
static int piece_rotation = 0;
static int piece_location[] = {0, 0};
static int piece_nodes[4][2] = {
    {0, 0},
    {0, 0},
    {0, 0},
    {0, 0},
};

lv_obj_t* piece_imgs[4] = {};

static bool music_playing;

static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<> piece_dist(2, 7);

static void generate_block();
static void show_board();
static void get_node_locations();
static bool check_overlap();

static void line_clear(int hi);
static void check_line_clears();
static void place_piece();

static void move_left();
static void move_right();
static void drop();
static void rotate_block();
static void update_score();
static void clear_board();


static int bbcc(int i) { // [0,1,2,3] -> [ 0, 0,+1,+1]
    const int map[] = {0, 0, 1, 1};
    return map[i];
}
static int bccb(int i) { // [0,1,2,3] -> [ 0,+1,+1, 0]
    const int map[] = {0, 1, 1, 0};
    return map[i];
}
static int ccbb(int i) { // [0,1,2,3] -> [+1,+1, 0, 0]
    const int map[] = {1, 1, 0, 0};
    return map[i];
}

static int acca(int i) { // [0,1,2,3] -> [-1,+1,+1,-1]
    const int map[] = {-1, 1, 1, -1};
    return map[i];
}
static int aacc(int i) { // [0,1,2,3] -> [-1,-1,+1,+1]
    const int map[] = {-1, -1, 1, 1};
    return map[i];
}
static int babc(int i) { // [0,1,2,3] -> [ 0,-1, 0,+1]
    const int map[] = {0, -1, 0, 1};
    return map[i];
}
static int abcb(int i) { // [0,1,2,3] -> [-1, 0,+1, 0]
    const int map[] = {-1, 0, 1, 0};
    return map[i];
}

static int acdb(int i) { // [0,1,2,3] -> [-1,+1,+2, 0]
    const int map[] = {-1, +1, 2, 0};
    return map[i];
}
static int bacd(int i) { // [0,1,2,3] -> [ 0,-1,+1,+2]
    const int map[] = {0, -1, 1, 2};
    return map[i];
}
static int dcab(int i) { // [0,1,2,3] -> [+2,+1,-1, 0]
    const int map[] = {2, 1, -1, 0};
    return map[i];
}
static int bdca(int i) { // [0,1,2,3] -> [ 0,+2,+1,-1]
    const int map[] = {0, 2, 1, -1};
    return map[i];
}

static void init_screen() {
    while (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdFALSE) vTaskDelay(pdMS_TO_TICKS(10));
    scr = lv_obj_create(NULL);

    img = lv_img_create(scr);
    lv_img_set_src(img, BACKGROUND_SRC);
    lv_obj_align(img, LV_ALIGN_CENTER, 0, 0);

    line_clear_img = lv_img_create(scr);
    lv_obj_add_flag(line_clear_img, LV_OBJ_FLAG_HIDDEN);
    lv_img_set_src(line_clear_img, LINE_CLEAR_SRC);
    lv_obj_align(line_clear_img, LV_ALIGN_BOTTOM_LEFT, 159, -(height*16));

    lv_style_init(&game_over_style);
    lv_style_set_text_color(&game_over_style, lv_color_white());
    lv_style_set_bg_color(&game_over_style, lv_color_black());
    lv_style_set_bg_opa(&game_over_style, LV_OPA_100);
    lv_style_set_text_align(&game_over_style, LV_TEXT_ALIGN_CENTER);

    game_over_label = lv_label_create(scr);
    lv_obj_add_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);
    lv_obj_align(game_over_label, LV_ALIGN_LEFT_MID, 0, 0);
    lv_obj_set_width(game_over_label, 160);
    lv_obj_add_style(game_over_label, &game_over_style, LV_STATE_DEFAULT);

    for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            visual_board[h][w] = lv_img_create(scr);
            lv_obj_align(visual_board[h][w], LV_ALIGN_BOTTOM_LEFT, 159 + w*16, -(h*16));
            lv_obj_add_flag(visual_board[h][w], LV_OBJ_FLAG_HIDDEN);
        }
    }

    for (int i = 0; i < 4; i++) {
        piece_imgs[i] = lv_img_create(scr);
        lv_obj_align(piece_imgs[i], LV_ALIGN_BOTTOM_LEFT, 159, -320);
    }
    
    old_scr = lv_scr_act();
    lv_scr_load(scr);

    xSemaphoreGive(xGuiSemaphore);

    play_clip_wav(MUSIC_FILE, true, true, 4, 0);
    music_playing = true;
}

static void deinit_screen() {
    while (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdFALSE) vTaskDelay(pdMS_TO_TICKS(10));
    lv_scr_load(old_scr);
    lv_obj_del(scr);
    xSemaphoreGive(xGuiSemaphore);

    if (!stop_clip(MUSIC_FILE, pdMS_TO_TICKS(1000))) {
        ESP_LOGW(TAG, "Can't stop, addicted to the shindig.");
    }
    music_playing = false;
}

static void reveal_board() {
for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            if (PIECE_IMG_SRC[board[h][w]]) {
                lv_img_set_src(visual_board[h][w], PIECE_IMG_SRC[board[h][w]]);
                lv_obj_clear_flag(visual_board[h][w], LV_OBJ_FLAG_HIDDEN);
            }
        }
    }
}
static void hide_board() {
for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            lv_obj_add_flag(visual_board[h][w], LV_OBJ_FLAG_HIDDEN);
            lv_img_set_src(visual_board[h][w], NULL);
        }
    }
}

bool play_game(int time, int required_score) {
    game = true;
    target_score = required_score;
    score = 0;
    update_score();
    set_module_time(time);
    start_module_timer();

    clear_board();

    generate_block();
    show_board();

    ButtonKey button;
    while (get_button_pressed(&button));
    // SwitchKey switch_;

    const TickType_t first_repeat_time = pdMS_TO_TICKS(700);
    const TickType_t repeat_time = pdMS_TO_TICKS(100);
    TickType_t down_held = 0;
    TickType_t last_repeat = 0;

    while(game) {
        while (get_button_pressed(&button)) {
            switch (button) {
                case ButtonKey::b1:
                move_left();
                break;
                case ButtonKey::b2:
                move_right();
                break;
                case ButtonKey::b3:
                down_held = xTaskGetTickCount();
                last_repeat = 0;
                drop();
                break;
                case ButtonKey::b4:
                rotate_block();
                break;
            }

            show_board();

            if (score >= required_score) {
                stop_module_timer();
                return true;
            } 
        }
        while (get_button_released(&button)) {
            if (button == ButtonKey::b3) {
                down_held = 0;
            }
        }

        // Toggle music with switch4
        SwitchKey switch_;
        while (get_switch_flipped(&switch_)) {
            if (switch_ == SwitchKey::s4) {
                if (music_playing) {
                    stop_clip(MUSIC_FILE, 0);
                    music_playing = false;
                } else {
                    play_clip_wav(MUSIC_FILE, true, true, 4, 0);
                    music_playing = true;
                }
            }
        }

        if (down_held != 0) {
            // check repeat
            TickType_t now = xTaskGetTickCount();
            if (now - down_held > first_repeat_time) {
                // repeat!
                if (now - last_repeat > repeat_time) {
                    last_repeat = now;
                    drop();
                    show_board();

                    if (score >= required_score) {
                        stop_module_timer();
                        return true;
                    } 
                }
            }
        }

        if (get_module_time() <= 0) {
            stop_module_timer();
            strike("Out of time");
            return false;
        }
        // if (get_switch_flipped(&switch_)) {
        //     printf("%d\n", piece);
        //     for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
        //         int* p = piece_nodes[i];
        //         printf("PieceLocation: %d, %d\n", p[0], p[1]);
        //     }
        //     printf("PieceRotation: %d\n", piece_rotation);
        // }

        vTaskDelay(pdMS_TO_TICKS(10));
    }

    // game over
    ESP_LOGI(TAG, "Game Over. Score: %d", score);
    stop_module_timer();
    strike("Out of room");
    return false;
}

static void complete() {
    if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
        reveal_board();
        lv_label_set_text(game_over_label, "Winner!\nPress any button to continue");
        lv_obj_clear_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);
        xSemaphoreGive(xGuiSemaphore);
    }
    vTaskDelay(pdMS_TO_TICKS(500));
    while (get_button_pressed(nullptr));
    while (1) {
        if (get_button_pressed(nullptr)) break;
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
        lv_obj_add_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);
        hide_board();
        xSemaphoreGive(xGuiSemaphore);
    }
}

static void fail() {
    if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
        lv_label_set_text(game_over_label, "Game Over\nPress any button to continue");
        lv_obj_clear_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);

        reveal_board();
        xSemaphoreGive(xGuiSemaphore);
    }
    vTaskDelay(pdMS_TO_TICKS(2000));
    while (get_button_pressed(nullptr));
    while (1) {
        if (get_button_pressed(nullptr)) break;
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
        lv_obj_add_flag(game_over_label, LV_OBJ_FLAG_HIDDEN);
        hide_board();
        xSemaphoreGive(xGuiSemaphore);
    }
}

void step4() {
    // TODO: extract to helper function
    SemaphoreHandle_t continue_sem = xSemaphoreCreateBinary();
    if (continue_sem == nullptr) {
        ESP_LOGE(TAG, "could not create semaphore");
    }

    StarCodeEntry start_code = {
        .code = "3850",
        .display_text = "Starting...",
        .delay_us = 2'000'000,
        .callback = nullptr,
        .triggered_sem = continue_sem,
    };

    add_star_code(start_code);
    xSemaphoreTake(continue_sem, portMAX_DELAY);
    rm_star_code(start_code.code);
    vSemaphoreDelete(continue_sem);

    init_screen();

    while (!play_game(4*60*1000, 2)) fail();
    // TODO: create constants for common assets, and put them in a folder.
    play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
    complete();
    while (!play_game(4*60*1000, 4)) fail();
    play_clip_wav(MOUNT_POINT "/partdone.wav", true, false, 0, 0);
    complete();
    while (!play_game(7*60*1000, 8)) fail();
    play_clip_wav(MOUNT_POINT "/stepdone.wav", true, false, 1, 0);
    complete();

    deinit_screen();
}

static void show_board() {
    for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            if (board[h][w] == 9) {
                board[h][w] = 0;
            }
        }
    }

    for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
        int* p = piece_nodes[i];
        board[p[0]][p[1]] = 9;
    }

    if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
        for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
            int* p = piece_nodes[i];
            lv_obj_t* piece_img = piece_imgs[i];
            lv_obj_align(piece_img, LV_ALIGN_BOTTOM_LEFT, 159 + p[1]*16, -(p[0]*16));
        }
        xSemaphoreGive(xGuiSemaphore);
    }
}

static void generate_block() {
    int new_piece = piece_dist(gen);
    if (new_piece == piece) {
        new_piece = 1;
    }

    piece = new_piece;
    piece_rotation = 0;
    
    piece_location[0] = 18;
    piece_location[1] = 4;

    get_node_locations();

    for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
                int* p = piece_nodes[i];
                if (board[p[0]][p[1]] != 0) {
                    game = false;
                    return;
                }
            }
        }
    }

    if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
        for (int i = 0; i < 4; i++) {
            lv_obj_t* piece_img = piece_imgs[i];    
            lv_img_set_src(piece_img, PIECE_IMG_SRC[piece]);
        }
        xSemaphoreGive(xGuiSemaphore);
    }
}

static void rotate_block() {
    piece_rotation++;
    if (piece_rotation > 3) {
        piece_rotation = 0;
    }
    get_node_locations();

    // Check overlap without moving
    if (check_overlap()) {
        
        // Check overlap after moving up 1
        piece_location[0]--;
        get_node_locations();
        if (check_overlap()) {
            
            // Check overlap after moving down 1
            piece_location[0]++;
            piece_location[0]++;				
            get_node_locations();
            if (check_overlap()) {
                
                // Check overlap after moving left 1
                piece_location[0]--;
                piece_location[1]--;				
                get_node_locations();
                if (check_overlap()) {
                    
                    // Check overlap after moving right 1
                    piece_location[1]++;
                    piece_location[1]++;
                    get_node_locations();
                    if (check_overlap()) {
                        piece_location[1]--;
                        // This is Original Position
                        
                        // If Line Piece, check 2 left, 2 right, 2 up and 2 down
                        
                        // Check 2 left
                        if (piece == 1 && piece_rotation == 0) {
                            piece_location[1]-=2;
                            get_node_locations();
                            if (check_overlap()) {
                                piece_location[1]+=2;
                                get_node_locations();
                            }
                        // Check 2 up
                        } else if (piece == 1 && piece_rotation == 1) {
                            piece_location[0]+=2;
                            get_node_locations();
                            if (check_overlap()) {
                                piece_location[0]-=2;
                                get_node_locations();
                            }
                        // Check 2 right
                        } else if (piece == 1 && piece_rotation == 2) {
                            piece_location[1]+=2;
                            get_node_locations();
                            if (check_overlap()) {
                                piece_location[1]-=2;
                                get_node_locations();
                            }
                        // Check 2 down
                        } else if (piece == 1 && piece_rotation == 3) {
                            piece_location[0]-=2;
                            get_node_locations();
                            if (check_overlap()) {
                                piece_location[0]+=2;
                                get_node_locations();
                            }
                        }
                    }
                }
            }
        }
    }
}

// Check if nodes are outside of map or interposed onto placed nodes
static bool check_overlap() {
    for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
        int* p = piece_nodes[i];
        if (p[0] < 0 || p[1] < 0 || p[1] >= width) {
            return true;
        } else if (board[p[0]][p[1]] != 0 && board[p[0]][p[1]] != 9) {
            return true;
        }
    }
    return false;
}

static void move_left() {
    for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
        int* p = piece_nodes[i];
        if (p[1] == 0) {
            return;
        } else if (board[p[0]][p[1]-1] != 0 && board[p[0]][p[1]-1] != 9) {
            return;
        }
    }
    
    piece_location[1]--;
    get_node_locations();
}

static void move_right() {
    for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
        int* p = piece_nodes[i];
        if (p[1] == width-1) {
            return;
        } else if (board[p[0]][p[1]+1] != 0 && board[p[0]][p[1]+1] != 9) {
            return;
        }
    }
    piece_location[1]++;
    get_node_locations();
}

static void drop() {
    for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
        int* p = piece_nodes[i];
        if (p[0] == 0) {
            place_piece();
            check_line_clears();
            generate_block();
            return;
        } else if (board[p[0]-1][p[1]] != 0 && board[p[0]-1][p[1]] != 9) {
            place_piece();
            check_line_clears();
            generate_block();
            return;
        }
    }

    piece_location[0]--;
    get_node_locations();
}

static void place_piece() {
    for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            if (board[h][w] == 9) {
                board[h][w] = piece;
            }
        }
    }
    if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
        for (int i = 0; i < sizeof(piece_nodes)/sizeof(piece_nodes[0]); i++) {
            lv_obj_align(piece_imgs[i], LV_ALIGN_BOTTOM_LEFT, 159, -(height*16));
        }
        xSemaphoreGive(xGuiSemaphore);
    }
    ESP_LOGI(TAG, "Placed Piece: %d", piece);
}

static void get_node_locations() {
    piece_nodes[0][0] = piece_location[0];
    piece_nodes[0][1] = piece_location[1];
    if (piece == 1) {
        piece_nodes[0][0] = piece_location[0]-bacd(piece_rotation);
        piece_nodes[0][1] = piece_location[1]+acdb(piece_rotation);

        piece_nodes[1][0] = piece_location[0]-bbcc(piece_rotation);
        piece_nodes[1][1] = piece_location[1]+bccb(piece_rotation);

        piece_nodes[2][0] = piece_location[0]-bccb(piece_rotation);
        piece_nodes[2][1] = piece_location[1]+ccbb(piece_rotation);

        piece_nodes[3][0] = piece_location[0]-bdca(piece_rotation);
        piece_nodes[3][1] = piece_location[1]+dcab(piece_rotation);
    } else if (piece == 2) {
        piece_nodes[1][0] = piece_location[0]-babc(piece_rotation);
        piece_nodes[1][1] = piece_location[1]+abcb(piece_rotation);

        piece_nodes[2][0] = piece_location[0]+babc(piece_rotation);
        piece_nodes[2][1] = piece_location[1]-abcb(piece_rotation);

        piece_nodes[3][0] = piece_location[0]-aacc(piece_rotation);
        piece_nodes[3][1] = piece_location[1]+acca(piece_rotation);
    } else if (piece == 3) {
        piece_nodes[1][0] = piece_location[0]-babc(piece_rotation);
        piece_nodes[1][1] = piece_location[1]+abcb(piece_rotation);

        piece_nodes[2][0] = piece_location[0]+babc(piece_rotation);
        piece_nodes[2][1] = piece_location[1]-abcb(piece_rotation);

        piece_nodes[3][0] = piece_location[0]-acca(piece_rotation);
        piece_nodes[3][1] = piece_location[1]-aacc(piece_rotation);
    } else if (piece == 4) {
        piece_nodes[1][0] = piece_location[0]+1;
        piece_nodes[1][1] = piece_location[1];

        piece_nodes[2][0] = piece_location[0];
        piece_nodes[2][1] = piece_location[1]+1;

        piece_nodes[3][0] = piece_location[0]+1;
        piece_nodes[3][1] = piece_location[1]+1;
    } else if (piece == 5) {
        piece_nodes[1][0] = piece_location[0]-babc(piece_rotation);
        piece_nodes[1][1] = piece_location[1]+abcb(piece_rotation);

        piece_nodes[2][0] = piece_location[0]-abcb(piece_rotation);
        piece_nodes[2][1] = piece_location[1]-babc(piece_rotation);

        piece_nodes[3][0] = piece_location[0]-acca(piece_rotation);
        piece_nodes[3][1] = piece_location[1]-aacc(piece_rotation);
    } else if (piece == 6) {
        piece_nodes[1][0] = piece_location[0]-babc(piece_rotation);
        piece_nodes[1][1] = piece_location[1]+abcb(piece_rotation);

        piece_nodes[2][0] = piece_location[0]-abcb(piece_rotation);
        piece_nodes[2][1] = piece_location[1]-babc(piece_rotation);

        piece_nodes[3][0] = piece_location[0]+babc(piece_rotation);
        piece_nodes[3][1] = piece_location[1]-abcb(piece_rotation);
    } else if (piece == 7) {
        piece_nodes[1][0] = piece_location[0]+babc(piece_rotation);
        piece_nodes[1][1] = piece_location[1]-abcb(piece_rotation);

        piece_nodes[2][0] = piece_location[0]-abcb(piece_rotation);
        piece_nodes[2][1] = piece_location[1]-babc(piece_rotation);

        piece_nodes[3][0] = piece_location[0]-aacc(piece_rotation);
        piece_nodes[3][1] = piece_location[1]+acca(piece_rotation);
    }
}

static void check_line_clears() {
    for (int h = height-2; h >= 0; h--) {
        for (int w = 0; w < width; w++) {
            if (board[h][w] != 0 && board[h][w] != 9) {
                if (w == width-1) {
                    line_clear(h);
                }
            } else {
                break;
            }
        }
    }
}

static void update_score() {
    char buff[16] = {};
    sprintf(buff, "%d/%d", score, target_score);
    lcd_clear();
    lcd_print(1, 1, buff);
}

static void line_clear(int hi) {
    for (int h = hi; h < height; h++) {
        for (int w = 0; w < width; w++) {
            board[h][w] = board[h+1][w];
        }
    }
    for (int w = 0; w < width; w++) {
        board[height-1][w] = 0;
    }
    score++;
    update_score();

    if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
        lv_obj_align(line_clear_img, LV_ALIGN_BOTTOM_LEFT, 159, -(hi*16));
        xSemaphoreGive(xGuiSemaphore);
    }
    for (int i = 0; i < 3; i++) {
        if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
            lv_obj_clear_flag(line_clear_img, LV_OBJ_FLAG_HIDDEN);
            xSemaphoreGive(xGuiSemaphore);
        }
        vTaskDelay(pdMS_TO_TICKS(300));
        if (xSemaphoreTake(xGuiSemaphore, portMAX_DELAY) == pdTRUE) {
            lv_obj_add_flag(line_clear_img, LV_OBJ_FLAG_HIDDEN);
            xSemaphoreGive(xGuiSemaphore);
        }
        vTaskDelay(pdMS_TO_TICKS(300));
    }
}

void clear_board() {
    for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            board[h][w] = 0;
        }
    }
}
