#include "idt.h"
#include "arch.h"
#include "fb.h"
#include "printf.h"
#include "util.h"
#include "dbg.h"
#include "lapic.h"
#include "crash.h"

struct idt_entry {
    uint16_t offset_low;
    uint16_t selector;
    uint8_t  ist;
    uint8_t  type_attr;
    uint16_t offset_mid;
    uint32_t offset_high;
    uint32_t zero;
} __attribute__((packed));

struct idt_ptr {
    uint16_t limit;
    uint64_t base;
} __attribute__((packed));

static struct idt_entry idt[256] __attribute__((aligned(16)));

struct interrupt_frame {
    uint64_t rip;
    uint64_t cs;
    uint64_t rflags;
    uint64_t rsp;
    uint64_t ss;
};

void idt_set_entry(int n, void *handler, uint8_t type_attr, uint8_t ist) {
    uint64_t addr = (uint64_t)handler;
    idt[n].offset_low  = addr & 0xFFFF;
    idt[n].selector    = 0x08;
    idt[n].ist         = ist;
    idt[n].type_attr   = type_attr;
    idt[n].offset_mid  = (addr >> 16) & 0xFFFF;
    idt[n].offset_high = (addr >> 32) & 0xFFFFFFFF;
    idt[n].zero        = 0;
}

__attribute__((interrupt)) void isr_de(struct interrupt_frame *frame) {
    crash_show(CRASH_DIVIDE_ERROR, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_db(struct interrupt_frame *frame) {
    crash_show(CRASH_INVALID_OPCODE, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_nmi(struct interrupt_frame *frame) {
    (void)frame;
}

__attribute__((interrupt)) void isr_bp(struct interrupt_frame *frame) {
    crash_show(CRASH_INVALID_OPCODE, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_of(struct interrupt_frame *frame) {
    crash_show(CRASH_INVALID_OPCODE, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_br(struct interrupt_frame *frame) {
    crash_show(CRASH_INVALID_OPCODE, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_ud(struct interrupt_frame *frame) {
    crash_show(CRASH_INVALID_OPCODE, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_nm(struct interrupt_frame *frame) {
    crash_show(CRASH_INVALID_OPCODE, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_mf(struct interrupt_frame *frame) {
    crash_show(CRASH_GPF, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_mc(struct interrupt_frame *frame) {
    crash_show(CRASH_MACHINE_CHECK, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_xf(struct interrupt_frame *frame) {
    crash_show(CRASH_GPF, frame->rip, 0, 0);
}

__attribute__((interrupt)) void isr_df(struct interrupt_frame *frame, uint64_t error) {
    crash_show(CRASH_DOUBLE_FAULT, frame->rip, 0, error);
}

__attribute__((interrupt)) void isr_ts(struct interrupt_frame *frame, uint64_t error) {
    crash_show(CRASH_GPF, frame->rip, 0, error);
}

__attribute__((interrupt)) void isr_np(struct interrupt_frame *frame, uint64_t error) {
    crash_show(CRASH_GPF, frame->rip, 0, error);
}

__attribute__((interrupt)) void isr_ss(struct interrupt_frame *frame, uint64_t error) {
    crash_show(CRASH_GPF, frame->rip, 0, error);
}

__attribute__((interrupt)) void isr_gp(struct interrupt_frame *frame, uint64_t error) {
    crash_show(CRASH_GPF, frame->rip, 0, error);
}

__attribute__((interrupt)) void isr_pf(struct interrupt_frame *frame, uint64_t error) {
    uint64_t cr2 = read_cr2();
    crash_show(CRASH_PAGE_FAULT, frame->rip, cr2, error);
}

__attribute__((interrupt)) void isr_ac(struct interrupt_frame *frame, uint64_t error) {
    crash_show(CRASH_GPF, frame->rip, 0, error);
}

__attribute__((interrupt)) void isr_cp(struct interrupt_frame *frame, uint64_t error) {
    crash_show(CRASH_GPF, frame->rip, 0, error);
}

__attribute__((interrupt)) void isr_generic(struct interrupt_frame *frame) {
    lapic_eoi();
    (void)frame;
}

__attribute__((interrupt)) void isr_spurious(struct interrupt_frame *frame) {
    (void)frame;
}

volatile uint64_t timer_ticks = 0;

__attribute__((interrupt)) void isr_timer_handler(struct interrupt_frame *frame) {
    timer_ticks++;
    lapic_eoi();
    (void)frame;
}

void idt_init(void) {
    dbg_printf("IDT: init...\n");
    memset(idt, 0, sizeof(idt));
    dbg_printf("IDT: table zeroed (%d bytes)\n", sizeof(idt));
    for(int i = 0; i < 256; i++)
        idt_set_entry(i, isr_generic, 0x8E, 0);
    /* Exceptions 0-31: use IST1 (dedicated crash stack) so crash screen works
     * even if the kernel stack is corrupted/overflowed */
    idt_set_entry(0,  isr_de, 0x8E, 1);
    idt_set_entry(1,  isr_db, 0x8E, 1);
    idt_set_entry(2,  isr_nmi, 0x8E, 1);
    idt_set_entry(3,  isr_bp, 0x8E, 1);
    idt_set_entry(4,  isr_of, 0x8E, 1);
    idt_set_entry(5,  isr_br, 0x8E, 1);
    idt_set_entry(6,  isr_ud, 0x8E, 1);
    idt_set_entry(7,  isr_nm, 0x8E, 1);
    idt_set_entry(8,  isr_df, 0x8E, 1);
    idt_set_entry(10, isr_ts, 0x8E, 1);
    idt_set_entry(11, isr_np, 0x8E, 1);
    idt_set_entry(12, isr_ss, 0x8E, 1);
    idt_set_entry(13, isr_gp, 0x8E, 1);
    idt_set_entry(14, isr_pf, 0x8E, 1);
    idt_set_entry(16, isr_mf, 0x8E, 1);
    idt_set_entry(17, isr_ac, 0x8E, 1);
    idt_set_entry(18, isr_mc, 0x8E, 1);
    idt_set_entry(19, isr_xf, 0x8E, 1);
    idt_set_entry(21, isr_cp, 0x8E, 1);
    idt_set_entry(0x20, isr_timer_handler, 0x8E, 0);
    idt_set_entry(0xFF, isr_spurious, 0x8E, 0);
    dbg_printf("IDT: handlers installed (256 entries)\n");
    struct idt_ptr ptr = { .limit = sizeof(idt) - 1, .base = (uint64_t)idt };
    lidt(&ptr);
    dbg_printf("IDT: loaded (base=%p)\n", (void*)ptr.base);
}
