#include <debug.h>
#include <list.h>
#include <kernel/thread.h>
#include <kernel/timer.h>
#include <platform/timer.h>
#include <platform.h>

static struct list_node timer_queue;

void timer_initialize(struct timer *timer)
{
	timer->magic = TIMER_MAGIC;
	list_clear_node(&timer->node);
	timer->scheduled_time = 0;
	timer->periodic_time = 0;
	timer->callback = 0;
	timer->arg = 0;
}

static void insert_timer_in_queue(struct timer *timer)
{
	struct timer *entry;

	list_for_every_entry(&timer_queue, entry, struct timer, node) {
		if (entry->scheduled_time > timer->scheduled_time) {
			list_add_before(&entry->node, &timer->node);
			return;
		}
	}

	/* walked off the end of the list */
	list_add_tail(&timer_queue, &timer->node);
}

void timer_set_oneshot(struct timer *timer, time_t delay, timer_callback callback, void *arg)
{
	time_t now;

	ASSERT(timer->magic == TIMER_MAGIC);	

	if (list_in_list(&timer->node)) {
		panic("timer %p already in list\n", timer);
	}

	now = current_time();
	timer->scheduled_time = now + delay;
	timer->periodic_time = 0;
	timer->callback = callback;
	timer->arg = arg;

	enter_critical_section();

	insert_timer_in_queue(timer);

	exit_critical_section();
}

/* called at interrupt time to process any pending timers */
static enum handler_return timer_tick(void *arg, time_t now)
{
	struct timer *timer;
	enum handler_return ret = INT_NO_RESCHEDULE;

//	dprintf("timer_tick: now 0x%x\n", (unsigned int)now);

	for (;;) {
		/* see if there's an event to process */
		timer = list_peek_head_type(&timer_queue, struct timer, node);
		if (likely(!timer || now < timer->scheduled_time))
			return ret;

		/* process it */
		list_delete(&timer->node);
//		timer = list_remove_head_type(&timer_queue, struct timer, node);
//		ASSERT(timer);

		if (timer->callback(timer, now, timer->arg) == INT_RESCHEDULE)
			ret = INT_RESCHEDULE;
	}
}

void timer_init(void)
{
	list_initialize(&timer_queue);

	/* register for a periodic timer tick */
	platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */
}


