/* Interrupt handling for CubeCactusOS */

#include "kernel.h"

extern void outb(uint16_t port, uint8_t value);
extern uint8_t inb(uint16_t port);
extern int printf(const char* format, ...);

/* IDT entry structure */
struct idt_entry {
    uint16_t offset_low;
    uint16_t selector;
    uint8_t zero;
    uint8_t type_attr;
    uint16_t offset_high;
} __attribute__((packed));

/* IDT pointer structure */
struct idt_ptr {
    uint16_t limit;
    uint32_t base;
} __attribute__((packed));

/* IDT with 256 entries */
static struct idt_entry idt[256];
static struct idt_ptr idtp;

/* PIC ports */
#define PIC1_COMMAND 0x20
#define PIC1_DATA    0x21
#define PIC2_COMMAND 0xA0
#define PIC2_DATA    0xA1

/* PIC initialization */
void init_pic() {
    /* ICW1 - Start initialization */
    outb(PIC1_COMMAND, 0x11);
    outb(PIC2_COMMAND, 0x11);
    
    /* ICW2 - Set vector offsets */
    outb(PIC1_DATA, 0x20); /* Master PIC: IRQ 0-7 -> INT 0x20-0x27 */
    outb(PIC2_DATA, 0x28); /* Slave PIC: IRQ 8-15 -> INT 0x28-0x2F */
    
    /* ICW3 - Set up cascading */
    outb(PIC1_DATA, 0x04); /* Tell master PIC there's a slave at IRQ2 */
    outb(PIC2_DATA, 0x02); /* Tell slave PIC its cascade identity */
    
    /* ICW4 - Set mode */
    outb(PIC1_DATA, 0x01); /* 8086 mode */
    outb(PIC2_DATA, 0x01);
    
    /* Mask all IRQs except IRQ1 (keyboard) */
    outb(PIC1_DATA, 0xFD); /* Enable IRQ1 only */
    outb(PIC2_DATA, 0xFF); /* Disable all slave IRQs */
}

/* Set IDT gate */
void idt_set_gate(uint8_t num, uint32_t handler, uint16_t selector, uint8_t flags) {
    idt[num].offset_low = handler & 0xFFFF;
    idt[num].offset_high = (handler >> 16) & 0xFFFF;
    idt[num].selector = selector;
    idt[num].zero = 0;
    idt[num].type_attr = flags;
}

/* External interrupt handlers defined in assembly */
extern void irq1_handler_asm();

/* Initialize IDT */
void init_idt() {
    idtp.limit = (sizeof(struct idt_entry) * 256) - 1;
    idtp.base = (uint32_t)&idt;
    
    /* Clear IDT */
    for (int i = 0; i < 256; i++) {
        idt_set_gate(i, 0, 0, 0);
    }
    
    /* Set up keyboard interrupt (IRQ1 = INT 0x21) */
    idt_set_gate(0x21, (uint32_t)irq1_handler_asm, 0x08, 0x8E);
    
    /* Load IDT */
    __asm__ __volatile__("lidt %0" : : "m"(idtp));
}

/* Send End of Interrupt to PIC */
void send_eoi(uint8_t irq) {
    if (irq >= 8) {
        outb(PIC2_COMMAND, 0x20);
    }
    outb(PIC1_COMMAND, 0x20);
}

/* Keyboard IRQ handler (called from assembly) */
extern void keyboard_irq_handler();

void irq1_handler() {
    keyboard_irq_handler();
    send_eoi(1);
}

/* Initialize interrupt system */
void init_interrupts() {
    printf("  - Initializing IDT...\n");
    init_idt();
    
    printf("  - Initializing PIC...\n");
    init_pic();
    
    printf("  - Enabling interrupts...\n");
    __asm__ __volatile__("sti");
}
