/*
    * main.cpp
    * MontaukOS Crashpad — displays crash reports in a popup window
    * Copyright (c) 2026 Daniel Hammer
*/

#include <montauk/syscall.h>
#include <montauk/heap.h>
#include <montauk/string.h>
#include <gui/standalone.hpp>
#include <gui/gui.hpp>
#include <gui/font.hpp>

using namespace gui;

static constexpr int WINDOW_W = 460;
static constexpr int WINDOW_H = 480;
static constexpr int PADDING   = 12;
static constexpr int HEADER_H = 48;
static constexpr int COL1_W   = 200;
static constexpr int SECTION_GAP = 10;

WsWindow g_win;
Montauk::CrashReportInfo g_report = {};
int g_scroll = 0;

// Colors - light theme matching DiskDetail
static constexpr Color BG         = Color::from_rgb(0xFF, 0xFF, 0xFF);
static constexpr Color ROW_EVEN   = Color::from_rgb(0xF7, 0xF7, 0xF7);
static constexpr Color ACCENT     = Color::from_rgb(0x33, 0x66, 0xCC);
static constexpr Color BORDER     = Color::from_rgb(0xE0, 0xE0, 0xE0);
static constexpr Color TEXT_COLOR = Color::from_rgb(0x22, 0x22, 0x22);
static constexpr Color DIM_TEXT   = Color::from_rgb(0x66, 0x66, 0x66);
static constexpr Color VALUE_COL  = Color::from_rgb(0x22, 0x22, 0x22);

// Custom hex formatter (avoids snprintf which isn't in freestanding libc)
static void format_hex16(char* out, uint64_t val) {
    out[0] = '0';
    out[1] = 'x';
    static const char hex[] = "0123456789ABCDEF";
    for (int i = 0; i < 16; i++) {
        out[2 + i] = hex[(val >> (60 - i * 4)) & 0xF];
    }
    out[18] = '\0';
}

static void format_decimal(char* out, int val) {
    if (val == 0) {
        out[0] = '0';
        out[1] = '\0';
        return;
    }
    if (val < 0) {
        *out++ = '-';
        val = -val;
    }
    char tmp[16];
    int i = 0;
    while (val > 0) {
        tmp[i++] = '0' + (val % 10);
        val /= 10;
    }
    while (i > 0) {
        *out++ = tmp[--i];
    }
    *out = '\0';
}

// Draw a section header label + horizontal rule
static void section_header(Canvas& c, int& y, int w, int fh, const char* label) {
    if (y < -fh - 8 || y > c.h + fh + 8) {
        y += fh + 8;
        return;
    }
    c.text(PADDING, y, label, ACCENT);
    y += fh;
    c.hline(PADDING, y, w - PADDING * 2, BORDER);
    y += 2;
}

// Draw one alternating table row (key left, value right)
static void table_row(Canvas& c, int& y, int row_h, int col1_w, int w,
                      int rowIdx, const char* key, const char* val, Color val_col) {
    if (y > c.h + row_h || y < -row_h) {
        y += row_h;
        return;
    }
    if (rowIdx % 2 == 0)
        c.fill_rect(PADDING, y, w - PADDING * 2, row_h, ROW_EVEN);
    int ty = y + (row_h - system_font_height()) / 2;
    c.text(PADDING + 8, ty, key, DIM_TEXT);
    c.text(PADDING + col1_w + 8, ty, val, val_col);
    y += row_h;
}

static void render() {
    if (g_win.id < 0) return;
    Canvas c = g_win.canvas();
    c.fill(BG);

    int fh = system_font_height();
    int row_h = fh + 8;
    int w = g_win.width;
    int y = PADDING - g_scroll;
    int rowIdx = 0;

    // Process info header (outside table)
    if (y >= -fh * 2 && y < c.h + fh) {
        char buf[128];
        char* p = buf;

        // Process name (large, white)
        const char* name = g_report.processName;
        while (*name) *p++ = *name++;
        *p++ = ' ';
        *p++ = '(';
        *p++ = 'P';
        *p++ = 'I';
        *p++ = 'D';
        *p++ = ' ';
        format_decimal(p, g_report.pid);
        while (*p) p++;
        *p++ = ')';
        *p = '\0';

        c.text(PADDING, y, buf, TEXT_COLOR);
        y += fh + 4;

        // Exception name below
        char excbuf[64];
        p = excbuf;
        const char* exc = g_report.exceptionName;
        while (*exc) *p++ = *exc++;
        *p++ = ' ';
        *p++ = '(';
        *p++ = 'v';
        *p++ = 'e';
        *p++ = 'c';
        *p++ = 't';
        *p++ = 'o';
        *p++ = 'r';
        *p++ = ' ';
        *p++ = '0';
        *p++ = 'x';
        static const char hex[] = "0123456789ABCDEF";
        *p++ = hex[(g_report.exceptionVector >> 4) & 0xF];
        *p++ = hex[g_report.exceptionVector & 0xF];
        *p++ = ')';
        *p++ = '\0';
        c.text(PADDING, y, excbuf, DIM_TEXT);
        y += fh;
    } else {
        y += fh + 4 + fh;
    }

    y += SECTION_GAP;

    // Registers section
    section_header(c, y, w, fh, "Registers");

    char hexbuf[32];
    auto draw_hex_row = [&](const char* key, uint64_t val) {
        format_hex16(hexbuf, val);
        table_row(c, y, row_h, COL1_W, w, rowIdx++, key, hexbuf, VALUE_COL);
    };

    draw_hex_row("Instruction Pointer", g_report.instructionPointer);
    draw_hex_row("Code Segment",         g_report.codeSegment);
    draw_hex_row("Flags",               g_report.flags);
    draw_hex_row("Stack Pointer",       g_report.stackPointer);
    draw_hex_row("Stack Segment",       g_report.stackSegment);

    y += SECTION_GAP;

    // Page Fault section (only for vector 0x0E)
    if (g_report.exceptionVector == 0x0E) {
        section_header(c, y, w, fh, "Page Fault");

        format_hex16(hexbuf, g_report.faultingAddress);
        table_row(c, y, row_h, COL1_W, w, rowIdx++, "Faulting Address (CR2)", hexbuf,
                 TEXT_COLOR);

        // Error code bits
        auto draw_pf_bit = [&](const char* label, bool val) {
            Color col = val ? Color::from_rgb(0x22, 0x88, 0x22) : Color::from_rgb(0xAA, 0xAA, 0xAA);
            table_row(c, y, row_h, COL1_W, w, rowIdx++, label, val ? "1" : "0", col);
        };

        draw_pf_bit("Present",           g_report.pfPresent);
        draw_pf_bit("Write",             g_report.pfWrite);
        draw_pf_bit("User",              g_report.pfUser);
        draw_pf_bit("Reserved Write",    g_report.pfReservedWrite);
        draw_pf_bit("Instruction Fetch", g_report.pfInstructionFetch);
        draw_pf_bit("Protection Key",    g_report.pfProtectionKey);
        draw_pf_bit("Shadow Stack",      g_report.pfShadowStack);
        draw_pf_bit("SGX",               g_report.pfSGX);

        y += SECTION_GAP;
    }

    g_win.present();
}

extern "C" void _start() {
    fonts::init();

    // Poll for crash report
    int available = montauk::crash_report(&g_report);
    if (available <= 0) montauk::exit(1);

    if (!g_win.create("Process Crashed", WINDOW_W, WINDOW_H)) montauk::exit(1);

    render();

    while (true) {
        Montauk::WinEvent ev;
        int r = g_win.poll(&ev);
        if (r < 0) break;
        if (r == 0) {
            montauk::sleep_ms(16);
            continue;
        }

        if (ev.type == 3) break; // close

        if (ev.type == 0 && ev.key.pressed) {
            if (ev.key.scancode == 0x01) break; // Escape
            if (ev.key.scancode == 0x51) {
                // Page Down: scroll down
                g_scroll += 60;
                render();
            }
            if (ev.key.scancode == 0x49) {
                // Page Up: scroll up
                g_scroll -= 60;
                if (g_scroll < 0) g_scroll = 0;
                render();
            }
        }

        if (ev.type == 2) {
            // Resize
            render();
        }
    }

    g_win.destroy();
    montauk::exit(0);
}
