#include <err.h>
#include <sys/types.h>
#include <debug.h>
#include <reg.h>
#include <kernel/thread.h>
#include <platform/interrupts.h>
#include <platform/armemu.h>
#include <arch/ops.h>
#include <arch/arm.h>
#include "platform_p.h"

struct int_handler_struct {
	int_handler handler;
	void *arg;
};

static struct int_handler_struct int_handler_table[PIC_MAX_INT];

void platform_init_interrupts(void)
{
	// mask all the interrupts
	*REG32(PIC_MASK_LATCH) = 0xffffffff;
}

status_t mask_interrupt(unsigned int vector, bool *oldstate)
{
	if (vector >= PIC_MAX_INT)
		return ERR_INVALID_ARGS;

//	dprintf("%s: vector %d\n", __PRETTY_FUNCTION__, vector);

	enter_critical_section();

	if (oldstate)
		*oldstate = *REG32(PIC_MASK) & (1<<vector);
	*REG32(PIC_MASK_LATCH) = 1 << vector;

	exit_critical_section();

	return NO_ERROR;
}

status_t unmask_interrupt(unsigned int vector, bool *oldstate)
{
	if (vector >= PIC_MAX_INT)
		return ERR_INVALID_ARGS;

//	dprintf("%s: vector %d\n", __PRETTY_FUNCTION__, vector);

	enter_critical_section();

	if (oldstate)
		*oldstate = *REG32(PIC_MASK) & (1<<vector);
	*REG32(PIC_UNMASK_LATCH) = 1 << vector;

	exit_critical_section();

	return NO_ERROR;
}

void platform_irq(struct arm_iframe *frame)
{
	// nothing pending
	uint32_t current_bit = *REG32(PIC_CURRENT_BIT);
	if (current_bit == 0)
		return;

	inc_critical_section();

	// get the current vector
	int vector = *REG32(PIC_CURRENT_NUM);

//	dprintf("platform_irq: spsr 0x%x, pc 0x%x, currthread %p, vector %d\n", frame->spsr, frame->pc, current_thread, vector);
//	Thread::GetCurrent()->Dump();

	// mask it out
//	*REG32(PIC_MASK_LATCH) = current_bit;

	// clear it, let another interrupt latch
	*REG32(PIC_CURRENT_BIT) = current_bit;

	// deliver the interrupt
	enum handler_return ret; 

	ret = INT_NO_RESCHEDULE;
	if (int_handler_table[vector].handler)
		ret = int_handler_table[vector].handler(int_handler_table[vector].arg);

	if (ret == INT_RESCHEDULE)
		thread_preempt();

	dec_critical_section();

//	dprintf("platform_irq: exit\n");
}

void platform_fiq(struct arm_iframe *frame)
{
	panic("FIQ: unimplemented\n");
}

void register_int_handler(unsigned int vector, int_handler handler, void *arg)
{
	if (vector >= PIC_MAX_INT)
		panic("register_int_handler: vector out of range %d\n", vector);

	enter_critical_section();

	int_handler_table[vector].handler = handler;
	int_handler_table[vector].arg = arg;

	exit_critical_section();
}

