/**********************************************************************
 
	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	"utils.h"
#include	"xl.h"
#include	"memory_debug.h"
#include	"task.h"
#include	"gbmp.h"
#include	"lock_level.h"

SEM tick_que_lock;

int regist_time;
TQUE_HEADER tql[TQL_MAX];


void
init_tick_que()
{
	tick_que_lock = new_lock(LL_TICK_QUE);
}

unsigned int
get_time()
{
	return get_xltime();
}

int
tq_cmp(TICK_QUE * t1,TICK_QUE * t2)
{
	if ( t1->invoke_time < t2->invoke_time )
		return -1;
	if ( t1->invoke_time > t2->invoke_time )
		return 1;
	return 0;
}

int
nq_cmp(TICK_QUE * t1,TICK_QUE * t2)
{
int ret;
	if ( t1->filename == 0 && t2->filename == 0 ) {
		if ( t1->type < t2->type )
			return -1;
		if ( t1->type > t2->type )
			return 1;
		return 0;
	}
	if ( t1->filename == 0 )
		return -1;
	if ( t2->filename == 0 )
		return 1;
	ret = l_strcmp(t1->filename,t2->filename);
	if ( ret != 0 )
		return ret;
	if ( t1->type < t2->type )
		return -1;
	if ( t1->type > t2->type )
		return 1;
	return 0;
}


void
free_tick_que(TICK_QUE * tq)
{
TICK_QUE * t;
	for ( ; tq ; ) {
		t = tq->next;
		if ( tq->filename )
			d_f_ree(tq->filename);
		d_f_ree(tq);
		tq = t;
	}
}


void
insert_tick_que(unsigned int delay,L_CHAR * filename,int type,int level)
{
AVT_NODE * a1, * a2;
TICK_QUE * tqp,* tqp2, ** tpp;
L_CHAR * _filename;
int len;
unsigned int now;

	if ( regist_time > delay )
		delay = regist_time;

	if ( filename ) {
		if ( filename[0] == '/' )
			_filename = ll_copy_str(filename);
		else {
			len = l_strlen(filename);
			_filename = d_alloc((len+2)*sizeof(L_CHAR));
			l_strcpy(&_filename[1],filename);
			_filename[0] = '/';
		}
	}
	lock_task(tick_que_lock);
	now = get_time();

	tqp = d_alloc(sizeof(*tqp));
	tqp->invoke_time = now + delay;
	if ( filename )
		tqp->filename = _filename;
	else	tqp->filename = 0;
	tqp->type = type;
	tqp->next = 0;

	a2 = avt_search(tql[level].name_que,tqp,nq_cmp);
	if ( a2 == 0 ) {
		a2 = d_alloc(sizeof(*a2));
		a2->data = tqp;
		avt_insert(&tql[level].name_que,a2,nq_cmp);
	}
	else {
		tqp2 = a2->data;
		if ( tqp2->invoke_time <= tqp->invoke_time ) {
			if ( tqp->filename )
				d_f_ree(tqp->filename);
			d_f_ree(tqp);
			goto end;
		}
		a1 = avt_search(tql[level].tick_que,tqp2,tq_cmp);
		if ( a1 == 0 )
			er_panic("insert_tick_que1");
		if ( a1->data == tqp2 && tqp2->next == 0 ) {
			a1 = avt_delete(&tql[level].tick_que,tqp2,tq_cmp);
			if ( a1 == 0 )
				er_panic("insert_tick_que2");
			d_f_ree(a1);
		}
		else {
			for ( tpp = (TICK_QUE**)&a1->data;
					*tpp && *tpp != tqp2;
					tpp = &(*tpp)->next );
			if ( *tpp == 0 )
				er_panic("insert_tick_que");
			*tpp = tqp2->next;
			tqp2->next = 0;
		}
		free_tick_que(tqp2);
		a2->data = tqp;
	}

	a1 = avt_search(tql[level].tick_que,tqp,tq_cmp);
	if ( a1 == 0 ) {
		a1 = d_alloc(sizeof(*a1));
		a1->data = tqp;
		avt_insert(&tql[level].tick_que,a1,tq_cmp);
	}
	else {
		tqp->next = a1->data;
		a1->data = tqp;
	}
end:

	unlock_task(tick_que_lock,"insert_tick_que");
}

typedef struct tq_work {
	AVT_NODE *		que;
	unsigned int		now;
} TQ_WORK;

int
delete_func(AVT_NODE * a,TQ_WORK * w)
{
TICK_QUE * t;
	t = a->data;
	if ( t->invoke_time == 0 ) {
		w->que = a;
		return 0;
	}
	else if ( t->invoke_time <= w->now ) {
		w->que = a;
		return 1;
	}
	else if ( t->invoke_time > w->now ) {
		return 1;
	}
	return 0;
}

TICK_QUE *
delete_tick_que(int level)
{
TICK_QUE * ret;
AVT_NODE * a;
TQ_WORK w;
	lock_task(tick_que_lock);
	w.que = 0;
	w.now = get_time();
	avt_trace_from_small(tql[level].tick_que,delete_func,&w);
	if ( w.que == 0 ) {
		unlock_task(tick_que_lock,"insert_tick_que");
		return 0;
	}
	ret = w.que->data;
	if ( ret->next == 0 ) {
		a = avt_delete(&tql[level].tick_que,w.que->data,tq_cmp);
		d_f_ree(a);
	}
	else {
		w.que->data = ret->next;
		ret->next = 0;
	}

	a = avt_delete(&tql[level].name_que,ret,nq_cmp);
	if ( a == 0 )
		er_panic("delete_tick_que");
	d_f_ree(a);
	unlock_task(tick_que_lock,"insert_tick_que");
	return ret;
}



int
tq_cnt_func(AVT_NODE * n,TQ_STATISTICS * w)
{
TICK_QUE * tq;
int len;
	tq = n->data;
	if ( w->comesoon == 0xffffffff )
		w->comesoon = tq->invoke_time;
	if ( tq->type < 0 || tq->type >= TQT_MAX ) {
		log_printf(LOG_DEBUG,"invalid tick que type %i",tq->type);
		er_panic("tq_cnt_func");
	}
	w->tqt_cnt[tq->type] ++;
	if ( tq->filename == 0 )
		goto next;
	len = l_strlen(tq->filename);
	if ( len < 4 )
		goto next;
	if ( l_strcmp(&tq->filename[len-4],
			l_string(std_cm,".map")) == 0 )
		w->maps ++;
next:
	return 0;
}

void
tick_que_statistics(TQ_STATISTICS * w)
{
int i;
	lock_task(tick_que_lock);
	w->maps = 0;
	w->comesoon = 0xffffffff;
	for ( i = 0 ; i < TQT_MAX ; i ++ )
		w->tqt_cnt[i] = 0;
	for ( i = 0 ; i < TQL_MAX ; i ++ )
		avt_trace_from_small(tql[i].name_que,tq_cnt_func,w);
	unlock_task(tick_que_lock,"tick_que_statistics");
}


int
tq_comesoon(AVT_NODE * n,TQ_STATISTICS * w)
{
TICK_QUE * tq;
	tq = n->data;
	w->comesoon = tq->invoke_time;
	return 1;
}

unsigned int
get_comesoon()
{
TQ_STATISTICS w;
int i;
unsigned int ret;
	ret = 0xffffffff;
	lock_task(tick_que_lock);
	for ( i = 0 ; i < TQL_MAX ; i ++ ) {
		w.comesoon = 0;
		avt_trace_from_small(tql[i].tick_que,tq_comesoon,&w);
		if ( w.comesoon == 0 )
			continue;
		if ( ret > w.comesoon )
			ret = w.comesoon;
	}
	unlock_task(tick_que_lock,"get_comesoon");
	return ret;
}
