/*
    * Apic.hpp
    * Local APIC (Advanced Programmable Interrupt Controller)
    * Copyright (c) 2025 Daniel Hammer
*/

#pragma once
#include <cstdint>

namespace Hal {
    namespace LocalApic {
        // Local APIC register offsets
        constexpr uint32_t REG_ID               = 0x020;
        constexpr uint32_t REG_VERSION           = 0x030;
        constexpr uint32_t REG_TPR               = 0x080;
        constexpr uint32_t REG_EOI               = 0x0B0;
        constexpr uint32_t REG_SPURIOUS          = 0x0F0;
        constexpr uint32_t REG_ICR_LOW           = 0x300;
        constexpr uint32_t REG_ICR_HIGH          = 0x310;
        constexpr uint32_t REG_TIMER_LVT         = 0x320;
        constexpr uint32_t REG_LINT0_LVT         = 0x350;
        constexpr uint32_t REG_LINT1_LVT         = 0x360;
        constexpr uint32_t REG_ERROR_LVT         = 0x370;
        constexpr uint32_t REG_TIMER_INITIAL      = 0x380;
        constexpr uint32_t REG_TIMER_CURRENT      = 0x390;
        constexpr uint32_t REG_TIMER_DIVIDE       = 0x3E0;

        // Spurious vector number
        constexpr uint8_t SPURIOUS_VECTOR = 0xFF;

        // MSR for APIC base
        constexpr uint32_t MSR_APIC_BASE = 0x1B;

        void Initialize(uint64_t apicBasePhys);

        // Initialize the Local APIC on an AP.
        // Uses the same MMIO base as the BSP (each CPU's APIC is at the same
        // physical address but routes to its own hardware).
        void InitializeAP();

        // Re-enable the Local APIC after S3 resume.
        // The MMIO mapping and base address survive (they're in page tables / RAM).
        // Only the SVR and TPR hardware registers need reprogramming.
        void Reinitialize();

        void SendEOI();
        void SendFixedIpi(uint32_t apicId, uint8_t vector);
        uint32_t GetId();

        uint32_t ReadRegister(uint32_t reg);
        void WriteRegister(uint32_t reg, uint32_t value);
    };
};
