/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/


#include	"task.h"
#include	"memory_debug.h"
#include	"utils.h"
#include	"lock_level.h"
#include	"pri_level.h"

void tick_thread();

typedef struct tick_func {
	struct tick_func *	next;
	void			(*func)();
	int			data;
	int			interval;
} TICK_FUNC;

typedef struct tick_wheel {
	struct tick_wheel *	next;
	TICK_FUNC *		list;
	unsigned int		invoke;
} TICK_WHEEL;


TICK_WHEEL * wheel;
SEM tick_lock;
TICK_FUNC * target_tick;

void
init_tick()
{
	tick_lock = new_lock(LL_TICK);
	create_task(tick_thread,0,PRI_TICK);
}


void
insert_tick(TICK_FUNC * tl,unsigned int time)
{
TICK_WHEEL * w, ** wp;
	tl->next = 0;
	lock_task(tick_lock);
	for ( wp = &wheel ; *wp ; wp = &(*wp)->next ) {
		w = *wp;
		if ( w->invoke > time ) {
			break;
		}
		else if ( w->invoke == time ) {
			tl->next = w->list;
			w->list = tl;
			goto end;
		}
	}
	w = d_alloc(sizeof(*w),1040);
	w->invoke = time;
	w->list = tl;
	w->next = *wp;
	*wp = w;
end:
	unlock_task(tick_lock,"new_tick(1)");
}

void
new_tick(void (*func)(),int interval,int data)
{
TICK_FUNC * tl;
int _interval;
	tl = d_alloc(sizeof(*tl),1041);
	tl->interval = interval;
	tl->func = func;
	tl->data = data;
	tl->next = 0;
	if ( interval < 0 )
		_interval = -interval;
	else	_interval = interval;
	insert_tick(tl,get_xltime() + _interval);
}

void
change_tick(int interval)
{
	if ( target_tick == 0 )
		return;
	target_tick->interval = interval;
}

void
del_tick(void (*func)())
{
TICK_WHEEL * w, ** wp;
TICK_FUNC * tl, ** tlp;
	lock_task(tick_lock);
	for ( wp = &wheel ; *wp ; ) {
		w = *wp;
		for ( tlp = &w->list ; *tlp ; ) {
			tl = *tlp;
			if ( tl->func != func ) {
				tlp = &tl->next;
				continue;
			}
			*tlp = tl->next;
			d_f_ree(tl);
		}
		if ( w->list )
			wp = &w->next;
		else {
			*wp = w->next;
			d_f_ree(w);
		}
	}
	unlock_task(tick_lock,"del_tick");
}

void
del_tick_with_data(void (*func)(),int data)
{
TICK_WHEEL * w, ** wp;
TICK_FUNC * tl, ** tlp;
	lock_task(tick_lock);
	for ( wp = &wheel ; *wp ; ) {
		w = *wp;
		for ( tlp = &w->list ; *tlp ; ) {
			tl = *tlp;
			if ( tl->func != func || tl->data != data ) {
				tlp = &tl->next;
				continue;
			}
			*tlp = tl->next;
			d_f_ree(tl);
		}
		if ( w->list )
			wp = &w->next;
		else {
			*wp = w->next;
			d_f_ree(w);
		}
	}
	unlock_task(tick_lock,"del_tick");
}

void
tick_thread()
{
int time;
TICK_WHEEL * w;
TICK_FUNC * tf1, * tf2;

	for ( ; ; ) {
		sleep_sec(1);
	more:
		time = get_xltime();
		lock_task(tick_lock);
		if ( wheel == 0 )
			goto next;
		if ( wheel->invoke > time )
			goto next;
		w = wheel;
		wheel = w->next;
		unlock_task(tick_lock,"tick_thread");

		for ( tf1 = w->list ; tf1 ; ) {
			tf2 = tf1->next;
			target_tick = tf1;
set_t_msg(1);
set_t_msg2(tf1->func);
			(*tf1->func)(tf1->data);
set_t_msg(0);
			target_tick = 0;
			if ( tf1->interval <= 0 )
				d_f_ree(tf1);
			else {
				insert_tick(tf1,get_xltime() + tf1->interval);
			}
			tf1 = tf2;
		}
		d_f_ree(w);
		goto more;
	next:
		unlock_task(tick_lock,"tick_thread");
	}
}

