/*
 * Copyright (c) 2026, Chloe M.
 * Provided under the BSD-3 clause.
 *
 * Description: IRQ chip management
 * Author:      Chloe M.
 */

#include <machine/irqchip.h>
#include <machine/pio.h>
#include <ke/knot.h>
#include <ex/trace.h>

/* PIC data ports */
#define PIC_MASTER_DATA 0x21
#define PIC_SLAVE_DATA  0xA1

#define DTRACE(Fmt, ...) \
    TRACE("[ IRQCHIP ]: " Fmt, ##__VA_ARGS__)

/* Set if dual i8259 is present */
#define MADT_PCAT_COMPAT BIT(0)

/* Local APIC list */
static ACPI_LOCAL_APIC *LapicList[LAPIC_MAX];
static USIZE LapicCount = 0;

/*
 * Append a Local APIC descriptor
 *
 * @Lapic: LAPIC to append
 */
static VOID
AppendLapic(ACPI_LOCAL_APIC *Lapic)
{
    if (Lapic == NULL) {
        return;
    }

    if (LapicCount >= LAPIC_MAX) {
        return;
    }

    if (LapicCount < 4) {
        DTRACE("lapic%d at cpu%d\n", Lapic->ApicId, Lapic->ProcessorId);
    }

    LapicList[LapicCount++] = Lapic;
}

ACPI_LOCAL_APIC *
MdLapicByIndex(USIZE Index)
{
    if (Index >= LapicCount) {
        return NULL;
    }

    return LapicList[Index];
}

VOID
MdIrqChipInit(VOID)
{
    ACPI_MADT *Madt;
    ACPI_APIC_HEADER *ApicHdr;
    ACPI_LOCAL_APIC *Lapic;
    UCHAR *MadtCur, *MadtEnd;

    Madt = AcpiQuery("APIC");
    if (Madt == NULL) {
        KeKnot(
            KNOT_UNBOUND_RSRC,
            "could not locate ACPI MADT\n"
        );
    }

    /*
     * If we have a dual-i8259 configuration present on the
     * machine, we'll want to disable it so it doesn't mess
     * up Local APIC operation and such.
     */
    if (ISSET(Madt->Flags, MADT_PCAT_COMPAT)) {
        DTRACE("detected dual i8259; disabling...\n");
        MdOutb(PIC_MASTER_DATA, 0xFF);
        MdOutb(PIC_SLAVE_DATA, 0xFF);
    }

    MadtCur = (UCHAR *)(Madt + 1);
    MadtEnd = (UCHAR *)Madt + Madt->Header.Length;

    while (MadtCur < MadtEnd) {
        ApicHdr = (ACPI_APIC_HEADER *)MadtCur;

        switch (ApicHdr->Type) {
        case APIC_TYPE_LOCAL_APIC:
            Lapic = (ACPI_LOCAL_APIC *)ApicHdr;
            AppendLapic(Lapic);
            break;
        }

        MadtCur += ApicHdr->Length;
    }
}
