// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "work_queue.h"
#include "spinlock.h"

extern void serial_write(const char *str);

#define WORK_QUEUE_SIZE 64

static work_item_t work_queue[WORK_QUEUE_SIZE];
static volatile int wq_head = 0;
static volatile int wq_tail = 0;
static spinlock_t wq_lock = SPINLOCK_INIT;

void work_queue_submit(work_fn_t fn, void *arg) {
    if (!fn) return;

    uint64_t flags = spinlock_acquire_irqsave(&wq_lock);
    int next_tail = (wq_tail + 1) % WORK_QUEUE_SIZE;
    if (next_tail == wq_head) {
        // Queue full — drop the work item
        spinlock_release_irqrestore(&wq_lock, flags);
        return;
    }
    work_queue[wq_tail].fn = fn;
    work_queue[wq_tail].arg = arg;
    wq_tail = next_tail;
    spinlock_release_irqrestore(&wq_lock, flags);
}

bool work_queue_drain_one(void) {
    uint64_t flags = spinlock_acquire_irqsave(&wq_lock);
    if (wq_head == wq_tail) {
        spinlock_release_irqrestore(&wq_lock, flags);
        return false;
    }
    work_item_t item = work_queue[wq_head];
    wq_head = (wq_head + 1) % WORK_QUEUE_SIZE;
    spinlock_release_irqrestore(&wq_lock, flags);

    // Execute outside the lock
    if (item.fn) {
        item.fn(item.arg);
    }
    return true;
}

void work_queue_drain_loop(void) {
    while (1) {
        // Try to drain all pending work
        while (work_queue_drain_one()) {
            // Keep draining
        }
        
        // No work — halt the CPU until an interrupt wakes us.
        // With legacy PIC, APs don't receive timer interrupts, so they'll
        // sleep until an IPI is sent (e.g., when work is submitted).
        // This is ideal: APs use 0% CPU when idle.
        asm volatile("sti; hlt; cli");
    }
}
