#include "cmd_runtime.h"
#include "qrcode/qrcodegen.h"

#define USH_QRCODE_MAX_TEXT 640U
#define USH_QRCODE_MAX_VERSION 15
#define USH_QRCODE_BORDER 4U
#define USH_QRCODE_MAX_MODULES ((USH_QRCODE_MAX_VERSION * 4U) + 17U + (USH_QRCODE_BORDER * 2U))
#define USH_QRCODE_CANVAS_MAX 640U
#define USH_QRCODE_TTY_DISPLAY 1ULL
#define USH_QRCODE_TITLE_H 34
#define USH_QRCODE_FOOTER_H 34
#define USH_QRCODE_PAD 24
#define USH_QRCODE_CLOSE_W 44
#define USH_QRCODE_WINDOW_DEFAULT_W 520
#define USH_QRCODE_WINDOW_DEFAULT_H 600
#define USH_QRCODE_WINDOW_MIN_W 320
#define USH_QRCODE_WINDOW_MIN_H 380

#define USH_QRCODE_COLOR_DARK 0x00000000U
#define USH_QRCODE_COLOR_LIGHT 0x00FFFFFFU
#define USH_QRCODE_COLOR_DESKTOP 0x00F3F3F3U
#define USH_QRCODE_COLOR_TITLE 0x000078D7U
#define USH_QRCODE_COLOR_CLOSE 0x00E81123U
#define USH_QRCODE_COLOR_TEXT 0x00232323U
#define USH_QRCODE_COLOR_MUTED 0x00666666U
#define USH_QRCODE_COLOR_BORDER 0x00D0D0D0U
#define USH_QRCODE_COLOR_PANEL 0x00FFFFFFU

#define USH_QRCODE_GLYPH7(r0, r1, r2, r3, r4, r5, r6)                                                                  \
    (((u64)(r0) << 30U) | ((u64)(r1) << 25U) | ((u64)(r2) << 20U) | ((u64)(r3) << 15U) | ((u64)(r4) << 10U) |          \
     ((u64)(r5) << 5U) | (u64)(r6))

static unsigned int ush_qrcode_canvas[USH_QRCODE_CANVAS_MAX][USH_QRCODE_CANVAS_MAX];

static int ush_qrcode_clampi(int value, int min_value, int max_value) {
    if (value < min_value) {
        return min_value;
    }

    if (value > max_value) {
        return max_value;
    }

    return value;
}

static int ush_qrcode_u64_as_i32(u64 raw) {
    return (int)(i64)raw;
}

static char ush_qrcode_upper_char(char ch) {
    if (ch >= 'a' && ch <= 'z') {
        return (char)(ch - ('a' - 'A'));
    }

    return ch;
}

static u64 ush_qrcode_glyph_mask(char ch) {
    switch (ush_qrcode_upper_char(ch)) {
    case 'C':
        return USH_QRCODE_GLYPH7(14U, 17U, 16U, 16U, 16U, 17U, 14U);
    case 'D':
        return USH_QRCODE_GLYPH7(30U, 17U, 17U, 17U, 17U, 17U, 30U);
    case 'E':
        return USH_QRCODE_GLYPH7(31U, 16U, 16U, 30U, 16U, 16U, 31U);
    case 'L':
        return USH_QRCODE_GLYPH7(16U, 16U, 16U, 16U, 16U, 16U, 31U);
    case 'O':
        return USH_QRCODE_GLYPH7(14U, 17U, 17U, 17U, 17U, 17U, 14U);
    case 'P':
        return USH_QRCODE_GLYPH7(30U, 17U, 17U, 30U, 16U, 16U, 16U);
    case 'Q':
        return USH_QRCODE_GLYPH7(14U, 17U, 17U, 17U, 21U, 18U, 13U);
    case 'R':
        return USH_QRCODE_GLYPH7(30U, 17U, 17U, 30U, 20U, 18U, 17U);
    case 'S':
        return USH_QRCODE_GLYPH7(15U, 16U, 16U, 14U, 1U, 1U, 30U);
    case 'T':
        return USH_QRCODE_GLYPH7(31U, 4U, 4U, 4U, 4U, 4U, 4U);
    default:
        return 0ULL;
    }
}

static void ush_qrcode_fill_rect(int canvas_w, int canvas_h, int x, int y, int w, int h, unsigned int color) {
    int left;
    int top;
    int right;
    int bottom;
    int row;

    if (canvas_w <= 0 || canvas_h <= 0 || canvas_w > (int)USH_QRCODE_CANVAS_MAX ||
        canvas_h > (int)USH_QRCODE_CANVAS_MAX || w <= 0 || h <= 0) {
        return;
    }

    left = ush_qrcode_clampi(x, 0, canvas_w);
    top = ush_qrcode_clampi(y, 0, canvas_h);
    right = ush_qrcode_clampi(x + w, 0, canvas_w);
    bottom = ush_qrcode_clampi(y + h, 0, canvas_h);
    if (left >= right || top >= bottom) {
        return;
    }

    for (row = top; row < bottom; row++) {
        int col;
        for (col = left; col < right; col++) {
            ush_qrcode_canvas[row][col] = color;
        }
    }
}

static void ush_qrcode_stroke_rect(int canvas_w, int canvas_h, int x, int y, int w, int h, unsigned int color) {
    ush_qrcode_fill_rect(canvas_w, canvas_h, x, y, w, 1, color);
    ush_qrcode_fill_rect(canvas_w, canvas_h, x, y + h - 1, w, 1, color);
    ush_qrcode_fill_rect(canvas_w, canvas_h, x, y, 1, h, color);
    ush_qrcode_fill_rect(canvas_w, canvas_h, x + w - 1, y, 1, h, color);
}

static void ush_qrcode_draw_char(int canvas_w, int canvas_h, int x, int y, char ch, int scale, unsigned int color) {
    u64 mask = ush_qrcode_glyph_mask(ch);
    int row;

    if (mask == 0ULL || scale <= 0) {
        return;
    }

    for (row = 0; row < 7; row++) {
        int col;
        for (col = 0; col < 5; col++) {
            unsigned int bit_index = (unsigned int)((6 - row) * 5 + (4 - col));
            if ((mask & (1ULL << bit_index)) != 0ULL) {
                ush_qrcode_fill_rect(canvas_w, canvas_h, x + (col * scale), y + (row * scale), scale, scale, color);
            }
        }
    }
}

static void ush_qrcode_draw_text(int canvas_w, int canvas_h, int x, int y, const char *text, int scale,
                                 unsigned int color) {
    int cursor_x = x;

    if (text == (const char *)0 || scale <= 0) {
        return;
    }

    while (*text != '\0' && cursor_x + (5 * scale) < canvas_w) {
        if (*text != ' ') {
            ush_qrcode_draw_char(canvas_w, canvas_h, cursor_x, y, *text, scale, color);
        }
        cursor_x += 6 * scale;
        text++;
    }
}

static int ush_qrcode_streq_ignore_case(const char *left, const char *right) {
    u64 i = 0ULL;

    if (left == (const char *)0 || right == (const char *)0) {
        return 0;
    }

    while (left[i] != '\0' && right[i] != '\0') {
        char lc = left[i];
        char rc = right[i];

        if (lc >= 'a' && lc <= 'z') {
            lc = (char)(lc - ('a' - 'A'));
        }

        if (rc >= 'a' && rc <= 'z') {
            rc = (char)(rc - ('a' - 'A'));
        }

        if (lc != rc) {
            return 0;
        }

        i++;
    }

    return (left[i] == '\0' && right[i] == '\0') ? 1 : 0;
}

static int ush_qrcode_parse_ecc(const char *text, enum qrcodegen_Ecc *out_ecc) {
    if (text == (const char *)0 || out_ecc == (enum qrcodegen_Ecc *)0) {
        return 0;
    }

    if (ush_qrcode_streq_ignore_case(text, "L") != 0 || ush_qrcode_streq_ignore_case(text, "LOW") != 0) {
        *out_ecc = qrcodegen_Ecc_LOW;
        return 1;
    }

    if (ush_qrcode_streq_ignore_case(text, "M") != 0 || ush_qrcode_streq_ignore_case(text, "MEDIUM") != 0) {
        *out_ecc = qrcodegen_Ecc_MEDIUM;
        return 1;
    }

    if (ush_qrcode_streq_ignore_case(text, "Q") != 0 || ush_qrcode_streq_ignore_case(text, "QUARTILE") != 0) {
        *out_ecc = qrcodegen_Ecc_QUARTILE;
        return 1;
    }

    if (ush_qrcode_streq_ignore_case(text, "H") != 0 || ush_qrcode_streq_ignore_case(text, "HIGH") != 0) {
        *out_ecc = qrcodegen_Ecc_HIGH;
        return 1;
    }

    return 0;
}

static void ush_qrcode_usage(void) {
    ush_writeln("usage: qrcode [--ecc <L|M|Q|H>] <text>");
    ush_writeln("       qrcode --help");
    ush_writeln("note: pipeline input supported when <text> omitted");
}

/* return: 0 fail, 1 ok, 2 help */
static int ush_qrcode_parse_args(const char *arg, char *out_text, u64 out_text_size, enum qrcodegen_Ecc *out_ecc) {
    char first[USH_PATH_MAX];
    char second[USH_PATH_MAX];
    const char *rest = "";
    const char *rest2 = "";

    if (out_text == (char *)0 || out_text_size == 0ULL || out_ecc == (enum qrcodegen_Ecc *)0) {
        return 0;
    }

    *out_ecc = qrcodegen_Ecc_LOW;
    out_text[0] = '\0';

    if (arg == (const char *)0 || arg[0] == '\0') {
        if (ush_pipeline_stdin_text != (const char *)0 && ush_pipeline_stdin_text[0] != '\0') {
            if (ush_pipeline_stdin_len + 1ULL > out_text_size) {
                return 0;
            }

            ush_copy(out_text, out_text_size, ush_pipeline_stdin_text);
            return 1;
        }

        return 0;
    }

    if (ush_split_first_and_rest(arg, first, (u64)sizeof(first), &rest) == 0) {
        return 0;
    }

    if (ush_streq(first, "--help") != 0 || ush_streq(first, "-h") != 0) {
        return 2;
    }

    if (ush_streq(first, "--ecc") != 0) {
        if (ush_split_first_and_rest(rest, second, (u64)sizeof(second), &rest2) == 0) {
            return 0;
        }

        if (ush_qrcode_parse_ecc(second, out_ecc) == 0) {
            return 0;
        }

        if (rest2 == (const char *)0 || rest2[0] == '\0') {
            if (ush_pipeline_stdin_text != (const char *)0 && ush_pipeline_stdin_text[0] != '\0') {
                if (ush_pipeline_stdin_len + 1ULL > out_text_size) {
                    return 0;
                }

                ush_copy(out_text, out_text_size, ush_pipeline_stdin_text);
                return 1;
            }

            return 0;
        }

        ush_copy(out_text, out_text_size, rest2);
        return 1;
    }

    if (first[0] == '-' && first[1] == '-') {
        const char *prefix = "--ecc=";
        u64 i = 0ULL;
        int match = 1;

        while (prefix[i] != '\0') {
            if (first[i] != prefix[i]) {
                match = 0;
                break;
            }

            i++;
        }

        if (match != 0) {
            if (first[i] == '\0') {
                return 0;
            }

            if (ush_qrcode_parse_ecc(&first[i], out_ecc) == 0) {
                return 0;
            }

            if (rest == (const char *)0 || rest[0] == '\0') {
                if (ush_pipeline_stdin_text != (const char *)0 && ush_pipeline_stdin_text[0] != '\0') {
                    if (ush_pipeline_stdin_len + 1ULL > out_text_size) {
                        return 0;
                    }

                    ush_copy(out_text, out_text_size, ush_pipeline_stdin_text);
                    return 1;
                }

                return 0;
            }

            ush_copy(out_text, out_text_size, rest);
            return 1;
        }
    }

    ush_copy(out_text, out_text_size, arg);
    return 1;
}

static void ush_qrcode_emit_ascii(const uint8_t qrcode[]) {
    int size = qrcodegen_getSize(qrcode);
    int border = (int)USH_QRCODE_BORDER;
    int limit;
    int y;
    int x;

    limit = size + border;

    for (y = -border; y < limit; y++) {
        for (x = -border; x < limit; x++) {
            int dark = (x >= 0 && y >= 0 && x < size && y < size && qrcodegen_getModule(qrcode, x, y)) ? 1 : 0;
            ush_write(dark != 0 ? "##" : "  ");
        }

        ush_write_char('\n');
    }
}

static void ush_qrcode_choose_window_geometry(const cleonos_fb_info *fb_info, int *out_x, int *out_y, int *out_w,
                                              int *out_h) {
    int screen_w = 800;
    int screen_h = 600;
    int max_w;
    int max_h;
    int win_w;
    int win_h;

    if (fb_info != (const cleonos_fb_info *)0 && fb_info->width > 0ULL && fb_info->height > 0ULL &&
        fb_info->width <= 4096ULL && fb_info->height <= 4096ULL) {
        screen_w = (int)fb_info->width;
        screen_h = (int)fb_info->height;
    }

    max_w = screen_w - 80;
    max_h = screen_h - 96;
    if (max_w < USH_QRCODE_WINDOW_MIN_W) {
        max_w = screen_w;
    }
    if (max_h < USH_QRCODE_WINDOW_MIN_H) {
        max_h = screen_h;
    }

    win_w = ush_qrcode_clampi(USH_QRCODE_WINDOW_DEFAULT_W, USH_QRCODE_WINDOW_MIN_W, max_w);
    win_h = ush_qrcode_clampi(USH_QRCODE_WINDOW_DEFAULT_H, USH_QRCODE_WINDOW_MIN_H, max_h);
    if (win_w > (int)USH_QRCODE_CANVAS_MAX) {
        win_w = (int)USH_QRCODE_CANVAS_MAX;
    }
    if (win_h > (int)USH_QRCODE_CANVAS_MAX) {
        win_h = (int)USH_QRCODE_CANVAS_MAX;
    }

    *out_w = win_w;
    *out_h = win_h;
    *out_x = (screen_w > win_w) ? ((screen_w - win_w) / 2) : 0;
    *out_y = (screen_h > win_h) ? ((screen_h - win_h) / 2) : 0;
}

static int ush_qrcode_draw_window_canvas(const uint8_t qrcode[], int canvas_w, int canvas_h) {
    int qr_size = qrcodegen_getSize(qrcode);
    int border = (int)USH_QRCODE_BORDER;
    int side_modules;
    int content_w;
    int content_h;
    int qr_area;
    int module_pixels;
    int side_pixels;
    int qr_x;
    int qr_y;
    int y;

    if (qrcode == (const uint8_t *)0 || qr_size <= 0 || canvas_w <= 0 || canvas_h <= 0 ||
        canvas_w > (int)USH_QRCODE_CANVAS_MAX || canvas_h > (int)USH_QRCODE_CANVAS_MAX) {
        return 0;
    }

    side_modules = qr_size + (border * 2);
    if (side_modules <= 0 || side_modules > (int)USH_QRCODE_MAX_MODULES) {
        return 0;
    }

    content_w = canvas_w - (USH_QRCODE_PAD * 2);
    content_h = canvas_h - USH_QRCODE_TITLE_H - USH_QRCODE_FOOTER_H - (USH_QRCODE_PAD * 2);
    qr_area = (content_w < content_h) ? content_w : content_h;
    module_pixels = qr_area / side_modules;
    if (module_pixels <= 0) {
        return 0;
    }

    side_pixels = side_modules * module_pixels;
    qr_x = (canvas_w - side_pixels) / 2;
    qr_y = USH_QRCODE_TITLE_H + USH_QRCODE_PAD + ((content_h - side_pixels) / 2);

    ush_qrcode_fill_rect(canvas_w, canvas_h, 0, 0, canvas_w, canvas_h, USH_QRCODE_COLOR_DESKTOP);
    ush_qrcode_fill_rect(canvas_w, canvas_h, 0, 0, canvas_w, USH_QRCODE_TITLE_H, USH_QRCODE_COLOR_TITLE);
    ush_qrcode_fill_rect(canvas_w, canvas_h, canvas_w - USH_QRCODE_CLOSE_W, 0, USH_QRCODE_CLOSE_W, USH_QRCODE_TITLE_H,
                         USH_QRCODE_COLOR_CLOSE);
    ush_qrcode_draw_text(canvas_w, canvas_h, 14, 10, "QRCODE", 1, USH_QRCODE_COLOR_LIGHT);
    ush_qrcode_fill_rect(canvas_w, canvas_h, canvas_w - 28, 11, 14, 2, USH_QRCODE_COLOR_LIGHT);
    ush_qrcode_fill_rect(canvas_w, canvas_h, canvas_w - 28, 22, 14, 2, USH_QRCODE_COLOR_LIGHT);
    ush_qrcode_fill_rect(canvas_w, canvas_h, canvas_w - 22, 15, 2, 6, USH_QRCODE_COLOR_LIGHT);

    ush_qrcode_fill_rect(canvas_w, canvas_h, qr_x - 10, qr_y - 10, side_pixels + 20, side_pixels + 20,
                         USH_QRCODE_COLOR_PANEL);
    ush_qrcode_stroke_rect(canvas_w, canvas_h, qr_x - 10, qr_y - 10, side_pixels + 20, side_pixels + 20,
                           USH_QRCODE_COLOR_BORDER);
    ush_qrcode_fill_rect(canvas_w, canvas_h, qr_x, qr_y, side_pixels, side_pixels, USH_QRCODE_COLOR_LIGHT);

    for (y = -border; y < qr_size + border; y++) {
        int x;
        for (x = -border; x < qr_size + border; x++) {
            int dark = (x >= 0 && y >= 0 && x < qr_size && y < qr_size && qrcodegen_getModule(qrcode, x, y)) ? 1 : 0;
            if (dark != 0) {
                int px = qr_x + ((x + border) * module_pixels);
                int py = qr_y + ((y + border) * module_pixels);
                ush_qrcode_fill_rect(canvas_w, canvas_h, px, py, module_pixels, module_pixels, USH_QRCODE_COLOR_DARK);
            }
        }
    }

    ush_qrcode_draw_text(canvas_w, canvas_h, 22, canvas_h - 24, "PRESS Q TO CLOSE", 1, USH_QRCODE_COLOR_MUTED);
    return 1;
}

static int ush_qrcode_present_window(u64 window_id, int width, int height) {
    cleonos_wm_present_req req;

    if (window_id == 0ULL || width <= 0 || height <= 0) {
        return 0;
    }

    req.window_id = window_id;
    req.pixels_ptr = (u64)(usize)&ush_qrcode_canvas[0][0];
    req.src_width = (u64)(unsigned int)width;
    req.src_height = (u64)(unsigned int)height;
    req.src_pitch_bytes = (u64)USH_QRCODE_CANVAS_MAX * 4ULL;
    return (cleonos_sys_wm_present(&req) != 0ULL) ? 1 : 0;
}

static int ush_qrcode_close_hit(int width, int local_x, int local_y) {
    return (local_x >= width - USH_QRCODE_CLOSE_W && local_x < width && local_y >= 0 && local_y < USH_QRCODE_TITLE_H)
               ? 1
               : 0;
}

static void ush_qrcode_window_loop(u64 window_id, int width, int height, int x, int y) {
    int running = 1;
    int dragging = 0;
    int drag_offset_x = 0;
    int drag_offset_y = 0;

    (void)height;

    while (running != 0) {
        u64 budget = 0ULL;
        int handled = 0;

        while (budget < 64ULL) {
            cleonos_wm_event event;
            ush_zero(&event, (u64)sizeof(event));
            if (cleonos_sys_wm_poll_event(window_id, &event) == 0ULL) {
                break;
            }

            handled = 1;
            if (event.type == CLEONOS_WM_EVENT_KEY) {
                if (event.arg0 == (u64)'q' || event.arg0 == (u64)'Q' || event.arg0 == 27ULL || event.arg0 == 13ULL) {
                    running = 0;
                    break;
                }
            } else if (event.type == CLEONOS_WM_EVENT_MOUSE_BUTTON) {
                u64 buttons = event.arg0;
                u64 changed = event.arg1;
                int local_x = ush_qrcode_u64_as_i32(event.arg2);
                int local_y = ush_qrcode_u64_as_i32(event.arg3);
                int left_changed = ((changed & 0x1ULL) != 0ULL) ? 1 : 0;
                int left_down = ((buttons & 0x1ULL) != 0ULL) ? 1 : 0;

                if (left_changed != 0) {
                    if (left_down == 0) {
                        dragging = 0;
                    } else if (ush_qrcode_close_hit(width, local_x, local_y) != 0) {
                        running = 0;
                        break;
                    } else if (local_y >= 0 && local_y < USH_QRCODE_TITLE_H) {
                        dragging = 1;
                        drag_offset_x = local_x;
                        drag_offset_y = local_y;
                    }
                }
            } else if (event.type == CLEONOS_WM_EVENT_MOUSE_MOVE && dragging != 0) {
                cleonos_wm_move_req move_req;
                x = ush_qrcode_u64_as_i32(event.arg0) - drag_offset_x;
                y = ush_qrcode_u64_as_i32(event.arg1) - drag_offset_y;
                move_req.window_id = window_id;
                move_req.x = (u64)(i64)x;
                move_req.y = (u64)(i64)y;
                (void)cleonos_sys_wm_move(&move_req);
            }

            budget++;
        }

        if (running == 0) {
            break;
        }

        if (handled != 0) {
            (void)cleonos_sys_yield();
        } else {
            (void)cleonos_sys_sleep_ticks(1ULL);
        }
    }
}

static int ush_qrcode_emit_window(const uint8_t qrcode[]) {
    int qr_size = qrcodegen_getSize(qrcode);
    cleonos_fb_info fb_info;
    cleonos_wm_create_req create_req;
    u64 old_tty;
    u64 window_id;
    int win_x;
    int win_y;
    int win_w;
    int win_h;

    if (qr_size <= 0) {
        return 0;
    }

    ush_zero(&fb_info, (u64)sizeof(fb_info));
    if (cleonos_sys_fb_info(&fb_info) == 0ULL || fb_info.width == 0ULL || fb_info.height == 0ULL ||
        fb_info.bpp != 32ULL) {
        ush_writeln("qrcode: desktop unavailable, fallback to ascii");
        ush_qrcode_emit_ascii(qrcode);
        return 1;
    }

    ush_qrcode_choose_window_geometry(&fb_info, &win_x, &win_y, &win_w, &win_h);
    if (ush_qrcode_draw_window_canvas(qrcode, win_w, win_h) == 0) {
        ush_writeln("qrcode: desktop window too small");
        return 0;
    }

    old_tty = cleonos_sys_tty_active();
    if (old_tty != USH_QRCODE_TTY_DISPLAY) {
        (void)cleonos_sys_tty_switch(USH_QRCODE_TTY_DISPLAY);
    }

    create_req.x = (u64)(i64)win_x;
    create_req.y = (u64)(i64)win_y;
    create_req.width = (u64)(unsigned int)win_w;
    create_req.height = (u64)(unsigned int)win_h;
    create_req.flags = CLEONOS_WM_FLAG_TOPMOST;
    window_id = cleonos_sys_wm_create(&create_req);
    if (window_id == 0ULL) {
        if (old_tty != USH_QRCODE_TTY_DISPLAY) {
            (void)cleonos_sys_tty_switch(old_tty);
        }
        ush_writeln("qrcode: wm window create failed, fallback to ascii");
        ush_qrcode_emit_ascii(qrcode);
        return 1;
    }

    if (ush_qrcode_present_window(window_id, win_w, win_h) == 0) {
        (void)cleonos_sys_wm_destroy(window_id);
        if (old_tty != USH_QRCODE_TTY_DISPLAY) {
            (void)cleonos_sys_tty_switch(old_tty);
        }
        ush_writeln("qrcode: wm present failed");
        return 0;
    }

    (void)cleonos_sys_wm_set_focus(window_id);
    ush_qrcode_window_loop(window_id, win_w, win_h, win_x, win_y);
    (void)cleonos_sys_wm_destroy(window_id);
    if (old_tty != USH_QRCODE_TTY_DISPLAY) {
        (void)cleonos_sys_tty_switch(old_tty);
    }

    return 1;
}

static int ush_cmd_qrcode(const char *arg) {
    char text[USH_QRCODE_MAX_TEXT];
    enum qrcodegen_Ecc ecc;
    uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(USH_QRCODE_MAX_VERSION)];
    uint8_t temp[qrcodegen_BUFFER_LEN_FOR_VERSION(USH_QRCODE_MAX_VERSION)];
    int parse_ret;
    int ok;

    parse_ret = ush_qrcode_parse_args(arg, text, (u64)sizeof(text), &ecc);
    if (parse_ret == 2) {
        ush_qrcode_usage();
        return 1;
    }

    if (parse_ret == 0 || text[0] == '\0') {
        ush_qrcode_usage();
        return 0;
    }

    ok = qrcodegen_encodeText(text, temp, qrcode, ecc, qrcodegen_VERSION_MIN, USH_QRCODE_MAX_VERSION,
                              qrcodegen_Mask_AUTO, true);

    if (ok == 0) {
        ush_writeln("qrcode: encode failed (input too long or invalid)");
        return 0;
    }

    return ush_qrcode_emit_window(qrcode);
}

int cleonos_app_main(void) {
    ush_cmd_ctx ctx;
    ush_cmd_ret ret;
    ush_state sh;
    char initial_cwd[USH_PATH_MAX];
    int has_context = 0;
    int success = 0;
    const char *arg = "";

    ush_zero(&ctx, (u64)sizeof(ctx));
    ush_zero(&ret, (u64)sizeof(ret));
    ush_init_state(&sh);
    ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd);

    if (ush_command_ctx_read(&ctx) != 0) {
        if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "qrcode") != 0) {
            has_context = 1;
            arg = ctx.arg;
            if (ctx.cwd[0] == '/') {
                ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd);
                ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd);
            }
        }
    }

    success = ush_cmd_qrcode(arg);

    if (has_context != 0) {
        if (ush_streq(sh.cwd, initial_cwd) == 0) {
            ret.flags |= USH_CMD_RET_FLAG_CWD;
            ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd);
        }

        if (sh.exit_requested != 0) {
            ret.flags |= USH_CMD_RET_FLAG_EXIT;
            ret.exit_code = sh.exit_code;
        }

        (void)ush_command_ret_write(&ret);
    }

    return (success != 0) ? 0 : 1;
}
