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

SEM text_render_lock;
void tr_sequence_task();
extern TR_L_CHAR_TBL tr_l_char_table[];
extern TR_LANG * default_tr_lang;
TR_LC_TBL_CACHE *	tbl_cache;

TR_LANG *		tr_lang_list;

int tr_rw_lock_count;
int tr_rw_lock_tid;
char * tr_rw_lock_file;
int tr_rw_lock_line;
int tr_rw_lock_time;

TR_SEQUENCE * sq_list;

void xx_tr_rw_lock(char * file,int line);
#define tr_rw_lock()	xx_tr_rw_lock(__FILE__,__LINE__)

void _indicate_tr_lock(int * ind);
void see_lock_tr_sys(int * ind);


void
see_lock_tr_sys(int * ind)
{
	if ( tr_rw_lock_file == 0 )
		return;
	if ( debug_th_timeout_check(tr_rw_lock_time) < 0 )
		return;
	if ( *ind == 0 )
		printf("LOCK INFO ====\n");
	*ind = 1;
	printf("LOCK_TR (SYS) %isec :: %s %i\n",
		get_xltime() - tr_rw_lock_time,
		tr_rw_lock_file,
		tr_rw_lock_line);	
}

void
_indicate_tr_lock(int * ind)
{
	see_lock_tr_sys(ind);
}


void
tr_system_lock()
{
	lock_task(text_render_lock);
}

void
tr_system_unlock()
{
	unlock_task(text_render_lock,"tr_system_unlock");
}



void
_tr_destroy_lang_line(TR_SEQUENCE * sq,TR_LANG_LINE * ll);

void
waitsync_test(TR_SEQUENCE * sq,char * msg)
{
	if ( sq->waitsync.list && ((unsigned int)sq->waitsync.list) < 0x1000 )
		er_panic(msg);
}

TR_LANG *
_tr_search_lang_from_abb(char * abb)
{
TR_LANG * ret;
	for ( ret = tr_lang_list ; ret ; ret = ret->next )
		if ( strcmp(ret->lang,abb) == 0 )
			return ret;
	return 0;
}

int
_tr_insert_lang(TR_LANG * lang)
{
TR_LANG * d;
	for ( d = tr_lang_list ; d ; d = d->next ) {
		if ( strcmp(lang->lang,d->lang) == 0 )
			return -1;
	}
	lang->next = tr_lang_list;
	tr_lang_list = lang;
	return 0;
}

void
init_lang(TR_LANG * lang)
{
	if ( lang->setup_lang == 0 )
		return;
	if ( _tr_search_lang_from_abb(lang->lang) )
		return;

	tr_system_lock();
	(*lang->setup_lang)(lang,TRT_INIT_BIN,0);
	_tr_insert_lang(lang);

	tr_system_unlock();
}

void
init_text_render()
{
TR_L_CHAR_TBL * tbl;
TR_L_CHAR_TBL2 * tbl2;
TR_L_CHAR_TBL2_HEADER * h;
	text_render_lock = new_lock(LL_TR);
	tbl_cache = d_alloc(sizeof(*tbl_cache));
	TR_RING_INIT(tbl_cache);
	tbl_cache->body = 0;

	for ( tbl = &tr_l_char_table[0] ; tbl->mask ; tbl ++ ) {
		if ( tbl->lang ) {
			init_lang(tbl->lang);
			trl_insert_table(tbl->ch,tbl->ch + (~tbl->mask) + 1,
				tbl->lang,0);
		}
		for ( h = tbl->tbl ; h ; h = h->inheritance )
			for ( tbl2 = h->tbl ; ((long)tbl2->from) < 
					((long)tbl2->to) ; 
					tbl2++ ) {
				if ( tbl2->from & tbl->mask )
					continue;
				if ( tbl2->to & tbl->mask )
					continue;
				if (tbl2->lang ) {
					init_lang(tbl2->lang);
					trl_insert_table(
						tbl->ch|tbl2->from,
						tbl->ch|tbl2->to,
						tbl2->lang,0);
				}
			}
	}
	init_lang(default_tr_lang);
}

void
_tr_rw_lock(char * file,int line)
{
int tid;
	tid = get_tid();
	for ( ; tr_rw_lock_count > 0 ||
			(tr_rw_lock_count < 0 && 
			tr_rw_lock_tid != tid) ; ) {
		sleep_task((int)&tr_rw_lock_count,text_render_lock);
		lock_task(text_render_lock);
	}
	tr_rw_lock_time = get_xltime();
	tr_rw_lock_file = file;
	tr_rw_lock_line = line;
	tr_rw_lock_count --;
	tr_rw_lock_tid = tid;
}

void
_tr_rw_unlock()
{
	if ( get_tid() != tr_rw_lock_tid )
		er_panic("_tr_rw_unlock");
	tr_rw_lock_count ++;
	if ( tr_rw_lock_count == 0 ) {
		tr_rw_lock_file = 0;
		tr_rw_lock_line = 0;
		tr_rw_lock_tid = 0;
		wakeup_task((int)&tr_rw_lock_count);
	}
}

void
xx_tr_rw_lock(char * file,int line)
{
	lock_task(text_render_lock);
	_tr_rw_lock(file,line);
	unlock_task(text_render_lock,"tr_rw_lock");
}

void
tr_rw_unlock()
{
	lock_task(text_render_lock);
	_tr_rw_unlock();
	unlock_task(text_render_lock,"tr_rw_lock");
}

void
gc_lock_text_render()
{
	lock_task(text_render_lock);
	_tr_rw_lock(__FILE__,__LINE__);
}

void
gc_unlock_text_render()
{
	_tr_rw_unlock();
	unlock_task(text_render_lock,"gc_unlock_text_render");
}

void
gc_tr_obj(TR_SEQUENCE * sq,void (*gc_obj)(void*))
{
TR_CHAR_BUF * cb;
int len,i;
TR_CHAR_ELEMENT * el;
	for ( cb = sq->char_buf_ring->next;
			cb != sq->char_buf_ring ;
			cb = cb->next ) {
		if ( cb->element_buf == 0 )
			continue;
		len = cb->buf_len;
		el = cb->element_buf;
		for ( i = 0 ; i < len ; i ++ , el ++ ) {
			if ( el->d.type >= 0 )
				continue;
			(*gc_obj)(el->d.obj.obj);
		}
	}
}

void
gc_text_render()
{
TR_SEQUENCE * sq;
	for ( sq = sq_list ; sq ; sq = sq->next ) {
		if ( sq->box_op->obj_op && sq->box_op->obj_op->gc_obj )
			gc_tr_obj(sq,sq->box_op->obj_op->gc_obj);
		if ( sq->box_op->gc_tr )
			(*sq->box_op->gc_tr)(sq);
	}
}

int
_tr_lock(TR_SEQUENCE * sq,char * file,int line)
{
	for ( ; tr_rw_lock_count < 0 ; ) {
		sleep_task((int)&tr_rw_lock_count,text_render_lock);
		lock_task(text_render_lock);
	}
	tr_rw_lock_count ++;
	tr_rw_lock_time = get_xltime();
	tr_rw_lock_file = file;
	tr_rw_lock_line = line;
	for ( ; sq->lock ; ) {
		sleep_task((int)sq,text_render_lock);
		lock_task(text_render_lock);
	}
	sq->lock = get_tid();
	sq->lock_file = file;
	sq->lock_line = line;
	return 0;
}

void
xx_tr_lock(TR_SEQUENCE * sq,char * file,int line)
{
	lock_task(text_render_lock);
	_tr_lock(sq,file,line);
	unlock_task(text_render_lock,"tr_lock");
}

void
_tr_unlock(TR_SEQUENCE * sq)
{
	if ( sq->lock != get_tid() )
		er_panic("tr_unlock");
	sq->lock = 0;
	tr_rw_lock_count --;
	tr_rw_lock_file = 0;
	tr_rw_lock_line = 0;
	sq->lock_file = 0;
	sq->lock_line = 0;
	wakeup_task((int)sq);
	wakeup_task((int)&tr_rw_lock_count);
	if ( tr_rw_lock_count == 0 )
		wakeup_task((int)&tr_rw_lock_count);
}

void
tr_unlock(TR_SEQUENCE * sq)
{
	lock_task(text_render_lock);
	_tr_unlock(sq);
	unlock_task(text_render_lock,"tr_lock");
}


void
_tr_sleep_sq_position(TR_SEQUENCE * sq)
{
	lock_task(text_render_lock);
	_tr_unlock(sq);
	sleep_task((int)sq,text_render_lock);
}

void
_tr_wakeup_sq_position(TR_SEQUENCE * sq)
{
	wakeup_task((int)sq);
}

void
_xx_tr_check_error(TR_ERROR er,char * file,int line)
{
char * _code,* _subcode;
	_subcode = "NONE";
	switch ( er.code ) {
	case TRE_OK:
		_code = "TRE_OK";
		switch ( er.subcode ) {
		case TRE_OK_NONE:
			_subcode = "TRE_OK_NONE";
			break;
		default:
			_subcode = "undef";
		}
		break;
	case TRE_INSERT_CHAR:
		_code = "TRE_INSERT_CHAR";
		break;
	case TRE_INSERT_LINE:
		_code = "TRE_INSERT_LINE";
		break;
	default:
		_code = "undef";
	}
//	ss_printf("TRE(%s,%i)==> %s:%s\n",
//		file,line,
//		_code,_subcode);
}


TR_LANG *
_tr_search_lang(L_CHAR ch,TRL_CONTEXT * ctx)
{
TR_LANG * ret;
	ret = trl_get_lang(ctx,ch);
	if ( ret == 0 )
		return default_tr_lang;
	return ret;
}

/*
TR_LANG *
_tr_search_lang(L_CHAR ch)
{
TR_L_CHAR_TBL * tbl;
TR_L_CHAR_TBL2 * tbl2;
TR_L_CHAR_TBL2_HEADER * tbl_h;
int i;
TR_LC_TBL_CACHE * cache;
TR_LC_TBL2_CACHE * c2, * c_head;
	for ( cache = tbl_cache->next ; cache != tbl_cache ;
			cache = cache->next ) {
		tbl = cache->body;
		if ( (ch & tbl->mask) != (tbl->ch & tbl->mask) ) 
			continue;
		TR_RING_DELETE(cache);
		TR_RING_INSERT(tbl_cache,cache);
		if ( tbl->lang ) {
			return tbl->lang;
		}
		goto next1;
	}
	for ( tbl = &tr_l_char_table[0] ; tbl->mask != 0 ; tbl ++ ) {
		if ( (ch & tbl->mask) != (tbl->ch & tbl->mask) ) 
			continue;
		if ( tbl->lang ) {
			return tbl->lang;
		}
		cache = d_alloc(sizeof(*cache));
		cache->body = tbl;
		cache->cache = d_alloc(sizeof(*cache->cache));
		cache->cache->body = 0;
		TR_RING_INIT(cache->cache);
		TR_RING_INSERT(tbl_cache,cache);
		break;
	}
	goto next2;
next1:
	ch = ch & (~tbl->mask);
	c_head = cache->cache;
	for ( c2 = c_head->next ; c2 != c_head ; c2 = c2->next ) {
		tbl2 = c2->body;
		if ( ch < ((long)tbl2->from) ||
				((long)tbl2->to) <= ch )
			continue;
		TR_RING_DELETE(c2);
		TR_RING_INSERT(c_head,c2);
		return tbl2->lang;
	}
	for ( tbl_h = tbl->tbl ; tbl_h ; tbl_h = tbl_h->inheritance ) {
		for ( tbl2 = tbl_h->tbl ; 
				tbl2->from < tbl2->to ;
				tbl2 ++ ) {
			if ( ch < ((long)tbl2->from) ||
					((long)tbl2->to) <= ch )
				continue;
			c2 = d_alloc(sizeof(*c2));
			c2->body = tbl2;
			TR_RING_INSERT(c_head,c2);
			return tbl2->lang;
		}
	}
next2:
	return default_tr_lang;
}
*/



void
_tr_push_ptr_stack(TR_PTR_STACK ** st,TR_PTR p)
{
TR_PTR_STACK * n;
	n = d_alloc(sizeof(*n));
	n->p = p;
	n->next = *st;
	*st = n;
}

TR_PTR
_tr_pop_ptr_stack(TR_PTR_STACK ** st)
{
TR_PTR_STACK * n;
TR_PTR ret;
	n = *st;
	if ( n == 0 ) {
		ret.buf = 0;
		ret.ptr = 0;
		return ret;
	}
	*st = n->next;
	ret = n->p;
	d_f_ree(n);
	return ret;
}

void
_tr_free_ptr_stack(TR_PTR_STACK ** st)
{
	for ( ; _tr_pop_ptr_stack(st).buf ; );
}

TR_PTR
tr_ptr_prev(TR_PTR p)
{
TR_PTR ret;
	ret.buf = 0;
	ret.ptr = 0;
	if ( p.buf == 0 )
		return ret;
	if ( p.ptr > 0 ) {
		p.ptr --;
		return p;
	}
	if ( p.buf->prev->seq == 0 )
		return ret;
	p.buf = p.buf->prev;
	p.ptr = p.buf->buf_len-1;
	return p;
}

TR_PTR
tr_ptr_next(TR_PTR p)
{
TR_PTR ret;
	ret.buf = 0;
	ret.ptr = 0;
	if ( p.buf == 0 )
		return ret;
	p.ptr ++;
	if ( p.ptr >= p.buf->buf_len ) {
		if ( p.buf->next->seq == 0 ) {
			if ( p.ptr == p.buf->buf_len )
				return p;
			else	return ret;
		}
		p.buf = p.buf->next;
		p.ptr = 0;
		return p;
	}
	return p;
}

int
_tr_ptr_regulation(TR_PTR * p)
{
	if ( p->buf == 0 )
		return 2;
	for ( ; ; ) {
		if ( p->ptr < 0 ) {
			p->buf = p->buf->prev;
			if ( p->buf->seq == 0 ) {
				p->buf = 0;
				p->ptr = 0;
				return -1;
			}
			p->ptr += p->buf->buf_len;
			if ( p->buf->seq == 0 ) {
				er_panic("regulation");
			}
		}
		else if ( p->buf->buf_len <= p->ptr ) {
			if ( p->buf->next->seq == 0 &&
					p->buf->buf_len == p->ptr ) {
				return 0;
			}
			p->buf = p->buf->next;
			if ( p->buf->seq == 0 ) {
				p->buf = 0;
				p->ptr = 0;
				return 1;
			}
			p->ptr -= p->buf->buf_len;
		}
		else 	return 0;
	}
}

void
tr_ptr_regulation(TR_SEQUENCE * sq,TR_PTR * p)
{
	tr_lock(sq);
	_tr_ptr_regulation(p);
	tr_unlock(sq);
}

TR_CHAR_BUF *
_tr_new_char_buf(TR_CHAR_BUF * cb)
{
TR_CHAR_BUF * new_cb;
	new_cb = d_alloc(sizeof(*new_cb));
	memset(new_cb,0,sizeof(*new_cb));
	if ( cb->next->seq == 0 ) {
		new_cb->seq = cb->seq + 10;
		TR_RING_INSERT(cb,new_cb);
	}
	else {
		if ( cb->seq + 1 == cb->next->seq )
			new_cb->seq = cb->seq+1;
		else 	new_cb->seq = (cb->seq + cb->next->seq)/2;
		TR_RING_INSERT(cb,new_cb);
		for ( cb = new_cb->next ; cb->seq ; cb = cb->next )
			if ( cb->prev->seq == cb->seq ) {
				cb->seq ++;
			}
			else	break;
	}
	return new_cb;
}

void
_tr_set_char_buf_lang_line(TR_LANG_LINE * ll)
{
TR_CHAR_BUF * buf;
TR_PTR bptr;
int cmp;
TR_LANG_LINE * nl;

	buf = ll->start.buf;
	for ( ; buf->seq ; buf = buf->next ) {
		bptr.buf = buf;
		bptr.ptr = 0;
		if ( tr_ptr_cmp(ll->end,bptr) < 0 )
			break;
		if ( buf->lang_line == 0 ) {
			buf->lang_line = ll;
			continue;
		}
		cmp = tr_ptr_cmp(ll->start,buf->lang_line->start);
		if ( cmp < 0 ) {
			buf->lang_line = ll;
			continue;
		}
		if ( cmp == 0 ) {
			nl = _tr_next_lang_line(ll);
			for ( ; nl ; nl = _tr_next_lang_line(nl) ) {
				if ( nl == buf->lang_line )
					break;
			}
			if ( nl == 0 )
				break;
			buf->lang_line = ll;
			continue;
		}
		break;
	}
}

int
tr_ptr_cmp(TR_PTR a,TR_PTR b)
{
	if ( a.buf == 0 && b.buf == 0 )
		return 0;
	if ( a.buf == 0 )
		return -1;
	if ( b.buf == 0 )
		return 1;
	if ( a.buf->seq < b.buf->seq )
		return -1;
	if ( a.buf->seq > b.buf->seq )
		return 1;
	if ( a.ptr < b.ptr )
		return -1;
	if ( a.ptr > b.ptr )
		return 1;
	return 0;
}

TR_LANG_LINE *
_tr_next_lang_line(TR_LANG_LINE * ll)
{
TR_LANG_AREA * a;
	if ( ll->area_next )
		return ll->area_next;
	a = ll->lang_area->next;
	for ( ; a->op ; a = a->next )
		if ( a->line_list )
			return a->line_list; 
	return 0;
}

TR_LANG_LINE *
_tr_prev_lang_line(TR_LANG_LINE * ll)
{
TR_LANG_AREA * a;
TR_LANG_LINE * ret, * next;
	a = ll->lang_area;
	if ( a->line_list != ll ) {
		for ( ret = a->line_list ; ret ; ) {
			next = ret->area_next;
			if ( next == ll )
				return ret;
			ret = next;
		}
		er_panic("_tr_prev_lang_line");
		return 0;
	}
	else {
		a = a->prev;
		for ( ; a->op ; ) {
			if ( a->line_list == 0 )
				continue;
			for ( ret = a->line_list ;
				ret && ret->area_next ;
				ret = ret->area_next );
			return ret;
		}
		return 0;
	}
}


TR_ATTR * tr_copy_default(TR_ATTR_TABLE *,TR_ATTR * );
void tr_free_default(TR_ATTR * );
int tr_cmp_short(TR_ATTR * ,TR_ATTR *);
int tr_cmp_int(TR_ATTR * ,TR_ATTR *);

TR_ATTR_TABLE tr_attr_table[] = {
	{0,0},
	{	sizeof(TRA_LINE_ATTR),	/* TRAT_LINE_ATTR */
		tr_copy_default,
		tr_free_default,
		0},
	{	sizeof(TRA_ORNAMENT),	/* TRAT_ORNAMENT */
		tr_copy_default,
		tr_free_default,
		tr_cmp_short},
	{	sizeof(TRA_REFERENECE),	/* TRAT_REFERENCE */
		tr_copy_default,
		tr_free_default,
		tr_cmp_short},
	{	sizeof(TRA_COLOR),	/* TRAT_COLOR */
		tr_copy_default,
		tr_free_default,
		tr_cmp_int},
};

int
tr_cmp_short(TR_ATTR * a,TR_ATTR * b)
{
	if ( a->sd.data < b->sd.data )
		return -1;
	if ( a->sd.data > b->sd.data )
		return 1;
	return 0;
}

int
tr_cmp_int(TR_ATTR * a,TR_ATTR * b)
{
	if ( a->id.data < b->id.data )
		return -1;
	if ( a->id.data > b->id.data )
		return 1;
	return 0;
}

void
tr_free_default(TR_ATTR * a)
{
	d_f_ree(a);
}

TR_ATTR *
tr_copy_default(TR_ATTR_TABLE * tbl,TR_ATTR * a)
{
TR_ATTR * ret;
	ret = d_alloc(tbl->size);
	memcpy(ret,a,tbl->size);
	ret->h.next = 0;
	return ret;
}

TR_ATTR *
_tr_copy_attr(TR_ATTR * a)
{
TR_ATTR * ret, ** rp;
	ret = 0;
	rp = &ret;
	for ( ; a ; a = a->h.next , rp = &(*rp)->h.next )
		*rp = (*tr_attr_table[a->h.type].copy)
				(&tr_attr_table[a->h.type],a);
	return ret;
}

void
_tr_free_attr(TR_ATTR * a)
{
TR_ATTR * ap;
	for ( ; a ; ) {
		ap = a->h.next;
		(*tr_attr_table[a->h.type].free)(a);
		a = ap;
	}
}

TR_ATTR *
_tr_get_attr(TR_ATTR * a,int type)
{
	for ( ; a && a->h.type < type ; a = a->h.next );
	if ( a == 0 )
		return 0;
	if ( a->h.type == type )
		return a;
	return 0;
}


int
_tr_get_attr_short_data(TR_ATTR * a,int type)
{
TR_ATTR * ret;
	ret = _tr_get_attr(a,type);
	if ( ret == 0 )
		return 0;
	return ret->sd.data;
}

void
_tr_set_attr_short_data(TR_ATTR ** ap,int type,short data)
{
TR_ATTR * ret;
	ret = _tr_get_attr(*ap,type);
	if ( ret ) {
		ret->sd.data = data;
		return;
	}
	ret = d_alloc(tr_attr_table[type].size);
	ret->h.type = type;
	ret->sd.data = data;
	for ( ; *ap ; ) {
		if ( (*ap)->h.type > type )
			break;
	}
	ret->h.next = *ap;
	*ap = ret;
}

int
_tr_get_attr_int_data(TR_ATTR * a,int type)
{
TR_ATTR * ret;
	ret = _tr_get_attr(a,type);
	if ( ret == 0 )
		return 0;
	return ret->id.data;
}


void
_tr_acc_line_attr(TR_LINE_ATTR * a,TR_ATTR * attr)
{
TR_ATTR * ret;
int flags;
	ret = _tr_get_attr(attr,TRAT_LINE_ATTR);
	if ( ret == 0 )
		return;
	flags = ret->la.attr.flags;
	if ( flags & TRLF_ALIGN ) {
		a->flags |= TRLF_ALIGN;
		a->align = ret->la.attr.align;
		flags &= ~TRLF_ALIGN;
	}
	if ( flags & TRLF_DEFAULT_DIR ) {
		a->flags |= TRLF_DEFAULT_DIR;
		a->default_dir = ret->la.attr.default_dir;
		flags &= ~TRLF_DEFAULT_DIR;
	}
	if ( flags & TRLF_PITCH ) {
		a->flags |= TRLF_PITCH;
		a->pitch = ret->la.attr.pitch;
		flags &= ~TRLF_PITCH;
	}
	if ( flags )
		er_panic("_tr_acc_line_attr");
}

void
_tr_set_line_attr(TR_LINE_ATTR * la,TR_ATTR ** ap)
{
TR_ATTR * ret;
char flags;
	flags = la->flags;
	ret = _tr_get_attr(*ap,TRAT_LINE_ATTR);
	if ( ret ) {
		if ( flags & TRLF_ALIGN ) {
			ret->la.attr.align = la->align;
			ret->la.attr.flags |= TRLF_ALIGN;
			flags &= ~TRLF_ALIGN;
		}
		if ( flags & TRLF_DEFAULT_DIR ) {
			ret->la.attr.default_dir = la->default_dir;
			ret->la.attr.flags |= TRLF_DEFAULT_DIR;
			flags &= ~TRLF_DEFAULT_DIR;
		}
		if ( flags & TRLF_PITCH ) {
			ret->la.attr.pitch = la->pitch;
			ret->la.attr.flags |= TRLF_PITCH;
			flags &= ~TRLF_PITCH;
		}
		if ( flags )
			er_panic("_tr_set_line_attr");
		return;
	}
	ret = d_alloc(tr_attr_table[TRAT_LINE_ATTR].size);
	ret->h.type = TRAT_LINE_ATTR;
	ret->la.attr = *la;
	for ( ; *ap ; ap = &(*ap)->h.next ) {
		if ( (*ap)->h.type > TRAT_LINE_ATTR )
			break;
	}
	ret->h.next = *ap;
	*ap = ret;
}


void
_tr_set_attr_int_data(TR_ATTR ** ap,int type,int data)
{
TR_ATTR * ret;
	ret = _tr_get_attr(*ap,type);
	if ( ret ) {
		ret->id.data = data;
		return;
	}
	ret = d_alloc(tr_attr_table[type].size);
	ret->h.type = type;
	ret->id.data = data;
	for ( ; *ap ; ap = &(*ap)->h.next ) {
		if ( (*ap)->h.type > type )
			break;
	}
	ret->h.next = *ap;
	*ap = ret;
}

int
_tr_cmp_attr(TR_ATTR * a,TR_ATTR * b)
{
int (*cmp)(TR_ATTR *,TR_ATTR *);
int ret;
	for ( ; a && b ; 
		a = a->h.next,
		b = b->h.next
			) {
		if ( a->h.type < b->h.type )
			return -1;
		if ( a->h.type > b->h.type )
			return 1;
		if ( a->h.type < 1 || a->h.type >= TRAT_MAX )
			er_panic("_tr_cmp_attr");
		cmp = tr_attr_table[a->h.type].cmp;
		if ( cmp == 0 )
			continue;
		ret = (*cmp)(a,b);
		if ( ret )
			return ret;
	}
	if ( a )
		return 1;
	if ( b )
		return -1;
	return 0;
}

int
tr_ref_cmp(TR_REFERENCE * r1,TR_REFERENCE * r2)
{
	if ( r1->id < r2->id )
		return -1;
	if ( r1->id > r2->id )
		return 1;
	return 0;
}

void
_tr_set_attr_reference(TR_SEQUENCE *sq,TR_ATTR ** ap,L_CHAR * ref)
{
TR_REFERENCE * d;
AVT_NODE * a;
	d = d_alloc(sizeof(*d));
	d->data = ll_copy_str(ref);
	a = d_alloc(sizeof(*a));
	a->data = d;
	for ( ; ; ) {
		sq->ref_id ++;
		if ( sq->ref_id <= 0 )
			sq->ref_id = 1;
		d->id = sq->ref_id;
		if ( avt_insert(&sq->ref,a,tr_ref_cmp) == a )
			break;
	}
	_tr_set_attr_short_data(ap,TRAT_REFERENCE,d->id);
}

TR_SEQUENCE *
tr_new_sequence(TR_BOX_OP * op,int pri,void * msg)
{
TR_SEQUENCE * ret;
TR_BOX * b_head;
TR_CHAR_BUF * cb_head;
TR_LANG_AREA * a;
	ret = d_alloc(sizeof(*ret));
	memset(ret,0,sizeof(*ret));
	ret->box_op = op;

	b_head = d_alloc(sizeof(*b_head));
	memset(b_head,0,sizeof(*b_head));
	b_head->sequence = ret;
	TR_RING_INIT(b_head)
	ret->box_ring = b_head;

	cb_head = d_alloc(sizeof(*cb_head));
	memset(cb_head,0,sizeof(*cb_head));
	ret->char_buf_ring = cb_head;
	TR_RING_INIT(cb_head)

	a = d_alloc(sizeof(*a));
	memset(a,0,sizeof(*a));
	ret->lang_area_ring = a;
	ret->pri = pri;
	TR_RING_INIT(a);


	if ( (*ret->box_op->new_sequence)(ret,msg) < 0 ) {
		er_panic("tr_new_sequence(1)");
	}

	lock_task(text_render_lock);
	ret->next = sq_list;
	sq_list = ret;
	unlock_task(text_render_lock,"tr_new_sequence");

	return ret;
}


void
_wakeup_sequence(TR_SEQUENCE * sq)
{

if ( sq->waitsync.list && ((unsigned int )sq->waitsync.list) < 0x1000 )
er_panic("4");
	wakeup_task((int)sq);
	if ( sq->thread == 0 ) {
		sq->thread = 1;
		create_task(tr_sequence_task,(int)sq,sq->pri);
	}
}



void
_insert_unmapping_char(TR_SEQUENCE * sq,TR_PTR ptr,int ok_subcode)
{
TR_UNMAPPING_CHAR ** ump,*um;
int cmp;

	if ( ptr.buf == 0 )
		er_panic("_insert_unmapping_char");
	for ( ump = &sq->unmapping_char ; *ump ; ump = &(*ump)->next ) {
		cmp = tr_ptr_cmp((*ump)->p,ptr);
		if ( cmp < 0 )
			continue;
		if ( cmp > 0 )
			break;
		return;
	}
	um = d_alloc(sizeof(*um));
	um->p = ptr;
	um->ok_subcode = ok_subcode;
	um->next = *ump;
	*ump = um;
	_wakeup_sequence(sq);
}

TR_UNMAPPING_CHAR
_get_unmapping_char(TR_SEQUENCE * sq)
{
TR_UNMAPPING_CHAR ret,* um;
	if ( sq->unmapping_char == 0 ) {
		ret.p.buf = 0;
		ret.p.ptr = 0;
	}
	else {
		um = sq->unmapping_char;
		sq->unmapping_char = um->next;
		ret = *um;
		d_f_ree(um);
		wakeup_task((int)sq);
	}
	return ret;
}

void
_tr_disable_unmapping_char(TR_SEQUENCE * sq,TR_PTR p)
{
TR_UNMAPPING_CHAR ** ump,* um;
	for ( ump = &sq->unmapping_char ; *ump ; ) {
		um = *ump;
		if ( p.buf == 0 || tr_ptr_cmp(um->p,p) < 0 ) {
			*ump = um->next;
			d_f_ree(um);
			wakeup_task((int)sq);
		}
		else {
			ump = &um->next;
		}
	}
}




void
_dirty_box(TR_SEQUENCE * sq,TR_BOX* b,int flags)
{
TR_BOX** bp;
	if ( b->dirty_flags == 0 ) {
		for ( bp = &sq->dirty_box ; *bp ;
				bp = &(*bp)->dirty_next ) {
			if ( (*bp)->seq > b->seq )
				break;
			if ( (*bp)->seq < b->seq )
				continue;
			er_panic("panic");
		}
		b->dirty_next = *bp;
		*bp = b;
	}
	b->dirty_flags |= flags;
	_wakeup_sequence(sq);
}

TR_BOX *
_get_dirty_box(TR_SEQUENCE * sq)
{
TR_BOX * ret;
	ret = sq->dirty_box;
	if ( ret == 0 )
		return 0;
	sq->dirty_box = ret->dirty_next;
	return ret;
}


int
_get_dirty(TR_SEQUENCE * sq,TR_DIRTY * d)
{
	memset(d,0,sizeof(*d));
	d->box = _get_dirty_box(sq);
	if ( d->box )
		return 0;
	d->umc = _get_unmapping_char(sq);
	if ( d->umc.p.buf )
		return 0;
	return -1;
}

int
get_dirty(TR_SEQUENCE * sq,TR_DIRTY * d)
{
int ret;
	tr_lock(sq);
	ret = _get_dirty(sq,d);
	tr_unlock(sq);
	return ret;
}

TR_BOX *
_tr_new_box(TR_SEQUENCE * sq,TR_BOX * b,TR_BOX_ATTR * attr)
{
TR_BOX * ret;
TR_BOX * p;
	ret = d_alloc(sizeof(*ret));
	memset(ret,0,sizeof(*ret));
	ret->sequence = sq;
	ret->attr = *attr;
	if ( b == 0 ) {
		TR_RING_INSERT(sq->box_ring->prev,ret)
	}
	else {
		TR_RING_INSERT(b,ret)
	}
	if ( ret->next->seq == 0 ) {
		ret->seq = ret->prev->seq + 10;
	}
	else if ( ret->prev->seq + 1 < ret->next->seq ) {
		ret->seq = (ret->prev->seq + ret->next->seq)/2;
	}
	else {
		ret->seq = ret->next->seq;
		for ( p = ret->next ; p->seq ; p = p->next ) {
			if ( p->prev->seq < p->seq )
				break;
			p->seq ++;
		}
	}
	_dirty_box(sq,ret,TRF_DIRTY_LINE_PIXELS|
			TRF_DIRTY_DOC_DIR_PIXELS);
	if ( sq->box_op->new_box )
		(*sq->box_op->new_box)(sq,ret);
	return ret;
/* err1: */
	d_f_ree(ret);
	return 0;
}


TR_BOX *
tr_new_box(TR_SEQUENCE * sq,TR_BOX * b,TR_BOX_ATTR * attr)
{
TR_BOX * ret;
	tr_lock(sq);
	ret = _tr_new_box(sq,b,attr);
	tr_unlock(sq);
	return ret;
}

void
_tr_insert_lang_line_mapping(TR_PTR ptr,int len)
{
TR_LANG_LINE * ll;

	for ( ll = ptr.buf->lang_line ; ll ; ) {
		if ( ll->start.buf == 0 )
			continue;
		if ( tr_ptr_cmp(ll->start,ptr) >= 0 )
			ll->start.ptr += len;
		if ( tr_ptr_cmp(ll->end,ptr) > 0 )
			ll->end.ptr += len;
		ll = _tr_next_lang_line(ll);
	}
}

void
_tr_insert_lang_line_mapping_new_cb(TR_PTR ptr,int len)
{
TR_LANG_LINE * ll;
TR_CHAR_BUF * prev;
	prev = ptr.buf->prev;
	for ( ; ; prev = prev->prev ) {
		if ( prev->seq == 0 )
			return;
		if ( prev->lang_line )
			break;
	}
	for ( ll = prev->lang_line ; ; ) {
		if ( ll->start.buf == 0 )
			goto next;
		if ( tr_ptr_cmp(ll->end,ptr) < 0 )
			goto next;
		if ( tr_ptr_cmp(ll->start,ptr) > 0 )
			break;
		ptr.buf->lang_line = ll;
		break;
	next:
		ll = _tr_next_lang_line(ll);
	}
}


TR_PTR
_tr_insert_string(
	TR_SEQUENCE * sq,
	TR_PTR p_ptr,
	int len,
	LC_WRITING_STYLE * ws,
	TR_ATTR	* attr,
	void *obj,
	int size,
	L_CHAR * str)
{
TR_CHAR_BUF * new_cb;
TR_PTR ret,first;
int new_len;
int i;
L_CHAR * p_str;
TR_CHAR_ELEMENT * el;
	ret.buf = 0;
	ret.ptr = 0;
	if ( ws == 0 && len != 1 ) {
		return ret;
	}
	_tr_ptr_regulation(&p_ptr);
	if ( p_ptr.buf == 0 ) {
		new_cb = _tr_new_char_buf(sq->char_buf_ring->prev);
		new_cb->buf_len = len;
		new_cb->element_buf = d_alloc(sizeof(TR_CHAR_ELEMENT)*len);
		p_ptr.buf = new_cb;
		p_ptr.ptr = 0;
		_tr_insert_lang_line_mapping_new_cb(p_ptr,len);
	}
	else if ( p_ptr.ptr == 0 ) {
		new_cb = _tr_new_char_buf(p_ptr.buf->prev);
		new_cb->buf_len = len;
		new_cb->element_buf = d_alloc(sizeof(TR_CHAR_ELEMENT)*len);
		p_ptr.buf = new_cb;
		first = p_ptr;
		_tr_insert_lang_line_mapping_new_cb(p_ptr,len);
	}
	else {
		new_len = p_ptr.buf->buf_len + len;
		p_ptr.buf->element_buf = d_re_alloc(p_ptr.buf->element_buf,
				sizeof(TR_CHAR_ELEMENT)*new_len);
		memmove(	&p_ptr.buf->element_buf[p_ptr.ptr+len],
				&p_ptr.buf->element_buf[p_ptr.ptr],
				sizeof(TR_CHAR_ELEMENT)*
					(p_ptr.buf->buf_len - p_ptr.ptr));
		p_ptr.buf->buf_len += len;
		_tr_insert_lang_line_mapping(p_ptr,len);
	}
	if ( ws ) {
		p_str = str;
		for ( i = p_ptr.ptr ; i < p_ptr.ptr + len ; i ++ ) {
			el = &p_ptr.buf->element_buf[i];
			el->d.ws.ws = ws;
			el->d.ws.size = size;
			el->attr = _tr_copy_attr(attr);
			el->ch = *p_str ++;
		}
		first = p_ptr;
		p_ptr.ptr += len;
	}
	else {
		if ( sq->box_op->obj_op == 0 )
			er_panic("insert_string");
		for ( i = p_ptr.ptr ; i < p_ptr.ptr+len ; i ++ ) {
			el = &p_ptr.buf->element_buf[i];
			el->d.obj.type = -1;
			el->d.obj.obj = (sq->box_op->obj_op->copy_obj)(obj);
			el->attr = _tr_copy_attr(attr);
			el->ch = LCC_ERROR;
		}
		first = p_ptr;
		p_ptr.ptr += len;
	}
	_insert_unmapping_char(sq,first,TRE_OK_NONE);
	return p_ptr;
}

TR_PTR
tr_insert_string(
	TR_SEQUENCE * sq,
	TR_PTR ptr,
	int len,
	LC_WRITING_STYLE * ws,
	TR_ATTR * attr,
	void *		obj,
	int size,
	L_CHAR * str)
{
TR_PTR ret;
	tr_lock(sq);
waitsync_test(sq,"7");
	ret = _tr_insert_string(sq,ptr,len,ws,attr,obj,size,str);
waitsync_test(sq,"8");
	tr_unlock(sq);
	return ret;
}


void
_tr_delete_lang_line_mapping1(TR_SEQUENCE * sq,TR_PTR p)
{
TR_LANG_LINE * ll,*ll2;
	for ( ll = p.buf->lang_line ; ll ; ) {
		ll2 = _tr_next_lang_line(ll);
		if ( ll->start.buf->seq > p.buf->seq )
			break;
		if ( tr_ptr_cmp(ll->start,p) >= 0 ) {
			ll->start.buf = 0;
			ll->start.ptr = 0;
			ll->end.buf = 0;
			ll->end.ptr = 0;
			_tr_destroy_lang_line(sq,ll);
		}
		else if ( tr_ptr_cmp(ll->end,p) > 0 ) {
			ll->end = p;
		}
		ll = ll2;
	}
}

void
_tr_delete_lang_line_mapping2(TR_SEQUENCE * sq,TR_PTR p1,TR_PTR p2,int len)
{
TR_LANG_LINE * ll,*ll2;
	for ( ll = p1.buf->lang_line ; ll ; ) {
		ll2 = _tr_next_lang_line(ll);
		if ( ll->start.buf->seq > p1.buf->seq )
			break;
		if ( ll->end.buf->seq > p1.buf->seq )
			goto next1;
		if ( tr_ptr_cmp(ll->end,p1) <= 0 )
			goto next2;
		if ( tr_ptr_cmp(ll->end,p2) <= 0 )
			ll->end = p1;
		else	ll->end.ptr -= len;
	next1:
		if ( tr_ptr_cmp(ll->start,p1) <= 0 )
			goto next2;
		if ( tr_ptr_cmp(ll->start,p2) <= 0 )
			ll->start = p1;
		else	ll->start.ptr -= len;
		if ( tr_ptr_cmp(ll->start,ll->end) == 0 )
			_tr_destroy_lang_line(sq,ll);
	next2:
		ll = ll2;
	}
}

void
_tr_cut_char_buf1(TR_SEQUENCE * sq,TR_PTR ptr,int len)
{
int from,to;
int new_len;
int size;
TR_PTR last;
int i;
	from = ptr.ptr + len;
	to = ptr.ptr;
	size = ptr.buf->buf_len - from;
	new_len = ptr.buf->buf_len - len;

	for ( i = to ; i < from ; i ++ ) {
		_tr_free_attr(ptr.buf->element_buf[i].attr);
		if ( ptr.buf->element_buf[i].d.type >= 0 )
			continue;
		(*sq->box_op->obj_op->free_obj)(ptr.buf->element_buf[i].d.obj.obj);
	}
	memmove(&ptr.buf->element_buf[to],
		&ptr.buf->element_buf[from],
		sizeof(TR_CHAR_ELEMENT)*size);
	ptr.buf->element_buf = d_re_alloc(
			ptr.buf->element_buf,
			sizeof(TR_CHAR_ELEMENT)*new_len);

	last = ptr;
	last.ptr += len;
	if ( _tr_ptr_regulation(&last) != 0 )
		_tr_delete_lang_line_mapping1(sq,ptr);
	else	_tr_delete_lang_line_mapping2(sq,ptr,last,len);

	ptr.buf->buf_len = new_len;
}

void
_tr_cut_char_buf2(TR_SEQUENCE * sq,TR_PTR p)
{
int i;
int buf_len;
int new_len;
TR_PTR last;
	buf_len = p.buf->buf_len;
	new_len = p.ptr;
	for ( i = p.ptr ; i < buf_len ; i ++ ) {
		_tr_free_attr(p.buf->element_buf[i].attr);
		if ( p.buf->element_buf[i].d.type >= 0 )
			continue;
		(*sq->box_op->obj_op->free_obj)(p.buf->element_buf[i].d.obj.obj);
	}
	p.buf->element_buf = d_re_alloc(
			p.buf->element_buf,
			sizeof(TR_CHAR_ELEMENT)*new_len);

	last.buf = p.buf->next;
	last.ptr = 0;
	if ( last.buf->seq == 0 )
		_tr_delete_lang_line_mapping1(sq,p);
	else	_tr_delete_lang_line_mapping2(sq,p,last,p.buf->buf_len - new_len);

	p.buf->buf_len = new_len;
}

void
_tr_destroy_char_buf(TR_SEQUENCE * sq,TR_CHAR_BUF * cb)
{
TR_LANG_LINE * ll, * ll2;
int i;
	ll = cb->lang_line;
	for ( ; ll ; ) {
		ll2 = _tr_next_lang_line(ll);
		if ( ll->start.buf->seq > cb->seq )
			break;
		if ( ll->start.buf == cb ) {
			ll->start.buf = cb->next;
			ll->start.ptr = 0;
			if ( ll->start.buf->seq == 0 )
				ll->start.buf = 0;
		}
		if ( ll->end.buf == cb ) {
			ll->end.buf = cb->prev;
			if ( ll->end.buf->seq == 0 ) {
				ll->end.buf = 0;
				ll->end.ptr = 0;
			}
			else 	ll->end.ptr = ll->end.buf->buf_len-1;
		}
		if ( ll->start.buf == 0 ) {
			ll->end = ll->start;
			_tr_destroy_lang_line(sq,ll);
		}
		else if ( ll->end.buf == 0 ) {
			ll->start = ll->end;
			_tr_destroy_lang_line(sq,ll);
		}
		else if ( tr_ptr_cmp(ll->start,ll->end) >= 0 ) {
			ll->start.buf = 0;
			ll->start.ptr = 0;
			ll->end = ll->start;
			_tr_destroy_lang_line(sq,ll);
		}
		ll = ll2;
	}
	for ( i = 0 ; i < cb->buf_len ; i ++ ) {
		_tr_free_attr(cb->element_buf[i].attr);
		if ( cb->element_buf[i].d.type >= 0 )
			continue;
		(*sq->box_op->obj_op->free_obj)(cb->element_buf[i].d.obj.obj);
	}
	d_f_ree(cb->element_buf);
	TR_RING_DELETE(cb)
	d_f_ree(cb);
}

TR_PTR
_tr_delete_string(
	TR_SEQUENCE * sq,
	TR_PTR ptr,
	int len)
{
TR_PTR ret;
TR_PTR next_ptr;
int next_len;
	_tr_ptr_regulation(&ptr);
	ret = ptr;
	for ( ; len ; ) {
		if ( ptr.ptr + len < ptr.buf->buf_len ) {
			_tr_cut_char_buf1(sq,ptr,len);
		}
		else {
			next_ptr.buf = ptr.buf->next;
			next_ptr.ptr = 0;
			next_len = len - (ptr.buf->buf_len - ptr.ptr);

			if ( ptr.ptr )
				_tr_cut_char_buf2(sq,ptr);
			else	_tr_destroy_char_buf(sq,ptr.buf);
		}
	}
	_insert_unmapping_char(sq,ret,TRE_OK_NONE);
/* end: */
	return ret;
}

TR_PTR
tr_delete_string(
	TR_SEQUENCE * sq,
	TR_PTR ptr,
	int len)
{
TR_PTR ret;
	tr_lock(sq);
	ret = _tr_delete_string(sq,ptr,len);
	tr_unlock(sq);
	return ret;
}

void
_tr_destroy_element(TR_SEQUENCE * sq,TR_LANG_LINE * ll,TR_ELEMENT * el)
{
TR_ELEMENT ** elp;
	for ( elp = &ll->element_list ; *elp && *elp != el ;
			 elp = &(*elp)->ll_next );
	if ( *elp == 0 )
		er_panic("_tr_Destroy_element");
	*elp = el->ll_next;
	TR_RING_DELETE(el);
	(*sq->box_op->unmapping_element)(el);
	d_f_ree(el);
}

void
_tr_destroy_lang_line(TR_SEQUENCE * sq,TR_LANG_LINE * ll)
{
TR_LANG_LINE ** llp;
TR_LANG_AREA * a;
TR_ELEMENT * el, * el2;
TR_CHAR_BUF * buf;
TR_PTR pbuf;
	if ( ll->start.buf ) {
		buf = ll->start.buf;
		for ( ; buf->seq ; buf = buf->next ) {
			pbuf.buf = buf;
			pbuf.ptr = 0;
			if ( tr_ptr_cmp(pbuf,ll->end) >= 0 )
				break;
			if ( buf->lang_line == ll ) {
				buf->lang_line
					= _tr_next_lang_line(ll);
			}
		}
	}
	for ( llp = &ll->box_line->lang_list ;
		*llp && *llp != ll ; llp = &(*llp)->box_next );
	if ( *llp == 0 )
		er_panic("_tr_destory_lang_line");
	*llp = ll->box_next;
	a = ll->lang_area;
	for ( llp = &a->line_list ;
		*llp && *llp != ll ; llp = &(*llp)->area_next );
	if ( *llp == 0 )
		er_panic("_tr_destroy_lang_line");
	*llp = ll->area_next;
	if ( a->line_list == 0 ) {
		TR_RING_DELETE(a);
		d_f_ree(a);
	}
	_insert_unmapping_char(sq,ll->start,TRE_OK_NONE);
	el = ll->element_list;
	for ( ; el ; ) {
		el2 = el->ll_next;
		_tr_destroy_element(sq,ll,el);
		el = el2;
	}
	d_f_ree(ll);
}

void
_tr_destroy_lang_line_list(TR_SEQUENCE * sq,TR_LANG_LINE * ll)
{
TR_LANG_LINE * ll2;
	for ( ; ll ; ) {
		ll2 = ll->box_next;
		_tr_destroy_lang_line(sq,ll);
		ll = ll2;
	}
}

void
_tr_destroy_box_line(TR_SEQUENCE * sq,TR_BOX_LINE * bl)
{
	_tr_destroy_lang_line_list(sq,bl->lang_list);
	(sq->box_op->free_box_line)(sq,bl);
	d_f_ree(bl);
}

void
_tr_destroy_box_line_list(TR_SEQUENCE * sq,TR_BOX_LINE * bl)
{
TR_BOX_LINE * bl2;
	for ( ; bl ; ) {
		bl2 = bl->next;
		_tr_destroy_box_line(sq,bl);
		bl = bl2;
	}
}

void
_tr_destroy_box(TR_SEQUENCE * sq,TR_BOX * b)
{
	_tr_destroy_box_line_list(sq,b->line_list);
	if ( sq->box_op->free_box )
		(*sq->box_op->free_box)(sq,b);
	TR_RING_DELETE(b);
}

TR_PTR
_tr_box_line_last_ptr(TR_BOX_LINE* bl)
{
TR_LANG_LINE * ll;
TR_PTR ret;
	ret.buf = 0;
	ret.ptr = 0;
	for ( ll = bl->lang_list ; ll ; ll = ll->box_next ) {
		if ( ll->end.buf ) {
			ret = ll->end;
			continue;
		}
		if ( ll->start.buf )
			ret = ll->start;
	}
	return ret;
}

TR_PTR
_tr_box_line_first_ptr(TR_BOX_LINE * bl)
{
TR_PTR ret;
TR_LANG_LINE * ll;
	ret.buf = 0;
	ret.ptr = 0;
	for ( ll = bl->lang_list ; ll ; ll = ll->box_next ) {
		if ( ll->start.buf )
			return ll->start;
		if ( ll->end.buf )
			return ll->end;
	}
	return ret;
}

TR_PTR
_tr_box_last_ptr(TR_BOX * b)
{
TR_BOX_LINE * bl;
TR_PTR ret;
	ret.buf = 0;
	ret.ptr = 0;
	if ( b->line_list == 0 )
		return ret;
	bl = b->line_list;
	for ( ; bl->next == 0 ; bl = bl->next );
	return _tr_box_line_last_ptr(bl);
}

TR_PTR
_tr_box_first_ptr(TR_BOX * b)
{
TR_PTR ret;
	ret.buf = 0;
	ret.ptr = 0;
	if ( b->line_list == 0 )
		return ret;
	return _tr_box_line_first_ptr(b->line_list);
}

int
_tr_get_doc_dir_pixels(TR_SEQUENCE * sq,TR_BOX * b)
{
int ret;
TR_BOX_LINE * bl;
	ret = 0;
	for ( bl = b->line_list ; bl ; bl = bl->next ) {
		ret += bl->height;
	}
	return ret;
}

void
_tr_box_doc_dir_pixels(TR_SEQUENCE * sq,TR_BOX * b)
{
int pixels;
TR_BOX_LINE * bl;
int ofs;
	pixels = _tr_get_doc_dir_pixels(sq,b);
	if ( b->start_px_offset + pixels < b->attr.max_doc_dir_pixels )
		_insert_unmapping_char(sq,_tr_box_last_ptr(b),TRE_OK_NONE);
	else if ( b->start_px_offset + pixels > b->attr.max_doc_dir_pixels ) {
		ofs = b->start_px_offset;
		for ( bl = b->line_list ; bl ; bl = bl->next ) {
			ofs += bl->height;
			if ( ofs > b->attr.max_doc_dir_pixels )
				break;
		}
		if ( bl ) {
			_insert_unmapping_char(sq,
				_tr_box_line_first_ptr(bl),TRE_OK_NONE);
			_tr_destroy_box_line_list(sq,bl->next);
			bl->next = 0;
		}
	}
}


void
_tr_dirty_box(TR_SEQUENCE * sq,TR_BOX * b)
{
	if ( b->dirty_flags & TRF_DIRTY_LINE_PIXELS ) {
		_tr_destroy_box_line_list(sq,b->line_list);
		b->line_list = 0;
		b->dirty_flags = 0;
		return;
	}
	if ( b->dirty_flags & TRF_DIRTY_DOC_DIR_PIXELS )
		_tr_box_doc_dir_pixels(sq,b);
	b->dirty_flags = 0;
}

void
tr_dirty_box(TR_SEQUENCE * sq,TR_BOX * b)
{
	tr_lock(sq);
	_tr_dirty_box(sq,b);
	tr_unlock(sq);
}

int
_tr_get_char_info(TR_CHAR_INFO * ci,TR_SEQUENCE * sq,TR_PTR ptr)
{
TR_PTR prev;
TR_LANG_LINE * ll;
int ret;
	ret = 0;
	ci->p = ptr;
	_tr_ptr_regulation(&ci->p);
	if ( ci->p.buf == 0 )
		ret = -1;
	if ( ptr.buf->lang_line == 0 ) {
		prev.buf = ptr.buf->prev;
		for ( ; ; ) {
			if ( prev.buf->seq == 0 ) {
				ci->lang_line = 0;
				ci->lang_area = 0;
				ci->box_line = 0;
				ci->box = sq->box_ring->next;
				if ( ci->box->seq == 0 ) {
					ret = -1;
					ci->box = 0;
				}
				goto end;
			}
			if ( prev.buf->lang_line )
				break;
			prev.buf = prev.buf->prev;
		}
		ll = prev.buf->lang_line;
	}
	else {
		ll = ptr.buf->lang_line;
	}
	for ( ; ll ; ll = _tr_next_lang_line(ll) ) {
		if ( tr_ptr_cmp(ll->start,ptr) > 0 ) {
			ll = 0;
			break;
		}
		if ( tr_ptr_cmp(ll->end,ptr) > 0 )
			break;
	}

	ci->lang_line = ll;
	if ( ll ) {
		ci->lang_area = ll->lang_area;
		ci->box_line = ll->box_line;
		ci->box = ci->box_line->box;
	}
	else {
		ci->lang_area = 0;
		ci->box_line = 0;
		ci->box = 0;
	}
end:
	if ( ci->p.buf )
		ci->lang = _tr_search_lang(ci->p.buf->element_buf[ci->p.ptr].ch,
					&sq->lang_ctx);
	else	ci->lang = 0;
	return ret;
}

void
_tr_set_bl_offset(TR_BOX * b,TR_BOX_LINE * st)
{
int doc_pixels;
	if ( st == 0 ) {
		doc_pixels = 0;
		st = b->line_list;
	}
	else {
		doc_pixels = st->doc_start_offset + st->height;
		st = st->next;
	}
	for ( ; st ; st = st->next ) {
		st->doc_start_offset = doc_pixels;
		doc_pixels += st->height;
	}
}

void
_tr_setup_bl_offset(TR_BOX * b,TR_BOX_LINE * bl)
{
	if ( bl == 0 ) {
		bl = b->line_list;
		bl->doc_start_offset = 0;
	}
	for ( ; bl && bl->next ; bl = bl->next ) {
		bl->next->doc_start_offset =
			bl->doc_start_offset +
			bl->height;
	}
}

TR_BOX_LINE *
_tr_new_box_line(TR_SEQUENCE * sq,TR_BOX * b,
		TR_BOX_LINE * bl,int where,int height)
{
TR_BOX_LINE * prev, ** blp, * ret;
TR_BOX_LINE * bl2;
TR_BOX * prev_box;
TR_ELEMENT * el;
	if ( b->attr.max_doc_dir_pixels >= 0 ) {
		for ( bl2 = b->line_list ; bl2 && bl2->next ; bl2 = bl2->next );
		if ( bl2 == 0 ) {
			if ( b->start_px_offset +
				b->attr.max_doc_dir_pixels < height )
				return 0;
		}
		else {
			if ( b->start_px_offset +
				b->attr.max_doc_dir_pixels <
				bl2->doc_start_offset +
				bl2->height + height )
				return 0;
		}
	}
	switch ( where ) {
	case TRF_WHERE_TOP:
		prev = 0;
		blp = &b->line_list;
		break;
	case TRF_WHERE_LAST:
		prev = 0;
		for ( blp = &b->line_list ; *blp ; 
			prev = *blp, blp = &(*blp)->next );
		break;
	case TRF_WHERE_NEXT:
		prev = 0;
		blp = &bl->next;
		break;
	case TRF_WHERE_PREV:
		prev = 0;
		for ( blp = &b->line_list ;
			*blp &&
			(*blp)->next != bl;
			prev = *blp, blp = &(*blp)->next );
		break;
	default:
		er_panic("_tr_new_box_line");
	}
	ret = d_alloc(sizeof(*ret));
	memset(ret,0,sizeof(*ret));
	ret->next = *blp;
	*blp = ret;
	ret->height = height;
	ret->el_head = el = d_alloc(sizeof(*el));
	ret->box = b;
	memset(el,0,sizeof(*el));
	TR_RING_INIT(el);
	
	if ( prev ) {
		ret->attr = prev->attr;
	}
	else {
		for ( prev_box = b->prev ; prev_box->seq ;
				prev_box = prev_box->prev ) {
			if ( prev_box->line_list )
				break;
		}
		if ( prev_box->seq == 0 ) {
			ret->attr = b->attr.default_lattr;
		}
		else {
			for ( bl2 = prev_box->line_list ; bl2->next ;
					bl2 = bl2->next );
			ret->attr = bl2->attr;
		}
	}
	if ( sq->box_op->new_box_line )
		(*sq->box_op->new_box_line)(sq,ret);
	_tr_setup_bl_offset(b,prev);
	return ret;
}

void
_tr_copy_line_attr(TR_LINE_ATTR * a1,TR_LINE_ATTR * a2)
{
	if ( a2->flags & TRLF_ALIGN ) {
		a1->align = a2->align;
	}
	if ( a2->flags & TRLF_DEFAULT_DIR ) {
		a1->default_dir = a2->default_dir;
	}
	if ( a2->flags & TRLF_PITCH ) {
		a1->pitch = a2->pitch;
	}
}



TR_ERROR
_tr_stop_lang_line_op(TR_ERROR e,TR_SEQUENCE * sq,TR_CHAR_INFO * ci)
{
TR_PTR p;
TR_LANG_LINE * ll;
TR_LINE_ATTR a;
TR_ERROR err;
	ll = ci->box_line->lang_list;
	memset(&a,0,sizeof(a));
	for ( ; ll ; ll = ll->box_next ) {
		for ( p = ll->start ; tr_ptr_cmp(p,ll->end) < 0 ; 
				p = tr_ptr_next(p) ) {
			_tr_acc_line_attr(&a,p.buf->element_buf[p.ptr].attr);
			if ( a.flags == TRLF_ALL )
				break;
		}
	}
	_tr_copy_line_attr(&ci->box_line->attr,&a);
	ll = ci->box_line->lang_list;
	for ( ; ll ; ll = ll->box_next ) {
		for ( p = ll->start ; tr_ptr_cmp(p,ll->end) < 0 ;
				p = tr_ptr_next(p) ) {
			_tr_set_line_attr(&a,
				&p.buf->element_buf[p.ptr].attr);
		}
	}
	err = (*sq->box_op->_stop_lang_line_op)(sq,ci);
	if ( err.code == TRE_OK )
		return e;
	else	return err;
}


int
_tr_lang_area_check(TR_LANG_AREA * a,TR_LANG_LINE * ll)
{
TR_LANG_LINE * llp;
	for ( llp = a->line_list ; llp ; llp = llp->area_next )
		if ( llp == ll )
			return 0;
	return -1;
}

TR_LANG_LINE *
_tr_new_lang_line(
	TR_SEQUENCE * sq,
	TR_BOX_LINE * bl,
	TR_LANG_AREA * a,
	TR_LANG_LINE * ll,
	int where)
{
TR_LANG_LINE ** llp;
TR_LANG_LINE * ret,* prev, * next, * target;
TR_LANG_AREA * a_prev, * a_next;
	ret = d_alloc(sizeof(*ret));
	memset(ret,0,sizeof(*ret));
	ret->box_line = bl;
	ret->lang_area = a;

	switch ( where ) {
	case TRF_WHERE_TOP:
		prev = 0;
		next = a->line_list;
		ret->area_next = a->line_list;
		a->line_list = ret;
		break;
	case TRF_WHERE_NEXT:
		if ( ll == 0 )
			er_panic("_tr_new_lang_line");
		prev = ll;
		next = ll->area_next;
		if ( ll->lang_area == a ) {
			ret->area_next = ll->area_next;
			ll->area_next = ret;
		}
		else {
			for ( a_next = ll->lang_area->next ;
				a_next->op &&
				a_next->line_list == 0;
				a_next = a_next->next )
				if ( a_next == a )
					break;
			if ( a_next->op == 0 )
				er_panic("_tr_new_lang_line");
			if ( a_next != a )
				er_panic("_tr_new_lang_line");
			ret->area_next = a->line_list;
			a->line_list = ret;
		}
		break;
	case TRF_WHERE_PREV:
		if ( ll == 0 )
			er_panic("_tr_new_lang_line");
		if ( ll->lang_area == a ) {
			if ( a->line_list == ll ) {
				prev = 0;
				ret->area_next = ll;
				a->line_list = ret;
			}
			else {
				for ( prev = a->line_list ;
					prev->area_next == ll ; 
					prev = prev->area_next );
				ret->area_next = ll;
				prev->area_next = ret;
			}
		}
		else {
			for ( a_prev = ll->lang_area->prev ;
				a_prev->op &&
				a_prev->line_list == 0;
				a_prev = a_prev->prev)
				if ( a_prev == a )
					break;
			if ( a_prev->op == 0 )
				er_panic("_tr_new_lang_line");
			if ( a_prev != a )
				er_panic("_tr_new_lang_line");
			ret->area_next = 0;
			for ( llp = &a_prev->line_list ;
				*llp;
				llp = &(*llp)->area_next );
			*llp = ret;
		}
		next = ll;
		break;
	case TRF_WHERE_LAST:
		if ( a->line_list == 0 ) {
			prev = 0;
			next = 0;
			a->line_list = ret;
			ret->area_next = 0;
		}
		else {
			for ( prev = a->line_list ; prev->area_next ;
					prev = prev->area_next );
			prev->area_next = ret;
			next = 0;
		}
		break;
	default:
		er_panic("_tr_new_lang_line");
	}
	if ( prev == 0 ) {
		a_prev = a->prev;
		for ( ; a_prev->op && a_prev->line_list == 0 ;
				a_prev = a_prev->prev );
		if ( a_prev->op )
			for ( prev = a_prev->line_list ;
					prev->area_next ;
					prev = prev->area_next );
	}
	if ( next == 0 ) {
		a_next = a->next;
		for ( ; a_next->op && a_next->line_list == 0 ;
				a_next = a_next->next );
		if ( a_next->op )
			next = a_next->line_list;
	}
	if ( prev == 0 ) {
		ret->box_next = bl->lang_list;
		bl->lang_list = ret;
	}
	else if ( next == 0 ) {
		for ( llp = &bl->lang_list ; *llp ; llp = &(*llp)->box_next );
		*llp = ret;
	}
	else {
		for ( target = bl->lang_list ;
				target &&
				target != prev ;
				target = target->box_next );
		if ( target ) {
			if ( target->box_next &&
					target->box_next != next )
				er_panic("_tr_new_lang_line");
			ret->box_next = target->box_next;
			target->box_next = ret;
		}
		else {
			if ( bl->lang_list && bl->lang_list != next )
				er_panic("_tr_new_lang_line");
			ret->box_next = bl->lang_list;
			bl->lang_list = ret;
		}
	}
	return ret;
}

TR_LANG_AREA *
_tr_new_lang_area(
	TR_SEQUENCE * sq,
	TR_LANG * lang,
	TR_LANG_AREA * prev)
{
TR_LANG_AREA * ret;
	ret = d_alloc(sizeof(*ret));
	memset(ret,0,sizeof(*ret));
	ret->op = lang;
	if ( prev ) {
		TR_RING_INSERT(prev,ret);
	}
	else {
		TR_RING_INSERT(sq->lang_area_ring,ret);
	}
	return ret;
}

void
_tr_get_lang_area_info(TR_LANG_AREA_INFO *lai,TR_LANG_AREA *a)
{
TR_LANG_LINE * ll;
TR_BOX_LINE * bl;
	ll = a->line_list;
	if ( ll == 0 ) {
		memset(lai,0,sizeof(*lai));
		return;
	}
	lai->start_char = ll->start;
	lai->start_lang_line = ll;
	lai->start_box_line = ll->box_line;
	lai->start_box = ll->box_line->box;
	for ( ; ll->area_next ; ll = ll->area_next );
	lai->end_char = ll->end;
	lai->end_lang_line = ll;
	lai->end_box_line = ll->box_line;
	lai->end_box = ll->box_line->box;


	if ( lai->start_box->seq <= lai->end_box->seq ) {
		lai->phys_start_box = lai->start_box;
		lai->phys_end_box = lai->end_box;
		if ( lai->start_box == lai->end_box ) {
			for ( bl = lai->start_box_line ;
					bl &&
					bl != lai->end_box_line;
					bl = bl->next );
			if ( bl == 0 )
				goto swap;
			goto 	strait;
		}
		else {
		strait:
			lai->phys_start_box_line = lai->start_box_line;
			lai->phys_end_box_line = lai->end_box_line;
		}
	}
	else {
		lai->phys_end_box = lai->start_box;
		lai->phys_start_box = lai->end_box;
	swap:
		lai->phys_end_box_line = lai->start_box_line;
		lai->phys_start_box_line = lai->end_box_line;
	}
}

int
_tr_lang_area_cmp_ci(TR_SEQUENCE * sq,TR_CHAR_INFO * ci1,TR_CHAR_INFO * ci2)
{
TR_CHAR_ELEMENT * el1,* el2;
	el1 = &(ci1->p.buf->element_buf[ci1->p.ptr]);
	el2 = &(ci2->p.buf->element_buf[ci2->p.ptr]);
	if ( el1->d.type < 0 || el2->d.type < 0 )
		return -1;
	if ( CTL_CODE(el1->ch) || CTL_CODE(el2->ch) )
		return -1;
	if ( ci1->lang != ci2->lang )
		return -1;
	if ( _tr_cmp_attr(el1->attr ,el2->attr) )
		return -1;
	return 0;
}

int
_tr_lang_area_cmp_ptr(TR_SEQUENCE * sq,TR_PTR p1,TR_PTR p2)
{
TR_CHAR_INFO ci1,ci2;
	_tr_get_char_info(&ci1,sq,p1);
	_tr_get_char_info(&ci2,sq,p2);
	return _tr_lang_area_cmp_ci(sq,&ci1,&ci2);
}

int
_tr_box_line_adjust(TR_SEQUENCE * sq,TR_BOX_LINE * b1,TR_BOX_LINE * b2)
{
TR_BOX_LINE * _b1;
	for ( _b1 = b1->next ; _b1 != b2 ; _b1 = _b1->next );
	if ( _b1 == 0 )
		return -1;
	for ( _b1 = b1->next ; _b1 != b2 ; _b1 = _b1->next ) {
		_tr_destroy_box_line(sq,_b1);
	}
	return 0;
}

void
_tr_lang_area_proc(TR_SEQUENCE * sq,TR_CHAR_INFO * ci)
{
TR_CHAR_INFO ci_prev;
TR_PTR prev;
TR_LANG_AREA_INFO lai;
TR_BOX_LINE * bl;
TR_LANG_LINE * ll;
TR_ERROR e;
int height;
	prev = tr_ptr_prev(ci->p);
	if ( prev.buf ) {
		_tr_get_char_info(&ci_prev,sq,prev);
		_tr_get_lang_area_info(&lai,ci->lang_area);
		if ( lai.start_box != ci_prev.box &&
				lai.end_box != ci_prev.box &&
				tr_ptr_cmp(ci->lang_line->start,
					ci->p) == 0 ) {
			if ( (ci->lang_area->dir & VSD_V_L2R) )
				er_panic("not support VSD_V_L2R");
			if ( ci_prev.box->line_list == 0 ) {
				bl = _tr_new_box_line(sq,
					ci_prev.box,
					0,TRF_WHERE_LAST,
					ci->box_line->height);
				if ( bl == 0 )
					goto next;
				ci->box = ci_prev.box;
				ci->box_line = bl;
				ll = _tr_new_lang_line(sq,
					bl,
					ci->lang_area,
					ci->lang_line,
					TRF_WHERE_PREV);
				ll->start = ci->lang_line->start;
				ci->lang_line = ll;
			}
		}
	}
next:
	e = (*ci->lang->make_lang_line)(sq,ci);
	switch ( e.code ) {
	case TRE_OK:
		_tr_disable_unmapping_char(sq,ci->lang_line->end);
		ll = _tr_next_lang_line(ci->lang_line);
		for ( ; ; ) {
			if ( ll == 0 ) {
				_insert_unmapping_char(sq,ci->lang_line->end,e.subcode);
				break;
			}
			switch ( tr_ptr_cmp(ll->start,ci->lang_line->end) ) {
			case 1:
				_insert_unmapping_char(sq,ci->lang_line->end,e.subcode);
				break;
			case 0:
				if ( e.subcode == TRE_OK_NEW_LINE ) {
					if ( _tr_box_line_adjust(
							sq,
							ci->lang_line->box_line,
							ll->box_line) == 0 )
						break;
					_insert_unmapping_char(sq,ci->lang_line->end,
						e.subcode);
				}
				else {
					if ( ci->lang_line->box_line
							== ll->box_line )
						break;
					_insert_unmapping_char(sq,ci->lang_line->end,
						e.subcode);
				}
				break;
			case -1:
				ll = _tr_next_lang_line(ll);
				_tr_destroy_lang_line(sq,ll);
				continue;
			}
			break;
		}
		if ( tr_ptr_cmp(ci->lang_line->start,ci->lang_line->end) == 0 ) {
			_tr_destroy_lang_line(sq,ci->lang_line);
			ci->lang_line = 0;
		}
		break;
	case TRE_INSERT_LINE:
		height = ci->box_line->height;
		ll = _tr_next_lang_line(ci->lang_line);
		_tr_destroy_lang_line(sq,ci->lang_line);
		ci->lang_line = 0;
		ci->box = ci->box->next;
		if ( ci->box->seq == 0 ) {
			_tr_disable_unmapping_char(sq,ci->p);
			break;
		}
		if ( ci->box->line_list == 0 ) {
			bl = _tr_new_box_line(sq,
				ci->box,
				0,TRF_WHERE_TOP,
				height);
			if ( bl == 0 ) {
				_tr_disable_unmapping_char(sq,ci->p);
				break;
			}
			ci->box_line = bl;
		}
		else {
			ci->box_line = ci->box->line_list;
		}
		if ( ll ) {
			ci->lang_line = _tr_new_lang_line(sq,
				ci->box_line,
				ci->lang_area,
				ll,
				TRF_WHERE_PREV);
		}
		else {
			ci->lang_line = _tr_new_lang_line(sq,
				ci->box_line,
				ci->lang_area,
				0,
				TRF_WHERE_TOP);
		}
		ci->lang_line->start = ci->p;
		goto next;
	default:
		er_panic("_tr_lang_area_proc");
	}
	if ( ci->lang_line )
		_tr_set_char_buf_lang_line(ci->lang_line);
}

void
_tr_unmapping_char_proc(TR_SEQUENCE * sq,TR_UNMAPPING_CHAR * umc)
{
TR_CHAR_INFO cinf,ci_prev;
TR_PTR prev;

	if ( umc->p.buf->next->seq == 0 &&
		umc->p.ptr == umc->p.buf->buf_len )
		return;
	if ( _tr_get_char_info(&cinf,sq,umc->p) < 0 )
		return;
	prev = tr_ptr_prev(umc->p);
	if ( prev.buf && prev.buf->seq ) {
		_tr_get_char_info(&ci_prev,sq,prev);
		if ( cinf.lang_line == 0 ) {
			if ( umc->ok_subcode == TRE_OK_NEW_LINE ) {
				cinf.box = ci_prev.box;
				cinf.lang_area = 
					_tr_new_lang_area(sq,
						cinf.lang,
						ci_prev.lang_area);
				cinf.box_line = _tr_new_box_line(sq,
							cinf.box,
							ci_prev.box_line,
							TRF_WHERE_NEXT,0);
				if ( ci_prev.lang_line == 0 )
					cinf.lang_line =
						_tr_new_lang_line(sq,
							cinf.box_line,
							cinf.lang_area,
							0,
							TRF_WHERE_TOP);
				else	cinf.lang_line =
						_tr_new_lang_line(sq,
							cinf.box_line,
							cinf.lang_area,
							ci_prev.lang_line,
							TRF_WHERE_NEXT);
				cinf.lang_line->start = cinf.p;
			}
			else if ( _tr_lang_area_cmp_ci(sq,&ci_prev,&cinf) == 0 ) {
				cinf = ci_prev;
			}
			else {
				cinf.box = ci_prev.box;
				cinf.box_line = ci_prev.box_line;
				cinf.lang_area = 
					_tr_new_lang_area(sq,
						cinf.lang,
						ci_prev.lang_area);
				if ( cinf.box_line == 0 )
					cinf.box_line = _tr_new_box_line(sq,
								cinf.box,0,
								TRF_WHERE_TOP,0);
				if ( ci_prev.lang_line == 0 )
					cinf.lang_line =
						_tr_new_lang_line(sq,
							cinf.box_line,
							cinf.lang_area,
							0,
							TRF_WHERE_TOP);
				else	cinf.lang_line =
						_tr_new_lang_line(sq,
							cinf.box_line,
							cinf.lang_area,
							ci_prev.lang_line,
							TRF_WHERE_NEXT);
				cinf.lang_line->start = cinf.p;
			}
		}
	}
	else {
		if ( cinf.lang_line == 0 ) {
			cinf.box_line = _tr_new_box_line(sq,cinf.box,0,
						TRF_WHERE_TOP,0);
			cinf.lang_area = _tr_new_lang_area(sq,cinf.lang,0);
			cinf.lang_line = _tr_new_lang_line(sq,
						cinf.box_line,
						cinf.lang_area,
						0,TRF_WHERE_LAST);
			cinf.lang_line->start = cinf.p;
		}
	}
	if ( cinf.lang_line->start.buf == 0 )
		er_panic("_tr_char_buf_proc");
	cinf.p = cinf.lang_line->start;
	cinf.lang = cinf.lang_area->op;
	_tr_lang_area_proc(sq,&cinf);
}

void
tr_unmapping_char_proc(TR_SEQUENCE * sq,TR_UNMAPPING_CHAR * umc)
{
	tr_lock(sq);
	_tr_unmapping_char_proc(sq,umc);
	tr_unlock(sq);
}

void
tr_sequence_task(TKEY d)
{
TR_SEQUENCE * sq;
TR_DIRTY dirty;
int df;
void (*cb_func)(TR_SEQUENCE *,void*);
void *cb_arg;
	sq = (TR_SEQUENCE*)GET_TKEY(d);
// ss_printf("START SEQUENCE TASK %i %x\n",get_tid(),sq);
if ( sq->waitsync.list && ((unsigned int)sq->waitsync.list) < 0x1000 )
er_panic("3");
	if ( sq->box_op->init_thread )
		(*sq->box_op->init_thread)(sq);
	for ( ; ; ) {
		df = 0;
		tr_lock(sq);
		if ( _get_dirty(sq,&dirty) < 0 )
			break;
		tr_unlock(sq);
		if ( dirty.box ) {
			df = 1;
			tr_dirty_box(sq,dirty.box);
		}
		if ( dirty.umc.p.buf ) {
			df = 1;
			tr_unmapping_char_proc(sq,&dirty.umc);
		}
		tr_lock(sq);
		cb_func = sq->callback;
		cb_arg = sq->callback_arg;
		tr_unlock(sq);
		if ( df && cb_func )
			(*cb_func)(sq,cb_arg);
	}
	if  ( sq->box_op->exit_thread )
		(*sq->box_op->exit_thread)(sq);
	_tr_wakeup_sq_position(sq);
	if ( sq->thread > 0 )
		sq->thread = 0;
	waitsync_wakeup(&sq->waitsync);
	tr_unlock(sq);
}

TR_LANG_LINE *
_tr_element_prev_lang_line(TR_LANG_LINE * ll)
{
TR_LANG_LINE * ll_prev;
	ll_prev = _tr_prev_lang_line(ll);
	for ( ; ll_prev ; ll_prev = _tr_prev_lang_line(ll) ) {
		if ( ll_prev->element_list )
			break;
	}
	return ll_prev;
}

TR_LANG_LINE *
_tr_element_next_lang_line(TR_LANG_LINE * ll)
{
TR_LANG_LINE * ll_prev;
	ll_prev = _tr_next_lang_line(ll);
	for ( ; ll_prev ; ll_prev = _tr_next_lang_line(ll_prev) ) {
		if ( ll_prev->element_list )
			break;
	}
	return ll_prev;
}

TR_ELEMENT *
_tr_forward(TR_LANG_LINE * ll,TR_LANG_LINE * ll_next)
{
TR_LANG_LINE * _ll_next;
TR_ELEMENT * ret;
	_ll_next = 0;
	for ( ; ll_next ; ll_next = _tr_element_next_lang_line(ll_next) ) {
		if ( ll_next->box_line != ll->box_line )
			break;
		if ( ll_next->dir & VSD_REVERSE )
			break;
		_ll_next = ll_next;
	}
	for ( ret = _ll_next->element_list ; ret->ll_next ;
		ret = ret->ll_next );
	return ret;
}

TR_ELEMENT *
_tr_last_el(TR_LANG_LINE * ll)
{
TR_ELEMENT * ret;
	if ( ll->element_list == 0 )
		er_panic("_tr_last_el");
	for ( ret = ll->element_list ; ret->ll_next ; ret = ret->ll_next );
	return ret;
}

TR_ELEMENT *
_tr_reverse(TR_LANG_LINE * ll,TR_LANG_LINE * ll_prev)
{
TR_LANG_LINE * _ll_prev;
	_ll_prev = 0;
	for ( ; ll_prev ; ll_prev = _tr_element_prev_lang_line(ll_prev) ) {
		if ( ll_prev->box_line != ll->box_line )
			break;
		if ( ll_prev->dir & VSD_FORWARD )
			break;
		_ll_prev = ll_prev;
	}
	return _ll_prev->element_list;
}

TR_ERROR
_tr_new_element(TR_ELEMENT ** el_ret,TR_SEQUENCE * sq,TR_LANG_LINE * ll)
{
TR_ELEMENT * el,* el_last, ** elp;
TR_LANG_LINE * ll_prev,* ll_next;
TR_ELEMENT * el_prev;
TR_ERROR err;
TR_BOX_LINE * bl;
	bl = ll->box_line;
	el_last = 0;
	for ( elp = &ll->element_list ; *elp ; el_last = *elp,
				elp = &(*elp)->ll_next );
	el = d_alloc(sizeof(*el));
	memset(el,0,sizeof(*el));

	if ( ll->element_list ) {
		if ( ll->dir & VSD_FORWARD )
			el_prev = el_last;
		else	el_prev = el_last->prev;
	}
	else {
		ll_prev = _tr_element_prev_lang_line(ll);
		ll_next = _tr_element_next_lang_line(ll);
		if ( ll_prev == 0 || ll_prev->box_line != ll->box_line ) {
			if ( ll_next == 0 || 
					ll_next->box_line != ll->box_line ) {
				el_prev = ll->box_line->el_head;
			}
			else {
			next_argo:
				if ( ll_next->dir & VSD_FORWARD ) {
					if ( ll->dir & VSD_FORWARD ) {
						el_prev = 
							ll_next->element_list
								->prev;
					}
					else if ( ll->box_line->attr.default_dir
							& VSD_FORWARD ) {
						el_prev =
							ll_next->element_list
								->prev;
					}
					else {
						el_prev = 
							_tr_forward(ll,ll_next);
					}
				}
				else {
					if ( ll->dir & VSD_REVERSE ) {
						el_prev =
							ll_next->element_list;
					}
					else if ( ll->box_line->attr.default_dir
							& VSD_REVERSE ) {
						el_prev =
							ll_next->element_list;
					}
					else {
						el_prev = ll->box_line->el_head;
					}
				}
			}
		}
		else {
			if ( ll_next == 0 || 
					ll_next->box_line != ll->box_line ) {
			prev_argo:
				if ( ll_prev->dir & VSD_FORWARD ) {
					if ( ll->dir & VSD_FORWARD ) {
						el_prev = 
							_tr_last_el(ll_prev);
					}
					else if ( ll->box_line->attr.default_dir
							& VSD_FORWARD ) {
						el_prev =
							_tr_last_el(ll_prev);
					}
					else {
						el_prev =
							_tr_reverse(ll,ll_prev);
					}
				}
				else {
					if ( ll->dir & VSD_REVERSE ) {
						el_prev = _tr_last_el(ll_prev)
								->prev;
					}
					else if ( ll->box_line->attr.default_dir
							& VSD_REVERSE ) {
						el_prev = _tr_last_el(ll_prev)
								->prev;
					}
					else {
						el_prev = 
							_tr_reverse(ll,ll_prev);
					}
				}
			}
			else {
				if ( ((ll_next->dir & VSD_FORWARD) &&
						(ll->dir & VSD_FORWARD)) ||
						((ll_next->dir & VSD_REVERSE) &&
						(ll->dir & VSD_REVERSE)) )
					goto next_argo;
				else	goto prev_argo;
			}
		}
	}
	if ( bl->el_head == el_prev ) {
		if ( bl->el_head->next->cr & VSD_REVERSE ) {
			err.code = TRE_INSERT_CHAR;
			d_f_ree(el);
			return err;
		}
	}
	else if ( bl->el_head == el_prev->next ) {
		if ( el_prev->cr & VSD_FORWARD ) {
			err.code = TRE_INSERT_CHAR;
			d_f_ree(el);
			return err;
		}
	}
	*elp = el;
	TR_RING_INSERT(el_prev,el);
	if ( el_ret )
		*el_ret = el;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}


void
_tr_set_default_lattr(TR_LINE_ATTR * a)
{
	a->flags = TRLF_ALL;
	a->align = TRT_LINE_START;
	a->default_dir = VSD_H_L2R;
	a->pitch = 15;
}


void
_tr_wait_stable_sequence(TR_SEQUENCE * sq)
{
	for ( ; sq->unmapping_char || sq->thread > 0 ; ) {
		_tr_sleep_sq_position(sq);
		tr_lock(sq);
	}
	
}



void
tr_wait_stable_sequence(TR_SEQUENCE * sq)
{
	tr_lock(sq);
	_tr_wait_stable_sequence(sq);
	tr_unlock(sq);
}

void
_tr_waitsync(TR_SEQUENCE * sq,WAITSYNC_CLIENT * c,int type)
{
	if ( sq->thread < 0 )
		return;
	if ( sq->unmapping_char || sq->thread > 0 )
		waitsync_ack(c,&sq->waitsync,type);
	if ( sq->box_op && sq->box_op->waitsync )
		(sq->box_op->waitsync)(sq,c,type);
}

void
tr_waitsync(TR_SEQUENCE * sq,WAITSYNC_CLIENT * c,int type)
{

	tr_lock(sq);
waitsync_test(sq,"5");
	_tr_waitsync(sq,c,type);
waitsync_test(sq,"6");
	tr_unlock(sq);
}



void
tr_close_sequence(TR_SEQUENCE * sq)
{
AVT_NODE * n;
TR_REFERENCE * ref;
TR_SEQUENCE ** pp;
	tr_lock(sq);
	_tr_wait_stable_sequence(sq);
	sq->thread = -1;

	lock_task(text_render_lock);
	for ( pp = &sq_list ; *pp && *pp != sq ; pp = &(*pp)->next );
	if ( *pp == 0 )
		er_panic("tr_close_sequence");
	*pp = sq->next;
	unlock_task(text_render_lock,"tr_close_sequnece");

	for ( ; sq->unmapping_char ; )
		_tr_disable_unmapping_char(sq,
			tr_ptr_next(sq->unmapping_char->p));
	for ( ; sq->box_ring->prev != sq->box_ring ; )
		_tr_destroy_box(sq,sq->box_ring->prev);
	for ( ; sq->char_buf_ring->prev != sq->char_buf_ring ; )
		_tr_destroy_char_buf(sq,sq->char_buf_ring->prev);
	for ( ; sq->ref ; ) {
		ref = sq->ref->data;
		n = avt_delete(&sq->ref,ref,tr_ref_cmp);
		if ( ref->data )
			d_f_ree(ref->data);
		d_f_ree(ref);
		d_f_ree(n);
	}
	_trl_free_langs(sq->lang_ctx.used);

	d_f_ree(sq->box_ring);
	d_f_ree(sq->char_buf_ring);

	waitsync_wakeup(&sq->waitsync);

	tr_unlock(sq);

	memset(sq,0xf7,sizeof(*sq));

	d_f_ree(sq);

}


int
_tr_copyout(TR_PTR p,L_CHAR * str,int len)
{
int ret;
	if ( len < 0 )
		return -1;
	_tr_ptr_regulation(&p);
	if ( p.buf == 0 )
		return 0;
	if ( p.buf->buf_len <= p.ptr )
		return 0;
	ret = 0;
	for ( ; len ; ) {
		if ( p.ptr >= p.buf->buf_len ) {
			p.ptr = 0;
			p.buf = p.buf->next;
			if ( p.buf->seq == 0 )
				break;
		}
		*str++ = p.buf->element_buf[p.ptr++].ch;
		len --;
		ret ++;
	}
	return ret;
}

int
tr_copyout(TR_SEQUENCE * sq,TR_PTR p,L_CHAR * str,int len)
{
int ret;
	tr_lock(sq);
	ret = _tr_copyout(p,str,len);
	tr_unlock(sq);
	return ret;
}

void
_tr_set_callback(TR_SEQUENCE * sq,
	void (*func)(TR_SEQUENCE*,void*),
	void * arg)
{
	sq->callback = func;
	sq->callback_arg = arg;
}

void
tr_set_callback(TR_SEQUENCE * sq,
	void (*func)(TR_SEQUENCE*,void*),
	void * arg)
{
	tr_lock(sq);
	_tr_set_callback(sq,func,arg);
	tr_unlock(sq);	
}


