/*
 * document.cpp
 * Display line building for the Wikipedia client
 * Copyright (c) 2026 Daniel Hammer
 */

#include "wikipedia.h"

void add_line(const char* text, int len, Color color, int size,
              TrueTypeFont* font, int indent) {
    if (g_line_count >= MAX_LINES) return;
    WikiLine* l = &g_lines[g_line_count++];
    int copy = len < 255 ? len : 255;
    memcpy(l->text, text, copy);
    l->text[copy] = '\0';
    l->color     = color;
    l->font_size = size;
    l->font      = font;
    l->indent    = indent;
}

void add_spacer(int size) {
    if (g_line_count >= MAX_LINES) return;
    WikiLine* l = &g_lines[g_line_count++];
    l->text[0]   = '\0';
    l->color     = reader_palette().text;
    l->font_size = size;
    l->font      = current_body_font();
    l->indent    = 0;
}

// Word-wrap a text segment into display lines using pixel-width measurement.
void wrap_text(TrueTypeFont* font, int size, const char* text, int textLen,
               int max_px, Color color, int indent) {
    if (!font || !text || textLen <= 0) return;
    static constexpr int MAX_LINE = 255;
    char  cur[MAX_LINE + 1];
    int   cur_len = 0;
    const char* p   = text;
    const char* end = text + textLen;

    while (p < end) {
        while (p < end && *p == ' ') p++;
        if (p >= end) break;

        const char* word_start = p;
        while (p < end && *p != ' ') p++;
        int word_len = (int)(p - word_start);
        if (word_len <= 0) continue;

        char test[MAX_LINE + 1];
        int test_len = cur_len;
        memcpy(test, cur, cur_len);
        if (cur_len > 0 && test_len < MAX_LINE) test[test_len++] = ' ';
        int avail = MAX_LINE - test_len;
        int copy = word_len < avail ? word_len : avail;
        if (copy > 0) {
            memcpy(test + test_len, word_start, copy);
            test_len += copy;
        }
        test[test_len] = '\0';

        int test_w = font->measure_text(test, size);
        if (test_w <= max_px || cur_len == 0) {
            memcpy(cur, test, test_len + 1);
            cur_len = test_len;
        } else {
            if (cur_len > 0) add_line(cur, cur_len, color, size, font, indent);
            int wl = word_len < MAX_LINE ? word_len : MAX_LINE;
            memcpy(cur, word_start, wl);
            cur_len = wl;
            cur[cur_len] = '\0';
        }
    }
    if (cur_len > 0) add_line(cur, cur_len, color, size, font, indent);
}

void build_display_lines(const char* title, const char* extract, int extractLen,
                         bool preserve_scroll) {
    ReaderPalette palette = reader_palette();
    TrueTypeFont* body_font = current_body_font();
    TrueTypeFont* heading_font = current_heading_font();
    int old_scroll = g_scroll_y;

    if (!body_font) return;

    g_line_count = 0;
    g_scroll_y   = 0;

    Rect column = article_column_rect();
    int max_px = column.w;

    if (title && title[0]) {
        TrueTypeFont* tf = heading_font ? heading_font : body_font;
        wrap_text(tf, TITLE_SIZE, title, (int)strlen(title), max_px, palette.heading);
        add_spacer(gui_max(FONT_SIZE, 12));
    }

    const char* p   = extract;
    const char* end = extract + extractLen;
    bool pending_paragraph_break = false;

    while (p < end && g_line_count < MAX_LINES) {
        const char* line_start = p;
        while (p < end && *p != '\n') p++;
        int line_len = (int)(p - line_start);
        if (p < end) p++;

        if (line_len == 0) {
            pending_paragraph_break = true;
            continue;
        }

        // Section header: == Title ==
        if (line_len >= 4 && line_start[0] == '=' && line_start[1] == '=') {
            int si = 0, ei = line_len;
            while (si < line_len && line_start[si] == '=') si++;
            while (si < line_len && line_start[si] == ' ') si++;
            while (ei > si && line_start[ei-1] == '=') ei--;
            while (ei > si && line_start[ei-1] == ' ') ei--;
            if (ei > si) {
                if (g_line_count > 0)
                    add_spacer(gui_max(FONT_SIZE - 2, 12));
                TrueTypeFont* tf = heading_font ? heading_font : body_font;
                wrap_text(tf, SECTION_SIZE, line_start + si, ei - si, max_px, palette.heading);
                add_spacer(gui_max(FONT_SIZE - 6, 8));
                pending_paragraph_break = false;
            }
            continue;
        }

        if (pending_paragraph_break && g_line_count > 0) {
            add_spacer(gui_max(FONT_SIZE - 6, 8));
            pending_paragraph_break = false;
        }

        wrap_text(body_font, FONT_SIZE, line_start, line_len, max_px, palette.text);
    }

    if (preserve_scroll)
        g_scroll_y = gui_clamp(old_scroll, 0, max_scroll_lines());
}

void build_welcome_lines(bool preserve_scroll) {
    ReaderPalette palette = reader_palette();
    TrueTypeFont* body_font = current_body_font();
    TrueTypeFont* heading_font = current_heading_font();
    int old_scroll = g_scroll_y;

    if (!body_font) return;

    g_line_count = 0;
    g_scroll_y = 0;

    Rect column = document_column_rect();
    int max_px = column.w;
    TrueTypeFont* tf = heading_font ? heading_font : body_font;

    const char* title = "Welcome to the Montauk Wikipedia reader";
    wrap_text(tf, TITLE_SIZE, title, (int)strlen(title), max_px, palette.heading);
    add_spacer(gui_max(FONT_SIZE - 4, 10));

    const char* intro = "Search for an article above.";
    wrap_text(body_font, FONT_SIZE, intro, (int)strlen(intro), max_px, palette.text);
    add_spacer(gui_max(FONT_SIZE + 2, 16));

    if (g_welcome_feature_loaded && g_welcome_feature_title[0]) {
        wrap_text(tf, SECTION_SIZE, "Featured article today",
                  (int)strlen("Featured article today"), max_px, palette.heading);
        add_spacer(gui_max(FONT_SIZE - 6, 10));

        wrap_text(tf, gui_max(FONT_SIZE + 2, 16),
                  g_welcome_feature_title,
                  (int)strlen(g_welcome_feature_title),
                  max_px, palette.accent);
        add_spacer(gui_max(FONT_SIZE - 4, 10));

        if (g_welcome_feature_extract[0]) {
            wrap_text(body_font, FONT_SIZE,
                      g_welcome_feature_extract,
                      (int)strlen(g_welcome_feature_extract),
                      max_px, palette.text);
            add_spacer(gui_max(FONT_SIZE - 6, 8));
        }

        char hint[360];
        snprintf(hint, sizeof(hint),
                 "To open the full article, search for \"%s\".",
                 g_welcome_feature_title);
        add_spacer(gui_max(FONT_SIZE - 8, 8));
        wrap_text(body_font, gui_max(FONT_SIZE - 2, 12),
                  hint, (int)strlen(hint), max_px, palette.muted);
    } else {
        const char* section = "Start reading";
        wrap_text(tf, SECTION_SIZE, section, (int)strlen(section), max_px, palette.heading);
        add_spacer(gui_max(FONT_SIZE - 8, 8));

        if (g_welcome_status[0]) {
            add_spacer(gui_max(FONT_SIZE - 6, 8));
            wrap_text(body_font, gui_max(FONT_SIZE - 2, 12),
                      g_welcome_status,
                      (int)strlen(g_welcome_status),
                      max_px, palette.muted);
        }
    }

    if (preserve_scroll)
        g_scroll_y = gui_clamp(old_scroll, 0, max_scroll_lines());
}

void rebuild_current_document(bool preserve_scroll) {
    if (g_phase == AppPhase::DONE && g_extract_len > 0)
        build_display_lines(g_title, g_extract_buf, g_extract_len, preserve_scroll);
    else if (g_phase == AppPhase::WELCOME)
        build_welcome_lines(preserve_scroll);
}

void apply_reader_setting_change() {
    g_scroll_dragging = false;
    apply_reader_preferences();
    update_toolbar_height();
    rebuild_current_document(true);
    clamp_scroll();
}

bool handle_reader_option_click(int mx, int my) {
    ReaderToolbarLayout lo = toolbar_layout();
    if (!g_display_controls_open) return false;

    for (int i = 0; i < READER_TEXT_SIZE_COUNT; i++) {
        if (!lo.text_size_buttons[i].contains(mx, my)) continue;
        ReaderTextSize next = (ReaderTextSize)i;
        if (g_reader_text_size != next) {
            g_reader_text_size = next;
            apply_reader_setting_change();
        }
        return true;
    }

    for (int i = 0; i < READER_FONT_FACE_COUNT; i++) {
        if (!lo.font_buttons[i].contains(mx, my)) continue;
        ReaderFontFace next = (ReaderFontFace)i;
        if (g_reader_font_face != next) {
            g_reader_font_face = next;
            apply_reader_setting_change();
        }
        return true;
    }

    return false;
}
