#include <irq/irq.h>
#include <libk/list.h>
#include <libk/std.h>
#include <mm/liballoc.h>
#include <sync/spin_lock.h>

#if defined(__x86_64__)
#include <amd64/apic.h>
#include <amd64/intr.h>
#endif

/* TODO: figure out a generic way to work with IRQs */

static struct list_node_link* irqs = NULL;
static spin_lock_t irqs_lock;

bool irq_attach (void (*func) (void*, void*), void* arg, uint32_t irq_num, uint32_t flags) {
  struct irq* irq = malloc (sizeof (*irq));
  if (irq == NULL) {
    return false;
  }

  irq->func = func;
  irq->arg = arg;
  irq->irq_num = irq_num;
  irq->flags = flags;

  spin_lock (&irqs_lock);
  list_append (irqs, &irq->irqs_link);
  spin_unlock (&irqs_lock);

#if defined(__x86_64__)
  uint8_t resolution = amd64_resolve_irq (irq_num);
  amd64_ioapic_route_irq (irq_num, resolution, 0, amd64_lapic_id ());
#endif

  return true;
}

void irq_detach (void (*func) (void*, void*)) {
  struct list_node_link *irq_link, *irq_link_tmp;
  spin_lock (&irqs_lock);

  list_foreach (irqs, irq_link, irq_link_tmp) {
    struct irq* irq = list_entry (irq_link, struct irq, irqs_link);

    if ((uintptr_t)irq->func == (uintptr_t)func)
      list_remove (irqs, irq_link);
  }

  spin_unlock (&irqs_lock);
}

struct irq* irq_find (uint32_t irq_num) {
  struct list_node_link *irq_link, *irq_link_tmp;
  spin_lock (&irqs_lock);

  list_foreach (irqs, irq_link, irq_link_tmp) {
    struct irq* irq = list_entry (irq_link, struct irq, irqs_link);

    if (irq->irq_num == irq_num) {
      spin_unlock (&irqs_lock);
      return irq;
    }
  }

  spin_unlock (&irqs_lock);

  return NULL;
}
