#include <stdint.h>

// Define the structure for an IDT entry
typedef struct {
    uint16_t offset_low;   // Lower 16 bits of the offset to the interrupt handler
    uint16_t selector;     // Selector for the GDT segment
    uint8_t  zero;         // Reserved, should be zero
    uint8_t  type_attr;    // Type and attributes (e.g., interrupt gate, DPL)
    uint16_t offset_high;  // Upper 16 bits of the offset to the interrupt handler
} __attribute__((packed)) idt_entry_t;

// Define the structure for the IDT pointer
typedef struct {
    uint16_t limit;        // Size of the IDT - 1
    uint32_t base;         // Base address of the IDT
} __attribute__((packed)) idt_ptr_t;

// Define the IDT
#define IDT_SIZE 256
idt_entry_t idt[IDT_SIZE];

// Pointer to the IDT
idt_ptr_t idt_ptr;

// Function to set an IDT entry
void set_idt_entry(int vector, uint32_t base, uint16_t selector, uint8_t type_attr) {
    idt[vector].offset_low = base & 0xFFFF;
    idt[vector].selector = selector;
    idt[vector].zero = 0;
    idt[vector].type_attr = type_attr;
    idt[vector].offset_high = (base >> 16) & 0xFFFF;
}

// PIC Ports
#define PIC1_CMD 0x20
#define PIC1_DATA 0x21
#define PIC2_CMD 0xA0
#define PIC2_DATA 0xA1

// Initialize PIC
void pic_init() {
    // Initialize PIC1 and PIC2
    outb(PIC1_CMD, 0x11);  // Start initialization sequence (ICW1)
    outb(PIC2_CMD, 0x11);

    outb(PIC1_DATA, 0x20);  // ICW2: Remap PIC1 to interrupt vector 0x20
    outb(PIC2_DATA, 0x28);  // ICW2: Remap PIC2 to interrupt vector 0x28

    outb(PIC1_DATA, 0x04);  // ICW3: Tell PIC1 that PIC2 is at IRQ2
    outb(PIC2_DATA, 0x02);  // ICW3: Tell PIC2 its cascade identity

    outb(PIC1_DATA, 0x01);  // ICW4: Set mode to 8086/88
    outb(PIC2_DATA, 0x01);

    // Mask all interrupts
    outb(PIC1_DATA, 0xFD);  // Only IRQ1 (PS/2 keyboard) is enabled
    outb(PIC2_DATA, 0xFF);
}

// Function to initialize the IDT
void idt_init() {
	pic_init();
	
    idt_ptr.limit = (sizeof(idt_entry_t) * IDT_SIZE) - 1;
    idt_ptr.base = (uint32_t) &idt;

    // Clear IDT entries
    for (int i = 0; i < IDT_SIZE; i++) {
        idt[i].offset_low = 0;
        idt[i].offset_high = 0;
        idt[i].selector = 0;
        idt[i].type_attr = 0;
    }

    // Load the IDT
    asm volatile("lidt %0" : : "m" (idt_ptr));
}
