/*
    * app_klog.cpp
    * ZenithOS Desktop - Kernel Log viewer (tails the kernel ring buffer)
    * Copyright (c) 2026 Daniel Hammer
*/

#include "apps_common.hpp"

// ============================================================================
// Kernel Log state
// ============================================================================

static constexpr int KLOG_READ_SIZE = 65536; // matches kernel ring buffer size
static constexpr int KLOG_POLL_MS   = 250;

struct KlogState {
    TerminalState term;
    char* klog_buf;
    int last_len;
    char last_tail_byte;
    uint64_t last_poll_ms;
};

// ============================================================================
// Re-feed the last screenful of log into the terminal
// ============================================================================

static void klog_refeed(KlogState* klog, int n) {
    // Find start of last `rows` lines from the end of the buffer
    int lines_needed = klog->term.rows;
    int start = 0;
    for (int i = n - 1; i >= 0 && lines_needed > 0; i--) {
        if (klog->klog_buf[i] == '\n') {
            lines_needed--;
            if (lines_needed == 0) { start = i + 1; break; }
        }
    }

    // Clear terminal
    int total = klog->term.cols * klog->term.rows;
    for (int i = 0; i < total; i++)
        klog->term.cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
    klog->term.cursor_x = 0;
    klog->term.cursor_y = 0;
    klog->term.current_fg = colors::TERM_FG;
    klog->term.current_bg = colors::TERM_BG;

    terminal_feed(&klog->term, klog->klog_buf + start, n - start);
}

// ============================================================================
// Callbacks
// ============================================================================

static void klog_on_draw(Window* win, Framebuffer& fb) {
    KlogState* klog = (KlogState*)win->app_data;
    if (!klog) return;

    Rect cr = win->content_rect();
    terminal_render(&klog->term, win->content, cr.w, cr.h);
}

static void klog_on_mouse(Window* win, MouseEvent& ev) {
    // Read-only viewer — no mouse interaction needed
}

static void klog_on_key(Window* win, const Zenith::KeyEvent& key) {
    // Read-only viewer — no keyboard input
}

static void klog_on_poll(Window* win) {
    KlogState* klog = (KlogState*)win->app_data;
    if (!klog) return;

    uint64_t now = zenith::get_milliseconds();
    if (now - klog->last_poll_ms < KLOG_POLL_MS) return;
    klog->last_poll_ms = now;

    int n = (int)zenith::read_klog(klog->klog_buf, KLOG_READ_SIZE);
    if (n <= 0 && klog->last_len <= 0) return;

    if (n > klog->last_len) {
        // Buffer grew — feed only the new portion
        terminal_feed(&klog->term, klog->klog_buf + klog->last_len, n - klog->last_len);
    } else if (n == klog->last_len && n > 0) {
        // Same size — check if the tail changed (ring buffer wrapped)
        if (klog->klog_buf[n - 1] != klog->last_tail_byte) {
            klog_refeed(klog, n);
        } else {
            return; // nothing changed
        }
    } else if (n < klog->last_len) {
        // Unexpected shrink — full re-render
        klog_refeed(klog, n);
    }

    klog->last_len = n;
    if (n > 0) klog->last_tail_byte = klog->klog_buf[n - 1];
}

static void klog_on_close(Window* win) {
    KlogState* klog = (KlogState*)win->app_data;
    if (klog) {
        if (klog->term.cells) zenith::mfree(klog->term.cells);
        if (klog->term.alt_cells) zenith::mfree(klog->term.alt_cells);
        if (klog->klog_buf) zenith::mfree(klog->klog_buf);
        zenith::mfree(klog);
        win->app_data = nullptr;
    }
}

// ============================================================================
// Kernel Log launcher
// ============================================================================

void open_klog(DesktopState* ds) {
    int idx = desktop_create_window(ds, "Kernel Log", 160, 60, 720, 480);
    if (idx < 0) return;

    Window* win = &ds->windows[idx];
    Rect cr = win->content_rect();
    int cols = cr.w / FONT_WIDTH;
    int rows = cr.h / FONT_HEIGHT;

    KlogState* klog = (KlogState*)zenith::malloc(sizeof(KlogState));
    zenith::memset(klog, 0, sizeof(KlogState));

    // Initialize the terminal cell grid (reuse terminal infrastructure)
    terminal_init_cells(&klog->term, cols, rows);

    // Allocate klog read buffer
    klog->klog_buf = (char*)zenith::malloc(KLOG_READ_SIZE);
    klog->last_len = 0;
    klog->last_tail_byte = 0;
    klog->last_poll_ms = 0;

    // Do an initial read to show existing log content
    int n = (int)zenith::read_klog(klog->klog_buf, KLOG_READ_SIZE);
    if (n > 0) {
        klog_refeed(klog, n);
        klog->last_len = n;
        klog->last_tail_byte = klog->klog_buf[n - 1];
    }

    win->app_data = klog;
    win->on_draw = klog_on_draw;
    win->on_mouse = klog_on_mouse;
    win->on_key = klog_on_key;
    win->on_close = klog_on_close;
    win->on_poll = klog_on_poll;
}
