#include "ioapic.h"
#include "arch.h"
#include "fb.h"
#include "printf.h"

static volatile uint32_t *ioapic_regsel;
static volatile uint32_t *ioapic_window;
static uint32_t ioapic_gsi_base;
static int ioapic_initialized;

void ioapic_init(uint64_t base, uint32_t gsi_base) {
    ioapic_regsel = (volatile uint32_t*)(uintptr_t)base;
    ioapic_window = (volatile uint32_t*)(uintptr_t)(base + 0x10);
    ioapic_gsi_base = gsi_base;
    ioapic_initialized = 1;

    /* Читаем версию IOAPIC, чтобы получить макс. количество записей перенаправления */
    ioapic_regsel[0] = 0x01; /* IOAPICVER — регистр версии */
    uint32_t ver = ioapic_window[0];
    uint32_t max_entries = (ver >> 16) & 0xFF;

    fb_printf("ioapic: base=%p, version=%x, max entries=%u, gsi_base=%u\n",
        base, ver, max_entries, gsi_base);

    /* Маскируем все записи перенаправления */
    for(uint32_t i = 0; i <= max_entries; i++) {
        uint32_t reg = 0x10 + i * 2;
        ioapic_regsel[0] = reg;
        uint32_t low = ioapic_window[0];
        low |= (1 << 16); /* маска */
        ioapic_window[0] = low;
    }

    fb_printf("ioapic: initialized and masked\n");
}

static void ioapic_write_entry(int irq, uint64_t entry) {
    if(!ioapic_initialized) return;
    int idx = irq - ioapic_gsi_base;
    if(idx < 0) return;

    uint32_t reg = 0x10 + idx * 2;
    ioapic_regsel[0] = reg;
    ioapic_window[0] = (uint32_t)(entry & 0xFFFFFFFF);
    ioapic_regsel[0] = reg + 1;
    ioapic_window[0] = (uint32_t)((entry >> 32) & 0xFFFFFFFF);
}

static uint64_t ioapic_read_entry(int irq) {
    if(!ioapic_initialized) return 0;
    int idx = irq - ioapic_gsi_base;
    if(idx < 0) return 0;

    uint32_t reg = 0x10 + idx * 2;
    ioapic_regsel[0] = reg;
    uint32_t low = ioapic_window[0];
    ioapic_regsel[0] = reg + 1;
    uint32_t high = ioapic_window[0];
    return ((uint64_t)high << 32) | low;
}

void ioapic_mask_irq(int irq) {
    uint64_t entry = ioapic_read_entry(irq);
    entry |= (1ULL << 16);
    ioapic_write_entry(irq, entry);
}

void ioapic_unmask_irq(int irq) {
    uint64_t entry = ioapic_read_entry(irq);
    entry &= ~(1ULL << 16);
    ioapic_write_entry(irq, entry);
}

void ioapic_redirect_irq(int irq, int vector, uint64_t flags) {
    uint64_t entry = vector | (flags & ~0xFFULL);
    entry &= ~(1ULL << 16); /* unmask */
    ioapic_write_entry(irq, entry);
}
