/*
 * render.cpp
 * Rendering for the standalone MontaukOS Word Processor
 * Copyright (c) 2026 Daniel Hammer
 */

#include "wordprocessor.hpp"

static int wp_ui_line_height() {
    if (g_ui_font && g_ui_font->valid)
        return g_ui_font->get_line_height(fonts::UI_SIZE);
    return fonts::UI_SIZE;
}

static void wp_draw_ui_text(Canvas& c, int x, int y, const char* text, Color color) {
    draw_text(c, g_ui_font, x, y, text, color, fonts::UI_SIZE);
}

static void wp_draw_ui_button(Canvas& c, int x, int y, int w, int h,
                              const char* label, Color bg, Color fg, int radius = 3) {
    c.fill_rounded_rect(x, y, w, h, radius, bg);
    int tw = text_width(g_ui_font, label, fonts::UI_SIZE);
    int th = wp_ui_line_height();
    draw_text(c, g_ui_font, x + (w - tw) / 2, y + (h - th) / 2, label, fg, fonts::UI_SIZE);
}

static void wp_draw_ui_icon_button(Canvas& c, int x, int y, int w, int h,
                                   const SvgIcon& icon, const char* fallback,
                                   Color bg, Color fg, int radius = 3) {
    c.fill_rounded_rect(x, y, w, h, radius, bg);
    if (icon.pixels) {
        c.icon(x + (w - icon.width) / 2, y + (h - icon.height) / 2, icon);
        return;
    }
    wp_draw_ui_button(c, x, y, w, h, fallback, bg, fg, radius);
}

static void wp_draw_context_menu(Canvas& c, WordProcessorState* wp) {
    if (!wp || !wp->context_menu.open) return;

    mtk::Theme theme = mtk::make_theme(colors::ACCENT);
    theme.border = colors::BORDER;
    theme.text = colors::TEXT_COLOR;

    mtk::ContextMenuItem items[WP_CTX_COUNT];
    wp_build_context_items(wp, items);
    mtk::draw_context_menu(c, wp->context_menu, items, WP_CTX_COUNT, theme,
                           mtk::CONTEXT_MENU_DEFAULT_W,
                           mtk::CONTEXT_MENU_ITEM_H,
                           g_ui_font, fonts::UI_SIZE);
}

static int wp_special_char_flyout_x() {
    int dx = WP_BTN_SECTION_X + 24 - WP_SPECIAL_CHAR_FLYOUT_W;
    if (dx + WP_SPECIAL_CHAR_FLYOUT_W > g_win_w) dx = g_win_w - WP_SPECIAL_CHAR_FLYOUT_W;
    return dx < 0 ? 0 : dx;
}

static int wp_divider_flyout_x() {
    int dx = WP_BTN_DIVIDER_X + 24 - WP_DIVIDER_FLYOUT_W;
    if (dx + WP_DIVIDER_FLYOUT_W > g_win_w) dx = g_win_w - WP_DIVIDER_FLYOUT_W;
    return dx < 0 ? 0 : dx;
}

static void wp_draw_divider_sample(Canvas& c, int x, int y, int w, int h,
                                   uint8_t divider_type, Color color) {
    if (w <= 0 || h <= 0 || divider_type == PARA_DIVIDER_NONE) return;

    int cy = y + h / 2;
    switch (divider_type) {
    case PARA_DIVIDER_SINGLE:
        c.fill_rect(x, cy, w, 1, color);
        break;
    case PARA_DIVIDER_DOUBLE:
        c.fill_rect(x, cy - 2, w, 1, color);
        c.fill_rect(x, cy + 2, w, 1, color);
        break;
    case PARA_DIVIDER_DOTTED:
        for (int dx = x; dx < x + w; dx += 5)
            c.fill_rect(dx, cy, 2, 1, color);
        break;
    case PARA_DIVIDER_DASHED:
        for (int dx = x; dx < x + w; dx += 11) {
            int dash_w = (x + w - dx) < 7 ? (x + w - dx) : 7;
            if (dash_w > 0) c.fill_rect(dx, cy, dash_w, 1, color);
        }
        break;
    case PARA_DIVIDER_HEAVY:
        c.fill_rect(x, cy - 1, w, 3, color);
        break;
    case PARA_DIVIDER_THIN_THICK:
        c.fill_rect(x, cy - 3, w, 1, color);
        c.fill_rect(x, cy, w, 3, color);
        break;
    case PARA_DIVIDER_THICK_THIN:
        c.fill_rect(x, cy - 3, w, 3, color);
        c.fill_rect(x, cy + 2, w, 1, color);
        break;
    }
}

static void wp_fit_ui_text(const char* src, char* out, int out_len, int max_w) {
    if (!src || !src[0]) {
        if (out_len > 0) out[0] = '\0';
        return;
    }

    montauk::strncpy(out, src, out_len - 1);
    out[out_len - 1] = '\0';
    if (text_width(g_ui_font, out, fonts::UI_SIZE) <= max_w) return;

    int slen = montauk::slen(src);
    for (int keep = slen; keep > 1; keep--) {
        char candidate[256];
        candidate[0] = '.';
        candidate[1] = '.';
        int pos = 2;
        const char* tail = src + (slen - keep);
        while (*tail && pos < (int)sizeof(candidate) - 1)
            candidate[pos++] = *tail++;
        candidate[pos] = '\0';
        if (text_width(g_ui_font, candidate, fonts::UI_SIZE) <= max_w) {
            montauk::strncpy(out, candidate, out_len - 1);
            out[out_len - 1] = '\0';
            return;
        }
    }
}

static ParagraphStyle* wp_current_paragraph_style(WordProcessorState* wp) {
    if (wp->paragraph_count <= 0) {
        static ParagraphStyle fallback = { PARA_ALIGN_LEFT, PARA_LIST_NONE, 100, PARA_DIVIDER_NONE, 0, 0, 0, 0 };
        return &fallback;
    }
    int para = wp_find_paragraph_at(wp, wp_abs_pos(wp, wp->cursor_run, wp->cursor_offset));
    if (para < 0) para = 0;
    if (para >= wp->paragraph_count) para = wp->paragraph_count - 1;
    return &wp->paragraphs[para];
}

static const char* wp_align_label(uint8_t align) {
    switch (align) {
    case PARA_ALIGN_CENTER: return "Center";
    case PARA_ALIGN_RIGHT: return "Right";
    default: return "Left";
    }
}

static const char* wp_list_label(uint8_t list_type) {
    switch (list_type) {
    case PARA_LIST_BULLET: return "Bullets";
    case PARA_LIST_NUMBER: return "Numbers";
    default: return "Plain";
    }
}

static const char* wp_divider_label(uint8_t divider_type) {
    switch (divider_type) {
    case PARA_DIVIDER_SINGLE: return "Single";
    case PARA_DIVIDER_DOUBLE: return "Double";
    case PARA_DIVIDER_DOTTED: return "Dotted";
    case PARA_DIVIDER_DASHED: return "Dashed";
    case PARA_DIVIDER_HEAVY: return "Heavy";
    case PARA_DIVIDER_THIN_THICK: return "Thin/Thick";
    case PARA_DIVIDER_THICK_THIN: return "Thick/Thin";
    default: return "None";
    }
}

static void wp_draw_list_marker(Canvas& c, WordProcessorState* wp, WrapLine* wl, int py, int page_off_x) {
    if (!wl->first_in_paragraph || wl->paragraph_idx < 0 || wl->paragraph_idx >= wp->paragraph_count)
        return;

    ParagraphStyle* para = &wp->paragraphs[wl->paragraph_idx];
    if (para->divider_type != PARA_DIVIDER_NONE || para->list_type == PARA_LIST_NONE) return;

    int marker_x = page_off_x + WP_MARGIN + wp_scale_layout_units(para->left_indent + para->first_line_indent, WP_SCREEN_DPI);
    if (marker_x < page_off_x + 4) marker_x = page_off_x + 4;

    if (para->list_type == PARA_LIST_BULLET) {
        int bullet_x = marker_x + wp_scale_layout_units(7, WP_SCREEN_DPI);
        StyledRun* run = (wl->run_idx >= 0 && wl->run_idx < wp->run_count) ? &wp->runs[wl->run_idx] : nullptr;
        int run_size = run ? run->size : WP_DEFAULT_SIZE;
        int bullet_size = wp_screen_font_pixels(run_size);
        TrueTypeFont* font = run ? wp_get_font(run->font_id, 0) : g_ui_font;
        if (!font) font = g_ui_font;
        int th = text_height(font, bullet_size);
        int top_y = py + (wl->height - th) / 2;
        char bullet[2] = { '\x95', '\0' };
        draw_text(c, font, bullet_x, top_y, bullet, colors::TEXT_COLOR, bullet_size);
        return;
    }

    char label[16];
    snprintf(label, sizeof(label), "%d.", wl->list_number > 0 ? wl->list_number : 1);
    if (wp->run_count <= 0) return;
    StyledRun* run = &wp->runs[wl->run_idx < wp->run_count ? wl->run_idx : 0];
    TrueTypeFont* font = wp_get_font(run->font_id, run->flags);
    int font_px = wp_screen_font_pixels(run->size);
    int top_y = py + (wl->height - font_px) / 2;
    draw_text(c, font ? font : g_ui_font, marker_x, top_y, label, colors::TEXT_COLOR, font_px);
}

void wp_render() {
    if (!g_win.pixels) return;

    WordProcessorState* wp = &g_wp;
    Canvas c = g_win.canvas();
    c.fill(colors::WINDOW_BG);

    int sfh = wp_ui_line_height();
    int cursor_abs = wp_abs_pos(wp, wp->cursor_run, wp->cursor_offset);
    ParagraphStyle* cur_para = wp_current_paragraph_style(wp);
    Color toolbar_bg = Color::from_rgb(0xF5, 0xF5, 0xF5);
    Color btn_bg = Color::from_rgb(0xE8, 0xE8, 0xE8);
    Color btn_active = Color::from_rgb(0xC0, 0xD0, 0xE8);

    c.fill_rect(0, 0, c.w, WP_TOOLBAR_H, toolbar_bg);

    c.fill_rounded_rect(WP_BTN_OPEN_X, 6, 24, 24, 3, btn_bg);
    if (g_icon_folder.pixels)
        c.icon(WP_BTN_OPEN_X + 4, 10, g_icon_folder);

    c.fill_rounded_rect(WP_BTN_SAVE_X, 6, 24, 24, 3, btn_bg);
    if (g_icon_save.pixels)
        c.icon(WP_BTN_SAVE_X + 4, 10, g_icon_save);

    c.fill_rounded_rect(WP_BTN_PRINT_X, 6, 24, 24, 3, btn_bg);
    if (g_icon_print.pixels)
        c.icon(WP_BTN_PRINT_X + 4, 10, g_icon_print);

    wp_draw_ui_icon_button(c, WP_BTN_EXPORT_X, 6, 24, 24, g_icon_export_pdf, "P",
                           btn_bg, colors::TEXT_COLOR);

    c.vline(WP_BTN_EXPORT_X + 28, 4, 28, colors::BORDER);

    wp_draw_ui_icon_button(c, WP_BTN_UNDO_X, 6, 24, 24, g_icon_undo, "U", btn_bg, colors::TEXT_COLOR);
    wp_draw_ui_icon_button(c, WP_BTN_REDO_X, 6, 24, 24, g_icon_redo, "R", btn_bg, colors::TEXT_COLOR);

    c.vline(WP_BTN_REDO_X + 28, 4, 28, colors::BORDER);

    Color bold_bg = (wp->cur_flags & STYLE_BOLD) ? btn_active : btn_bg;
    c.fill_rounded_rect(WP_BTN_BOLD_X, 6, 24, 24, 3, bold_bg);
    draw_text(c, g_ui_bold ? g_ui_bold : g_ui_font, WP_BTN_BOLD_X + 7, 8, "B", colors::TEXT_COLOR, fonts::UI_SIZE);

    Color italic_bg = (wp->cur_flags & STYLE_ITALIC) ? btn_active : btn_bg;
    c.fill_rounded_rect(WP_BTN_ITALIC_X, 6, 24, 24, 3, italic_bg);
    {
        TrueTypeFont* italic_font = wp_get_font(FONT_ROBOTO, STYLE_ITALIC);
        draw_text(c, italic_font ? italic_font : g_ui_font, WP_BTN_ITALIC_X + 9, 8, "I", colors::TEXT_COLOR, fonts::UI_SIZE);
    }

    c.fill_rounded_rect(WP_FONT_DD_X, 6, WP_FONT_DD_W, 24, 3, btn_bg);
    wp_draw_ui_text(c, WP_FONT_DD_X + 6, (WP_TOOLBAR_H - sfh) / 2,
                    WP_FONT_NAMES[wp->cur_font_id < FONT_COUNT ? wp->cur_font_id : 0],
                    colors::TEXT_COLOR);

    c.fill_rounded_rect(WP_SIZE_DD_X, 6, WP_SIZE_DD_W, 24, 3, btn_bg);
    {
        char sz[8];
        snprintf(sz, sizeof(sz), "%d", (int)wp->cur_size);
        wp_draw_ui_text(c, WP_SIZE_DD_X + 8, (WP_TOOLBAR_H - sfh) / 2, sz, colors::TEXT_COLOR);
    }

    c.vline(WP_SIZE_DD_X + WP_SIZE_DD_W + 6, 4, 28, colors::BORDER);

    wp_draw_ui_icon_button(c, WP_BTN_ALIGN_L_X, 6, 24, 24, g_icon_align_left, "L",
                           cur_para->align == PARA_ALIGN_LEFT ? btn_active : btn_bg, colors::TEXT_COLOR);
    wp_draw_ui_icon_button(c, WP_BTN_ALIGN_C_X, 6, 24, 24, g_icon_align_center, "C",
                           cur_para->align == PARA_ALIGN_CENTER ? btn_active : btn_bg, colors::TEXT_COLOR);
    wp_draw_ui_icon_button(c, WP_BTN_ALIGN_R_X, 6, 24, 24, g_icon_align_right, "R",
                           cur_para->align == PARA_ALIGN_RIGHT ? btn_active : btn_bg, colors::TEXT_COLOR);
    c.vline(WP_BTN_ALIGN_R_X + 28, 4, 28, colors::BORDER);
    wp_draw_ui_icon_button(c, WP_BTN_BULLET_X, 6, 24, 24, g_icon_list_bullet, "*",
                           cur_para->list_type == PARA_LIST_BULLET ? btn_active : btn_bg, colors::TEXT_COLOR);
    wp_draw_ui_icon_button(c, WP_BTN_NUMBER_X, 6, 24, 24, g_icon_list_number, "1.",
                           cur_para->list_type == PARA_LIST_NUMBER ? btn_active : btn_bg, colors::TEXT_COLOR);
    c.vline(WP_BTN_NUMBER_X + 28, 4, 28, colors::BORDER);
    wp_draw_ui_icon_button(c, WP_BTN_OUTDENT_X, 6, 24, 24, g_icon_indent_less, "<", btn_bg, colors::TEXT_COLOR);
    wp_draw_ui_icon_button(c, WP_BTN_INDENT_X, 6, 24, 24, g_icon_indent_more, ">", btn_bg, colors::TEXT_COLOR);
    c.vline(WP_BTN_INDENT_X + 28, 4, 28, colors::BORDER);

    {
        char spacing[12];
        snprintf(spacing, sizeof(spacing), "%d%%", (int)cur_para->line_spacing);
        c.fill_rounded_rect(WP_LINE_DD_X, 6, WP_LINE_DD_W, 24, 3, btn_bg);
        wp_draw_ui_text(c, WP_LINE_DD_X + 8, (WP_TOOLBAR_H - sfh) / 2, spacing, colors::TEXT_COLOR);
    }

    c.vline(WP_BTN_DIVIDER_X - 4, 4, 28, colors::BORDER);

    Color divider_bg = (wp->divider_flyout_open || cur_para->divider_type != PARA_DIVIDER_NONE) ? btn_active : btn_bg;
    c.fill_rounded_rect(WP_BTN_DIVIDER_X, 6, 24, 24, 3, divider_bg);
    uint8_t divider_preview = cur_para->divider_type != PARA_DIVIDER_NONE
        ? cur_para->divider_type
        : (uint8_t)PARA_DIVIDER_SINGLE;
    wp_draw_divider_sample(c, WP_BTN_DIVIDER_X + 4, 6, 16, 24,
                           divider_preview,
                           colors::TEXT_COLOR);

    c.vline(WP_BTN_SECTION_X - 4, 4, 28, colors::BORDER);

    Color special_bg = wp->special_char_flyout_open ? btn_active : btn_bg;
    c.fill_rounded_rect(WP_BTN_SECTION_X, 6, 24, 24, 3, special_bg);
    {
        char section[2] = { (char)0xA7, '\0' };
        TrueTypeFont* font = wp_get_font(FONT_ROBOTO, 0);
        draw_text(c, font ? font : g_ui_font, WP_BTN_SECTION_X + 6, 8, section, colors::TEXT_COLOR, fonts::UI_SIZE);
    }

    c.vline(WP_BTN_PAGEVIEW_X - 4, 4, 28, colors::BORDER);

    Color pageview_bg = wp->page_view_mode ? btn_active : btn_bg;
    c.fill_rounded_rect(WP_BTN_PAGEVIEW_X, 6, 24, 24, 3, pageview_bg);
    wp_draw_ui_icon_button(c, WP_BTN_PAGEVIEW_X, 6, 24, 24, g_icon_page_view, "\xA7",
                           pageview_bg, colors::TEXT_COLOR);

    c.hline(0, WP_TOOLBAR_H - 1, c.w, colors::BORDER);

    int edit_y = WP_TOOLBAR_H;
    if (wp->show_pathbar) {
        int pb_y = WP_TOOLBAR_H;
        c.fill_rect(0, pb_y, c.w, WP_PATHBAR_H, Color::from_rgb(0xF0, 0xF0, 0xF0));

        int inp_x = 8;
        int inp_y = pb_y + 4;
        int btn_w = 56;
        int inp_w = c.w - inp_x - btn_w - 12;
        int inp_h = 24;

        c.fill_rect(inp_x, inp_y, inp_w, inp_h, colors::WHITE);
        c.rect(inp_x, inp_y, inp_w, inp_h, colors::ACCENT);

        int text_y = inp_y + (inp_h - sfh) / 2;
        wp_draw_ui_text(c, inp_x + 4, text_y, wp->pathbar_text, colors::TEXT_COLOR);

        char prefix[256];
        int plen = wp->pathbar_cursor;
        if (plen > 255) plen = 255;
        for (int i = 0; i < plen; i++) prefix[i] = wp->pathbar_text[i];
        prefix[plen] = '\0';
        int cx = inp_x + 4 + text_width(g_ui_font, prefix, fonts::UI_SIZE);
        c.fill_rect(cx, inp_y + 3, 2, inp_h - 6, colors::ACCENT);

        int btn_x = inp_x + inp_w + 6;
        wp_draw_ui_button(c, btn_x, inp_y, btn_w, inp_h,
                          wp->pathbar_save_mode ? "Save" : "Open",
                          colors::ACCENT, colors::WHITE);

        c.hline(0, pb_y + WP_PATHBAR_H - 1, c.w, colors::BORDER);
        edit_y = WP_TOOLBAR_H + WP_PATHBAR_H;
    }

    int text_area_h = c.h - edit_y - WP_STATUS_H;

    // Handle pending page view toggle
    if (wp->pending_page_view_toggle) {
        wp->page_view_mode = !wp->page_view_mode;
        wp->pending_page_view_toggle = false;
        wp->wrap_dirty = true;
    }

    // Page view mode: compute A4 page position and use A4 content width
    int page_x = 0;
    int page_y = edit_y;
    int page_content_w = c.w;
    bool in_page_view = wp->page_view_mode;
    if (in_page_view) {
        // Center A4 page horizontally in the window
        page_x = (c.w - WP_A4_PAGE_W) / 2;
        if (page_x < 0) page_x = 0;
        // Page top with margin from edit area top
        page_y = edit_y + WP_PAGE_TOP_MARGIN;
        // Use full A4 page width for wrap computation to get proper A4 margins
        // (WP_MARGIN is used for left margin, right margin comes from WP_A4_PAGE_W)
        page_content_w = WP_A4_PAGE_W;
    }

    wp_recompute_wrap(wp, page_content_w, WP_SCREEN_DPI);

    wp->scrollbar.bounds = {c.w - WP_SCROLLBAR_W, edit_y, WP_SCROLLBAR_W, text_area_h};
    wp->scrollbar.content_height = wp->content_height;
    wp->scrollbar.view_height = text_area_h;

    // In page view mode, keep scrollbar at right edge but use text_area_h for proper thumb sizing
    // The scrollbar position doesn't change - it stays at c.w - WP_SCROLLBAR_W
    // But the content scrolls within the A4 page view

    {
        int ms = wp->scrollbar.max_scroll();
        if (wp->scrollbar.scroll_offset > ms) wp->scrollbar.scroll_offset = ms;
        if (wp->scrollbar.scroll_offset < 0) wp->scrollbar.scroll_offset = 0;
    }

    wp_ensure_cursor_visible(wp, text_area_h);

    int scroll_y = wp->scrollbar.scroll_offset;
    int sel_s = 0;
    int sel_e = 0;
    if (wp->has_selection) wp_sel_range(wp, &sel_s, &sel_e);
    Color sel_bg = Color::from_rgb(0xB0, 0xD0, 0xF0);

    // In page view mode, draw the A4 page with shadow before text rendering
    if (in_page_view) {
        // Gray background behind page (same style as PDF viewer)
        Color page_bg = Color::from_rgb(0x80, 0x80, 0x80);
        c.fill_rect(0, edit_y, c.w, text_area_h, page_bg);

        // Simple offset shadow (same style as PDF viewer)
        Color shadow_col = Color::from_rgb(0x60, 0x60, 0x60);
        c.fill_rect(page_x + 3, page_y + 3, WP_A4_PAGE_W, WP_A4_PAGE_H, shadow_col);

        // White page background
        c.fill_rect(page_x, page_y, WP_A4_PAGE_W, WP_A4_PAGE_H, colors::WHITE);
    }

    for (int li = 0; li < wp->wrap_line_count; li++) {
        WrapLine* wl = &wp->wrap_lines[li];
        int py = edit_y + wl->y - scroll_y;
        // In page view mode, offset y by page_y
        if (in_page_view) {
            py = page_y + wl->y - scroll_y;
        }

        if (py + wl->height <= edit_y) continue;
        if (py >= edit_y + text_area_h) break;

        // In page view mode, offset x by page_x for all drawing
        int draw_x = wl->x;
        if (in_page_view) {
            draw_x = page_x + wl->x;
        }

        wp_draw_list_marker(c, wp, wl, py, in_page_view ? page_x : 0);
        if (wl->divider_type != PARA_DIVIDER_NONE)
            wp_draw_divider_sample(c, draw_x, py, wl->width, wl->height, wl->divider_type, colors::TEXT_COLOR);

        int chars_left = wl->char_count;
        int ri = wl->run_idx;
        int ro = wl->run_offset;
        int x = draw_x;
        int line_abs_start = wp_wrap_line_start(wp, li);
        int char_idx = 0;

        while (chars_left > 0 && ri < wp->run_count) {
            StyledRun* r = &wp->runs[ri];
            TrueTypeFont* font = wp_get_font(r->font_id, r->flags);
            if (!font || !font->valid) {
                ri++;
                ro = 0;
                continue;
            }

            GlyphCache* gc = font->get_cache(wp_screen_font_pixels(r->size));
            int baseline = py + wl->baseline;

            int avail = r->len - ro;
            int to_draw = avail < chars_left ? avail : chars_left;

            for (int ci = 0; ci < to_draw; ci++) {
                char ch = r->text[ro + ci];
                int abs_ch = line_abs_start + char_idx;

                int char_adv = 0;
                if (ch != '\n' && (ch >= 32 || ch < 0)) {
                    CachedGlyph* g = font->get_glyph(gc, (unsigned char)ch);
                    char_adv = g ? g->advance : 8;
                }

                if (wp->has_selection && abs_ch >= sel_s && abs_ch < sel_e) {
                    int sel_w = char_adv > 0 ? char_adv : 6;
                    // Clip selection to page bounds in page view mode
                    int sel_x = x;
                    int sel_y = py;
                    int sel_h = wl->height;
                    if (in_page_view) {
                        sel_x = gui_max(x, page_x);
                        int right = gui_min(x + sel_w, page_x + WP_A4_PAGE_W);
                        sel_w = right - sel_x;
                        sel_y = gui_max(py, page_y);
                        int bot = gui_min(py + sel_h, page_y + WP_A4_PAGE_H);
                        sel_h = bot - sel_y;
                        if (sel_w <= 0 || sel_h <= 0) sel_w = 0;
                    }
                    if (sel_w > 0)
                        c.fill_rect(sel_x, sel_y, sel_w, sel_h, sel_bg);
                }

                if (abs_ch == cursor_abs) {
                    int cur_h = wl->height;
                    int cur_x = x;
                    int cur_y = py;
                    int cur_w = 2;
                    if (in_page_view) {
                        // Clip cursor to page bounds
                        if (cur_x < page_x) { cur_w = cur_x + cur_w - page_x; cur_x = page_x; }
                        if (cur_x + cur_w > page_x + WP_A4_PAGE_W) cur_w = page_x + WP_A4_PAGE_W - cur_x;
                        cur_y = gui_max(cur_y, page_y);
                        int bot = gui_min(cur_y + cur_h, page_y + WP_A4_PAGE_H);
                        cur_h = bot - cur_y;
                        if (cur_h <= 0 || cur_w <= 0) cur_x = -1; // hide
                    }
                    if (cur_x >= 0 && py >= edit_y && py + cur_h <= edit_y + text_area_h)
                        c.fill_rect(cur_x, cur_y, cur_w, cur_h, colors::ACCENT);
                }

                if (ch != '\n' && (ch >= 32 || ch < 0)) {
                    // In page view mode, clip to page bounds
                    bool can_draw = (x < c.w - WP_SCROLLBAR_W - WP_MARGIN);
                    if (in_page_view) {
                        can_draw = (x + char_adv > page_x && x < page_x + WP_A4_PAGE_W &&
                                    py + wl->height > page_y && py < page_y + WP_A4_PAGE_H);
                    }
                    if (can_draw) {
                        Color text_col = (wp->has_selection && abs_ch >= sel_s && abs_ch < sel_e)
                            ? Color::from_rgb(0x10, 0x10, 0x10) : colors::TEXT_COLOR;
                        int adv = font->draw_char_to_buffer(
                            c.pixels, c.w, c.h, x, baseline, (unsigned char)ch, text_col, gc);
                        x += adv;
                    } else {
                        x += char_adv;
                    }
                }

                char_idx++;
            }

            chars_left -= to_draw;
            ro += to_draw;
            if (ro >= r->len) {
                ri++;
                ro = 0;
            }
        }

        if (line_abs_start + char_idx == cursor_abs && li == wp->wrap_line_count - 1) {
            int cur_h = wl->height;
            int cur_x = x;
            int cur_y = py;
            if (in_page_view) {
                if (cur_x < page_x) cur_x = page_x;
                if (cur_x > page_x + WP_A4_PAGE_W) cur_x = -1;
                cur_y = gui_max(cur_y, page_y);
                int bot = gui_min(cur_y + cur_h, page_y + WP_A4_PAGE_H);
                cur_h = bot - cur_y;
                if (cur_h <= 0) cur_x = -1;
            }
            if (cur_x >= 0 && py >= edit_y && py + cur_h <= edit_y + text_area_h)
                c.fill_rect(cur_x, cur_y, 2, cur_h, colors::ACCENT);
        }
    }

    if (wp->scrollbar.content_height > wp->scrollbar.view_height) {
        Color sb_fg = (wp->scrollbar.hovered || wp->scrollbar.dragging)
            ? wp->scrollbar.hover_fg : wp->scrollbar.fg;
        int sbx = wp->scrollbar.bounds.x;
        int sby = wp->scrollbar.bounds.y;
        int sbw = wp->scrollbar.bounds.w;
        int sbh = wp->scrollbar.bounds.h;
        c.fill_rect(sbx, sby, sbw, sbh, colors::SCROLLBAR_BG);
        int th = wp->scrollbar.thumb_height();
        int tty = wp->scrollbar.thumb_y();
        c.fill_rect(sbx + 1, tty, sbw - 2, th, sb_fg);
    }

    int status_y = c.h - WP_STATUS_H;
    c.fill_rect(0, status_y, c.w, WP_STATUS_H, Color::from_rgb(0x2B, 0x3E, 0x50));
    int status_text_y = status_y + (WP_STATUS_H - sfh) / 2;

    char status_left[320];
    const char* name = wp->filename[0] ? wp->filename : "Untitled";
    if (cur_para->divider_type != PARA_DIVIDER_NONE) {
        snprintf(status_left, sizeof(status_left), " %s%s | %s %dpt | Divider: %s | %d%%",
                 name, wp->modified ? " *" : "",
                 WP_FONT_NAMES[wp->cur_font_id < FONT_COUNT ? wp->cur_font_id : 0], (int)wp->cur_size,
                 wp_divider_label(cur_para->divider_type), (int)cur_para->line_spacing);
    } else {
        snprintf(status_left, sizeof(status_left), " %s%s | %s %dpt | %s | %s | %d%%",
                 name, wp->modified ? " *" : "",
                 WP_FONT_NAMES[wp->cur_font_id < FONT_COUNT ? wp->cur_font_id : 0], (int)wp->cur_size,
                 wp_align_label(cur_para->align), wp_list_label(cur_para->list_type),
                 (int)cur_para->line_spacing);
    }

    char status_right[32];
    snprintf(status_right, sizeof(status_right), "%d chars ", wp->total_text_len);
    int sr_w = text_width(g_ui_font, status_right, fonts::UI_SIZE);
    char status_combined[320];
    if (wp->status_msg[0]) snprintf(status_combined, sizeof(status_combined), "%s | %s", status_left, wp->status_msg);
    else montauk::strncpy(status_combined, status_left, (int)sizeof(status_combined) - 1);
    status_combined[sizeof(status_combined) - 1] = '\0';

    char status_fit[320];
    wp_fit_ui_text(status_combined, status_fit, sizeof(status_fit), c.w - sr_w - 12);
    wp_draw_ui_text(c, 4, status_text_y, status_fit, colors::PANEL_TEXT);
    wp_draw_ui_text(c, c.w - sr_w - 4, status_text_y, status_right, colors::PANEL_TEXT);

    if (wp->font_dropdown_open) {
        int dx = WP_FONT_DD_X;
        int dy = WP_TOOLBAR_H;
        int dw = 110;
        int dh = FONT_COUNT * 26 + 4;
        c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
        c.rect(dx, dy, dw, dh, colors::BORDER);
        for (int i = 0; i < FONT_COUNT; i++) {
            int iy = dy + 2 + i * 26;
            if (i == wp->cur_font_id)
                c.fill_rect(dx + 2, iy, dw - 4, 24, colors::MENU_HOVER);
            wp_draw_ui_text(c, dx + 8, iy + (24 - sfh) / 2, WP_FONT_NAMES[i], colors::TEXT_COLOR);
        }
    }

    if (wp->size_dropdown_open) {
        int dx = WP_SIZE_DD_X;
        int dy = WP_TOOLBAR_H;
        int dw = 56;
        int dh = WP_SIZE_OPTION_COUNT * 26 + 4;
        c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
        c.rect(dx, dy, dw, dh, colors::BORDER);
        for (int i = 0; i < WP_SIZE_OPTION_COUNT; i++) {
            int iy = dy + 2 + i * 26;
            if (WP_SIZE_OPTIONS[i] == wp->cur_size)
                c.fill_rect(dx + 2, iy, dw - 4, 24, colors::MENU_HOVER);
            char sz[8];
            snprintf(sz, sizeof(sz), "%d", WP_SIZE_OPTIONS[i]);
            wp_draw_ui_text(c, dx + 8, iy + (24 - sfh) / 2, sz, colors::TEXT_COLOR);
        }
    }

    if (wp->line_spacing_dropdown_open) {
        int dx = WP_LINE_DD_X;
        int dy = WP_TOOLBAR_H;
        int dw = 64;
        int dh = WP_LINE_SPACING_OPTION_COUNT * 26 + 4;
        c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
        c.rect(dx, dy, dw, dh, colors::BORDER);
        for (int i = 0; i < WP_LINE_SPACING_OPTION_COUNT; i++) {
            int iy = dy + 2 + i * 26;
            if (WP_LINE_SPACING_OPTIONS[i] == cur_para->line_spacing)
                c.fill_rect(dx + 2, iy, dw - 4, 24, colors::MENU_HOVER);
            char spacing[12];
            snprintf(spacing, sizeof(spacing), "%d%%", WP_LINE_SPACING_OPTIONS[i]);
            wp_draw_ui_text(c, dx + 8, iy + (24 - sfh) / 2, spacing, colors::TEXT_COLOR);
        }
    }

    if (wp->divider_flyout_open) {
        int dx = wp_divider_flyout_x();
        int dy = WP_TOOLBAR_H;
        int dw = WP_DIVIDER_FLYOUT_W;
        int dh = WP_DIVIDER_OPTION_COUNT * WP_DIVIDER_ROW_H + 4;
        c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
        c.rect(dx, dy, dw, dh, colors::BORDER);
        for (int i = 0; i < WP_DIVIDER_OPTION_COUNT; i++) {
            int iy = dy + 2 + i * WP_DIVIDER_ROW_H;
            if (WP_DIVIDER_OPTIONS[i].type == cur_para->divider_type)
                c.fill_rect(dx + 2, iy, dw - 4, WP_DIVIDER_ROW_H - 2, colors::MENU_HOVER);
            wp_draw_divider_sample(c, dx + 10, iy, 56, WP_DIVIDER_ROW_H,
                                   WP_DIVIDER_OPTIONS[i].type, colors::TEXT_COLOR);
            wp_draw_ui_text(c, dx + 78, iy + (WP_DIVIDER_ROW_H - sfh) / 2,
                            WP_DIVIDER_OPTIONS[i].label, colors::TEXT_COLOR);
        }
    }

    if (wp->special_char_flyout_open) {
        int dx = wp_special_char_flyout_x();
        int dy = WP_TOOLBAR_H;
        int dw = WP_SPECIAL_CHAR_FLYOUT_W;
        int dh = WP_SPECIAL_CHAR_OPTION_COUNT * WP_SPECIAL_CHAR_ROW_H + 4;
        TrueTypeFont* glyph_font = wp_get_font(FONT_ROBOTO, 0);
        c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
        c.rect(dx, dy, dw, dh, colors::BORDER);
        for (int i = 0; i < WP_SPECIAL_CHAR_OPTION_COUNT; i++) {
            int iy = dy + 2 + i * WP_SPECIAL_CHAR_ROW_H;
            char glyph[2] = { (char)WP_SPECIAL_CHAR_OPTIONS[i].code, '\0' };
            c.fill_rect(dx + 2, iy, dw - 4, WP_SPECIAL_CHAR_ROW_H - 2, colors::MENU_BG);
            draw_text(c, glyph_font ? glyph_font : g_ui_font,
                      dx + 8, iy + (WP_SPECIAL_CHAR_ROW_H - sfh) / 2,
                      glyph, colors::TEXT_COLOR, fonts::UI_SIZE);
            wp_draw_ui_text(c, dx + 32, iy + (WP_SPECIAL_CHAR_ROW_H - sfh) / 2,
                            WP_SPECIAL_CHAR_OPTIONS[i].label, colors::TEXT_COLOR);
        }
    }

    wp_draw_context_menu(c, wp);
}
