#include <stdint.h>
#include "idt.h"
#include "print.h"
#include "vga.h"


#define IDT_ENTRIES 256
// Declare the IDT and IDT pointer as external symbols
struct idt_entry_t idt[IDT_ENTRIES];  // Assuming 256 entries
struct idt_ptr_t idt_ptr;


// Declare ISRs implemented in assembly
extern void isr0();
extern void isr1();
extern void isr2();
extern void isr3();
extern void isr4();
extern void isr5();
extern void isr6();
extern void isr7();
extern void isr8();
extern void isr9();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
// Continue up to the required number of ISRs


// IDT entry setup for 64-bit mode
void setup_idt_entry(int index, void (*handler)(), uint16_t selector, uint8_t type_attr) {
    uint64_t handler_addr = (uint64_t)handler;

    idt[index].offset_low = handler_addr & 0xFFFF;            // Lower 16 bits of handler function address
    idt[index].selector = selector;                           // Segment selector (kernel code segment)
    idt[index].ist = 0;                                       // Interrupt Stack Table (IST)
    idt[index].type_attr = type_attr;                         // Type and attributes (P, DPL, S, Type)
    idt[index].offset_mid = (handler_addr >> 16) & 0xFFFF;    // Middle 16 bits of handler address
    idt[index].offset_high = (handler_addr >> 32) & 0xFFFFFFFF; // Higher 32 bits of handler address (for 64-bit)
    idt[index].zero = 0;                                      // Reserved (set to 0)
}

// Initialize the IDT
void init_idt() {
    // Setup each IDT entry
    setup_idt_entry(0, isr0, 0x08, 0x8E);  // Divide-by-zero
    setup_idt_entry(1, isr1, 0x08, 0x8E);  // Debug
    setup_idt_entry(2, isr2, 0x08, 0x8E);  // Non-maskable interrupt
    setup_idt_entry(3, isr3, 0x08, 0x8E);  // Breakpoint
    setup_idt_entry(4, isr4, 0x08, 0x8E);  // Overflow
    setup_idt_entry(5, isr5, 0x08, 0x8E);  // Bound Range Exceeded
    setup_idt_entry(6, isr6, 0x08, 0x8E);  // Invalid Opcode
    setup_idt_entry(7, isr7, 0x08, 0x8E);  // Device Not Available
    setup_idt_entry(8, isr8, 0x08, 0x8E);  // Double Fault
    setup_idt_entry(9, isr9, 0x08, 0x8E);  // Coprocessor Segment Overrun
    setup_idt_entry(10, isr10, 0x08, 0x8E); // Invalid TSS
    setup_idt_entry(11, isr11, 0x08, 0x8E); // Segment Not Present
    setup_idt_entry(12, isr12, 0x08, 0x8E); // Stack-Segment Fault
    setup_idt_entry(13, isr13, 0x08, 0x8E); // General Protection Fault
    setup_idt_entry(14, isr14, 0x08, 0x8E); // Page Fault
    setup_idt_entry(15, isr15, 0x08, 0x8E); // Reserved
    setup_idt_entry(16, isr16, 0x08, 0x8E); // x87 FPU Floating-Point Error
    setup_idt_entry(17, isr17, 0x08, 0x8E); // Alignment Check
    setup_idt_entry(18, isr18, 0x08, 0x8E); // Machine Check
    setup_idt_entry(19, isr19, 0x08, 0x8E); // SIMD Floating-Point Exception
    setup_idt_entry(20, isr20, 0x08, 0x8E); // Virtualization Exception
    // Additional Reserved Entries as needed, or continue with ISR setup

    // Load the IDT
    // IDT pointer setup
    idt_ptr.limit = sizeof(idt) - 1;
    idt_ptr.base = (uint64_t)&idt;

    // Load the IDT with the lidt instruction
    __asm__ volatile ("lidt %0" : : "m"(idt_ptr));

}


void isr_common_handler(uint32_t isr_number) {
    printf("Interrupt received: ");
    printf("IDT: %d", isr_number);  // To verify if the correct ISR is triggered
    printf("\n");
    
    
    kpanic(isr_number);
}



// Define kpanic to handle panic with BSOD and error messages
void kpanic(int error_code) {
    // Set the background color to blue and text color to white
    print_set_color(PRINT_COLOR_BLUE, PRINT_COLOR_BLUE);
    move_print_cursor(0, 0);

    // Fill the entire screen with the background color
    for (int y = 0; y < 25; y++) {  // Replace SCREEN_HEIGHT with the actual screen height
        for (int x = 0; x < 80; x++) {  // Replace SCREEN_WIDTH with the actual screen width
            print_char(' ');  // Print a space to fill the background
        }
    }

    // Set the text color to white and background color to blue
    print_set_color(PRINT_COLOR_WHITE, PRINT_COLOR_BLUE);
    move_print_cursor(0, 0);

    // Print the text in the top left corner
    print_str("              _ \n");
    print_str("             / )\n");
    print_str("         _  / / \n");
    print_str("        (_)( (  \n");
    print_str("           | |  \n");
    print_str("         _ ( (  \n");
    print_str("        (_) \\ \\ \n");
    print_str("             \\_)\n");
    print_str("\n\n\n");
    print_str("     KERNEL PANIC\n\n");
    print_str("     ");


    switch (error_code) {
        case ERROR_DIVIDE_BY_ZERO:
            print_str("Error: Divide-by-Zero Exception\n");
            break;
        case ERROR_DEBUG:
            print_str("Error: Debug Exception\n");
            break;
        case ERROR_NMI:
            print_str("Error: Non-Maskable Interrupt\n");
            break;
        case ERROR_BREAKPOINT:
            print_str("Error: Breakpoint Exception\n");
            break;
        case ERROR_OVERFLOW:
            print_str("Error: Overflow Exception\n");
            break;
        case ERROR_BOUND:
            print_str("Error: Bound Range Exceeded\n");
            break;
        case ERROR_INVALID_OP:
            print_str("Error: Invalid Opcode\n");
            break;
        case ERROR_DEVICE_NOT_AVAIL:
            print_str("Error: Device Not Available\n");
            break;
        case ERROR_DOUBLE_FAULT:
            print_str("Error: Double Fault\n");
            break;
        case ERROR_COPROCESSOR_OVERRUN:
            print_str("Error: Coprocessor Segment Overrun\n");
            break;
        case ERROR_INVALID_TSS:
            print_str("Error: Invalid TSS\n");
            break;
        case ERROR_SEGMENT_NOT_PRESENT:
            print_str("Error: Segment Not Present\n");
            break;
        case ERROR_STACK_SEGMENT:
            print_str("Error: Stack-Segment Fault\n");
            break;
        case ERROR_GENERAL_PROTECTION:
            print_str("Error: General Protection Fault\n");
            break;
        case ERROR_PAGE_FAULT:
            print_str("Error: Page Fault\n");
            break;
        case FILESYSTEM_ERROR:
            print_str("Error: Filesystem Error\n");
            break;
        default:
            printf("Error: Unknown Exception '%d'\n", error_code);
            break;
    }

    // Halt the system
    while (1) {}
}
void idt_install() {
    print_set_color(PRINT_COLOR_LIGHT_GRAY, PRINT_COLOR_BLACK);
    print_str("[ init ] IDT\n");
    init_idt();
    printf("IDT Base: %p\n", (void*)idt_ptr.base);   // For pointer
    printf("IDT Limit: %x\n", idt_ptr.limit);        // Assuming limit is small enough for %x

    print_set_color(PRINT_COLOR_GREEN, PRINT_COLOR_BLACK);
    print_str("[ OK ] IDT\n");
}
