// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "process.h"
#include "graphics.h"
#include "io.h"
#include "kutils.h"

extern void serial_write(const char *str);

static void draw_string_centered(int y, const char *s, uint32_t color) {
    if (!s) return;
    int len = 0;
    while (s[len]) len++;
    int x = (get_screen_width() - (len * 8)) / 2;
    draw_string(x, y, s, color);
}

void kernel_panic(registers_t *regs, const char *error_name) {
    // Disable interrupts to prevent nested panics
    asm volatile("cli");

    // Clear back buffer to black
    graphics_clear_back_buffer(0x00000000);

    int sh = get_screen_height();
    int cy = sh / 2;

    // Draw header
    draw_string_centered(cy - 150, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", 0xFFFF0000);
    draw_string_centered(cy - 130, "KERNEL EXCEPTION OCCURRED", 0xFFFFFFFF);
    draw_string_centered(cy - 110, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", 0xFFFF0000);

    // Error name
    char err_buf[256];
    int pos = 0;
    const char *prefix = "Exception: ";
    while(prefix[pos]) { err_buf[pos] = prefix[pos]; pos++; }
    int i = 0;
    while(error_name[i]) { err_buf[pos++] = error_name[i++]; }
    err_buf[pos] = 0;
    draw_string_centered(cy - 70, err_buf, 0xFFFFCC00);

    // Details - simplified centering by drawing them as a block or individually centered
    char info_buf[64];
    
    // Vector
    pos = 0;
    prefix = "Vector: ";
    while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
    uint64_t v = regs->int_no;
    const char* digits = "0123456789ABCDEF";
    info_buf[pos++] = '0'; info_buf[pos++] = 'x';
    for (int i = 15; i >= 0; i--) {
        info_buf[pos + i] = digits[v & 0xF];
        v >>= 4;
    }
    info_buf[pos + 16] = 0;
    draw_string_centered(cy - 40, info_buf, 0xFFFFFFFF);

    // Error Code
    pos = 0;
    prefix = "Error Code: ";
    while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
    v = regs->err_code;
    info_buf[pos++] = '0'; info_buf[pos++] = 'x';
    for (int i = 15; i >= 0; i--) {
        info_buf[pos + i] = digits[v & 0xF];
        v >>= 4;
    }
    info_buf[pos + 16] = 0;
    draw_string_centered(cy - 20, info_buf, 0xFFFFFFFF);

    // RIP
    pos = 0;
    prefix = "RIP: ";
    while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
    v = regs->rip;
    info_buf[pos++] = '0'; info_buf[pos++] = 'x';
    for (int i = 15; i >= 0; i--) {
        info_buf[pos + i] = digits[v & 0xF];
        v >>= 4;
    }
    info_buf[pos + 16] = 0;
    draw_string_centered(cy, info_buf, 0xFFFFFFFF);

    // CR2 for page faults
    if (regs->int_no == 14) {
        uint64_t cr2;
        asm volatile("mov %%cr2, %0" : "=r"(cr2));
        pos = 0;
        prefix = "CR2: ";
        while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
        info_buf[pos++] = '0'; info_buf[pos++] = 'x';
        for (int i = 15; i >= 0; i--) {
            info_buf[pos + i] = digits[cr2 & 0xF];
            cr2 >>= 4;
        }
        info_buf[pos + 16] = 0;
        draw_string_centered(cy + 20, info_buf, 0xFFFF5555);
    }

    // Message
    draw_string_centered(cy + 100, "The system has been halted to prevent damage.", 0xFFFFFFFF);
    draw_string_centered(cy + 120, "Please restart your computer.", 0xFFAAAAAA);


    // Flip buffer to screen
    graphics_mark_screen_dirty();
    graphics_flip_buffer();

    char hex_buf[17];
    serial_write("\n*** KERNEL PANIC ***\n");
    serial_write(error_name);
    serial_write("\n");

    serial_write("Vector: 0x");
    k_itoa_hex(regs->int_no, hex_buf);
    serial_write(hex_buf);
    serial_write("\n");

    serial_write("Error Code: 0x");
    k_itoa_hex(regs->err_code, hex_buf);
    serial_write(hex_buf);
    serial_write("\n");

    serial_write("RIP: 0x");
    k_itoa_hex(regs->rip, hex_buf);
    serial_write(hex_buf);
    serial_write("\n");

    if (regs->int_no == 14) {
        uint64_t cr2;
        asm volatile("mov %%cr2, %0" : "=r"(cr2));
        serial_write("CR2: 0x");
        k_itoa_hex(cr2, hex_buf);
        serial_write(hex_buf);
        serial_write("\n");
    }

    // Halt
    while(1) {
        asm volatile("cli; hlt");
    }
}
