/*
 * render.cpp
 * MontaukOS Device Explorer — main window rendering
 * Copyright (c) 2026 Daniel Hammer
 */

#include "px.h"

// ============================================================================
// Triangle drawing helpers
// ============================================================================

static void draw_triangle_right(uint32_t* px, int bw, int bh,
                                int x, int y, int size, Color col) {
    uint32_t v = col.to_pixel();
    int half = size / 2;
    for (int row = 0; row < size; row++) {
        int dist = (row <= half) ? row : (size - 1 - row);
        for (int col_px = 0; col_px <= dist; col_px++) {
            int dx = x + col_px, dy = y + row;
            if (dx >= 0 && dx < bw && dy >= 0 && dy < bh)
                px[dy * bw + dx] = v;
        }
    }
}

static void draw_triangle_down(uint32_t* px, int bw, int bh,
                               int x, int y, int size, Color col) {
    uint32_t v = col.to_pixel();
    int half = size / 2;
    for (int row = 0; row < half + 1; row++) {
        int w = size - row * 2;
        for (int col_px = 0; col_px < w; col_px++) {
            int dx = x + row + col_px, dy = y + row;
            if (dx >= 0 && dx < bw && dy >= 0 && dy < bh)
                px[dy * bw + dx] = v;
        }
    }
}

// ============================================================================
// Render: main device list
// ============================================================================

static void render_toolbar(uint32_t* px) {
    auto& de = g_state;
    int fh = font_h();

    px_fill(px, g_win_w, g_win_h, 0, 0, g_win_w, TOOLBAR_H, TOOLBAR_BG);
    px_hline(px, g_win_w, g_win_h, 0, TOOLBAR_H - 1, g_win_w, BORDER_COLOR);

    // "Refresh" button (MTK primary button)
    Canvas canvas(px, g_win_w, g_win_h);
    mtk::WidgetState btn_state = mtk::widget_state(false, de.btn_hover, true);
    mtk::draw_button(canvas, refresh_button_rect(), "Refresh",
                     mtk::BUTTON_PRIMARY, btn_state, g_theme);

    // Device count on right
    char count_str[24];
    snprintf(count_str, sizeof(count_str), "%d devices", de.dev_count);
    int cw = text_w(count_str);
    px_text(px, g_win_w, g_win_h, g_win_w - cw - 12, (TOOLBAR_H - fh) / 2,
            count_str, TEXT_COLOR);
}

static void render_list(uint32_t* px) {
    auto& de = g_state;
    int fh = font_h();

    DisplayRow rows[MAX_DISPLAY_ROWS];
    int row_count = build_display_rows(&de, rows);

    ScrollMetrics m = compute_scroll_metrics(&de, rows, row_count);
    int list_y = m.list_y;
    int list_h = m.list_h;
    if (list_h < 1) return;

    if (de.selected_row >= row_count) de.selected_row = row_count - 1;

    // Locate the first visible row for the current pixel scroll offset. The top
    // row may be partially scrolled above the viewport (its top is then above
    // list_y); render() draws the toolbar after the list so that overhang is
    // masked. Clamping the scroll offset in pixels (not whole rows) lets the
    // final row sit flush with the bottom edge, so the last category is never
    // cut off.
    int scroll_px = m.scroll_px;
    int first_visible = 0, row_top = 0;
    {
        int acc = 0;
        for (int i = 0; i < row_count; i++) {
            int rh = (rows[i].type == ROW_CATEGORY) ? CAT_H : ITEM_H;
            if (acc + rh > scroll_px) { first_visible = i; row_top = acc; break; }
            acc += rh;
            first_visible = i + 1;
            row_top = acc;
        }
    }

    // Draw rows
    int cur_y = list_y - (scroll_px - row_top);
    Color menu_hover = Color::from_rgb(0xD0, 0xE0, 0xF8);

    for (int i = first_visible; i < row_count; i++) {
        int row_h = (rows[i].type == ROW_CATEGORY) ? CAT_H : ITEM_H;
        if (cur_y >= g_win_h) break;

        if (rows[i].type == ROW_CATEGORY) {
            int cat = rows[i].category;
            px_fill(px, g_win_w, g_win_h, 0, cur_y, g_win_w, CAT_H,
                    Color::from_rgb(0xF0, 0xF0, 0xF0));

            if (i == de.selected_row)
                px_fill(px, g_win_w, g_win_h, 0, cur_y, g_win_w, CAT_H, menu_hover);

            // Expand/collapse triangle
            int tri_x = 10;
            int tri_y = cur_y + (CAT_H - 8) / 2;
            Color tri_color = Color::from_rgb(0x55, 0x55, 0x55);
            if (de.collapsed[cat])
                draw_triangle_right(px, g_win_w, g_win_h, tri_x, tri_y, 8, tri_color);
            else
                draw_triangle_down(px, g_win_w, g_win_h, tri_x, tri_y, 8, tri_color);

            // Colored dot
            int dot_x = 24;
            int dot_y = cur_y + (CAT_H - 8) / 2;
            Color cat_col = (cat >= 0 && cat < NUM_CATEGORIES)
                            ? category_colors[cat] : TEXT_COLOR;
            px_fill_rounded(px, g_win_w, g_win_h, dot_x, dot_y, 8, 8, 4, cat_col);

            // Category name
            const char* cat_name = (cat >= 0 && cat < NUM_CATEGORIES)
                                   ? category_names[cat] : "?";
            int text_y = cur_y + (CAT_H - fh) / 2;
            px_text(px, g_win_w, g_win_h, 36, text_y, cat_name,
                    Color::from_rgb(0x33, 0x33, 0x33));

            // Device count
            int cat_count = 0;
            for (int d = 0; d < de.dev_count; d++)
                if (de.devs[d].category == cat) cat_count++;
            char cnt_buf[16];
            snprintf(cnt_buf, sizeof(cnt_buf), "(%d)", cat_count);
            int name_w = text_w(cat_name);
            px_text(px, g_win_w, g_win_h, 36 + name_w + 8, text_y, cnt_buf,
                    Color::from_rgb(0x88, 0x88, 0x88));

            // Bottom border
            px_hline(px, g_win_w, g_win_h, 0, cur_y + CAT_H - 1, g_win_w,
                     Color::from_rgb(0xE0, 0xE0, 0xE0));
        } else {
            int di = rows[i].dev_index;

            if (i == de.selected_row)
                px_fill(px, g_win_w, g_win_h, 0, cur_y, g_win_w, ITEM_H, menu_hover);

            int text_y = cur_y + (ITEM_H - fh) / 2;

            // Device name (indented)
            px_text(px, g_win_w, g_win_h, INDENT + 10, text_y,
                    de.devs[di].name, TEXT_COLOR);

            // Detail string (right column)
            int detail_x = g_win_w / 2 + 20;
            px_text(px, g_win_w, g_win_h, detail_x, text_y,
                    de.devs[di].detail, DIM_TEXT);
        }

        cur_y += row_h;
    }

    // Draggable MTK scrollbar
    Rect viewport = { 0, list_y, g_win_w, list_h };
    Rect track = mtk::scrollbar_track_rect(viewport);
    Rect thumb = mtk::scrollbar_thumb_rect(track, m.total_h, list_h, m.scroll_px);
    if (!track.empty() && !thumb.empty()) {
        bool hovered = track.contains(de.mouse_x, de.mouse_y);
        mtk::Theme theme{};
        Color thumb_col = mtk::scrollbar_thumb_color(theme, hovered, de.sb_dragging);
        px_fill(px, g_win_w, g_win_h, track.x, track.y, track.w, track.h,
                colors::SCROLLBAR_BG);
        px_fill(px, g_win_w, g_win_h, thumb.x, thumb.y, thumb.w, thumb.h,
                thumb_col);
    }
}

void render(uint32_t* pixels) {
    px_fill(pixels, g_win_w, g_win_h, 0, 0, g_win_w, g_win_h, BG_COLOR);
    // List first, toolbar last: a partially-scrolled top row can extend above
    // the list area, and drawing the toolbar afterward masks that overhang.
    render_list(pixels);
    render_toolbar(pixels);
}

