// TODO: upstream these helper functions
// TODO: rp2350 support?

const std = @import("std");
const microzig = @import("microzig");
const rp2xxx = microzig.hal;

const peripherals = microzig.chip.peripherals;

const Scenario = packed struct(u4) {
    low: u1 = 0,
    high: u1 = 0,
    fall: u1 = 0,
    rise: u1 = 0,
};

const Destination = packed struct {
    dormant_wake: u1 = 0,
    proc0: u1 = 0,
    proc1: u1 = 0,
};

const Bank = packed struct {
    io_bank0: Destination = @bitCast(@as(u3, 0)),
    io_qspi: Destination = @bitCast(@as(u3, 0)),
};

pub fn enable_interrupt(gpio: rp2xxx.gpio.Pin, bank: Bank, mask: Scenario) void {
    const shift: u5 = @intCast(@intFromEnum(gpio) % 8 * 4);
    const shifted_mask: u32 = @as(u32, @intCast(@as(u4, @bitCast(mask)))) << shift;

    if (@as(u3, @bitCast(bank.io_bank0)) != 0) {
        const b = peripherals.IO_BANK0;
        switch (@intFromEnum(gpio)) {
            0...7 => {
                if (bank.io_bank0.dormant_wake == 1) b.DORMANT_WAKE_INTE0.raw |= shifted_mask;
                if (bank.io_bank0.proc0 == 1) b.PROC0_INTE0.raw |= shifted_mask;
                if (bank.io_bank0.proc1 == 1) b.PROC1_INTE0.raw |= shifted_mask;
            },
            8...15 => {
                if (bank.io_bank0.dormant_wake == 1) b.DORMANT_WAKE_INTE1.raw |= shifted_mask;
                if (bank.io_bank0.proc0 == 1) b.PROC0_INTE1.raw |= shifted_mask;
                if (bank.io_bank0.proc1 == 1) b.PROC1_INTE1.raw |= shifted_mask;
            },
            16...23 => {
                if (bank.io_bank0.dormant_wake == 1) b.DORMANT_WAKE_INTE2.raw |= shifted_mask;
                if (bank.io_bank0.proc0 == 1) b.PROC0_INTE2.raw |= shifted_mask;
                if (bank.io_bank0.proc1 == 1) b.PROC1_INTE2.raw |= shifted_mask;
            },
            24...29 => {
                if (bank.io_bank0.dormant_wake == 1) b.DORMANT_WAKE_INTE3.raw |= shifted_mask;
                if (bank.io_bank0.proc0 == 1) b.PROC0_INTE3.raw |= shifted_mask;
                if (bank.io_bank0.proc1 == 1) b.PROC1_INTE3.raw |= shifted_mask;
            },
            else => @panic("the RP2040 only has GPIO 0-29"),
        }

        // Enable this interrupt type
        rp2xxx.irq.enable(.IO_IRQ_BANK0);
    }

    if (@as(u3, @bitCast(bank.io_qspi)) != 0) {
        const b = peripherals.IO_QSPI;
        if (bank.io_qspi.dormant_wake == 1) b.DORMANT_WAKE_INTE.raw |= shifted_mask;
        if (bank.io_qspi.proc0 == 1) b.PROC0_INTE.raw |= shifted_mask;
        if (bank.io_qspi.proc1 == 1) b.PROC1_INTE.raw |= shifted_mask;

        // Enable this interrupt type
        rp2xxx.irq.enable(.IO_IRQ_QSPI);
    }

    // Enable CPU interrupt handling
    // rp2xxx.irq.globally_enable();
}

pub fn disable_interrupt(gpio: rp2xxx.gpio.Pin, bank: Bank, mask: Scenario) void {
    const shift: u5 = @intCast(@intFromEnum(gpio) % 8 * 4);
    const shifted_mask: u32 = @as(u32, @intCast(@as(u4, @bitCast(mask)))) << shift;

    if (@as(u3, @bitCast(bank.io_bank0)) != 0) {
        const b = peripherals.IO_BANK0;
        switch (@intFromEnum(gpio)) {
            0...7 => {
                if (bank.io_bank0.dormant_wake == 1) b.DORMANT_WAKE_INTE0.raw ^= shifted_mask;
                if (bank.io_bank0.proc0 == 1) b.PROC0_INTE0.raw ^= shifted_mask;
                if (bank.io_bank0.proc1 == 1) b.PROC1_INTE0.raw ^= shifted_mask;
            },
            8...15 => {
                if (bank.io_bank0.dormant_wake == 1) b.DORMANT_WAKE_INTE1.raw ^= shifted_mask;
                if (bank.io_bank0.proc0 == 1) b.PROC0_INTE1.raw ^= shifted_mask;
                if (bank.io_bank0.proc1 == 1) b.PROC1_INTE1.raw ^= shifted_mask;
            },
            16...23 => {
                if (bank.io_bank0.dormant_wake == 1) b.DORMANT_WAKE_INTE2.raw ^= shifted_mask;
                if (bank.io_bank0.proc0 == 1) b.PROC0_INTE2.raw ^= shifted_mask;
                if (bank.io_bank0.proc1 == 1) b.PROC1_INTE2.raw ^= shifted_mask;
            },
            24...29 => {
                if (bank.io_bank0.dormant_wake == 1) b.DORMANT_WAKE_INTE3.raw ^= shifted_mask;
                if (bank.io_bank0.proc0 == 1) b.PROC0_INTE3.raw ^= shifted_mask;
                if (bank.io_bank0.proc1 == 1) b.PROC1_INTE3.raw ^= shifted_mask;
            },
            else => @panic("the RP2040 only has GPIO 0-29"),
        }
    }

    if (@as(u3, @bitCast(bank.io_qspi)) != 0) {
        const b = peripherals.IO_QSPI;
        if (bank.io_qspi.dormant_wake == 1) b.DORMANT_WAKE_INTE.raw ^= shifted_mask;
        if (bank.io_qspi.proc0 == 1) b.PROC0_INTE.raw ^= shifted_mask;
        if (bank.io_qspi.proc1 == 1) b.PROC1_INTE.raw ^= shifted_mask;
    }
}