/**********************************************************************
 
	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	"rect_tree.h"


void
free_rect_node(RECT_NODE * rn)
{
	d_f_ree(rn);
	touch_obj_mem(-sizeof(*rn));
}

int
rt_cmp(GB_RECT * min,GB_RECT * r,GB_RECT * max)
{
	if ( min->tl.x > r->tl.x )
		return -1;
	if ( min->tl.y > r->tl.y )
		return -1;
	if ( min->br.x > r->br.x )
		return -1;
	if ( min->br.y > r->br.y )
		return -1;

	if ( max->tl.x < r->tl.x )
		return -1;
	if ( max->tl.y < r->tl.y )
		return -1;
	if ( max->br.x < r->br.x )
		return -1;
	if ( max->br.y < r->br.y )
		return -1;
	return 0;
}

int
rt_equ(GB_RECT * r1,GB_RECT * r2)
{
	if ( r1->tl.x != r2->tl.x )
		return -1;
	if ( r1->tl.y != r2->tl.y )
		return -1;
	if ( r1->br.x != r2->br.x )
		return -1;
	if ( r1->br.y != r2->br.y )
		return -1;
	return 0;
}

RECT_NODE *
new_rect_node(GB_RECT* min,GB_RECT *max,GB_RECT *avg,int index)
{
RECT_NODE * ret;
int i;

	touch_obj_mem(sizeof(*ret)); 
	ret = d_alloc(sizeof(*ret));
	if ( avg == 0 ) {
		ret->max = *max;
		ret->min = *min;
	}
	else {
		if ( index & RN_TLX ) {
			ret->min.tl.x = avg->tl.x;
			ret->max.tl.x = max->tl.x;
		}
		else {
			ret->min.tl.x = min->tl.x;
			ret->max.tl.x = avg->tl.x;
		}
		if ( index & RN_TLY ) {
			ret->min.tl.y = avg->tl.y;
			ret->max.tl.y = max->tl.y;
		}
		else {
			ret->min.tl.y = min->tl.y;
			ret->max.tl.y = avg->tl.y;
		}
		if ( index & RN_BRX ) {
			ret->min.br.x = avg->br.x;
			ret->max.br.x = max->br.x;
		}
		else {
			ret->min.br.x = min->br.x;
			ret->max.br.x = avg->br.x;
		}
		if ( index & RN_BRY ) {
			ret->min.br.y = avg->br.y;
			ret->max.br.y = max->br.y;
		}
		else {
			ret->min.br.y = min->br.y;
			ret->max.br.y = avg->br.y;
		}
	}
	ret->ntype = 0;
	for ( i = 0 ; i < RN_INDEX_MAX ; i ++ )
		ret->ptr[i].node = 0;
	ret->avg.tl.x = (ret->max.tl.x + ret->min.tl.x)/2;
	ret->avg.tl.y = (ret->max.tl.y + ret->min.tl.y)/2;
	ret->avg.br.x = (ret->max.br.x + ret->min.br.x)/2;
	ret->avg.br.y = (ret->max.br.y + ret->min.br.y)/2;
	return ret;
}


RT_OBJ_LIST *
new_rt_obj_list(void * obj,RT_OBJ_LIST * ol)
{
RT_OBJ_LIST * ret;
	touch_obj_mem(sizeof(*ret));
	ret = d_alloc(sizeof(*ret));
	ret->obj = obj;
	ret->next = ol;
	return ret;
}

void
free_rt_obj_list(RT_OBJ_LIST * ol)
{
RT_OBJ_LIST * ol2;
	for ( ; ol ; ) {
		ol2 = ol;
		ol = ol->next;
		d_f_ree(ol2);
		touch_obj_mem(-sizeof(*ol2));
	}
}

void
init_rect_tree(RECT_TREE * rt,GB_RECT * r,GB_RECT* (*func)())
{
	rt->min.tl = r->tl;
	rt->min.br = r->tl;
	rt->max.tl = r->br;
	rt->max.br = r->br;
	rt->ntype = 0;
	rt->ptr.node = 0;
	rt->get_minrect = func;
}


void
_insert_rect_tree(RECT_TREE * rt,RECT_NODE * rn,RT_OBJ_LIST * ol2,
	int depth)
{
int index;
RT_OBJ_LIST * ol3;
	index = 0;
	if ( rn->avg.tl.x < ol2->r.tl.x )
		index |= RN_TLX;
	if ( rn->avg.tl.y < ol2->r.tl.y )
		index |= RN_TLY;
	if ( rn->avg.br.x < ol2->r.br.x )
		index |= RN_BRX;
	if ( rn->avg.br.y < ol2->r.br.y )
		index |= RN_BRY;
	if ( rn->ntype & (1<<index) ) {
		ol3 = rn->ptr[index].ol;
		if ( rt_equ(&ol2->r,&ol3->r) == 0 ) {
			ol2->next = ol3;
			rn->ptr[index].ol = ol2;
			return;
		}

		rn->ptr[index].node = new_rect_node
			(&rn->min,&rn->max,&rn->avg,index);
		_insert_rect_tree(rt,rn->ptr[index].node,ol2,depth+1);
		_insert_rect_tree(rt,rn->ptr[index].node,ol3,depth+1);
		rn->ntype &= ~(1<<index);
	}
	else if ( rn->ptr[index].node ) {
		_insert_rect_tree(rt,rn->ptr[index].node,ol2,depth+1);
	}
	else {
		rn->ptr[index].ol = ol2;
		rn->ntype |= 1<<index;
	}
}

void
insert_rect_tree(RECT_TREE * rt,void * obj)
{
GB_RECT * r;
void * obj2;
RT_OBJ_LIST * ol2, * ol3;

	ol2 = new_rt_obj_list(obj,0);
	ol2->r = *(*rt->get_minrect)(obj);
	if ( rt->ptr.node == 0 ) {
		rt->ptr.ol = ol2;
		rt->ntype = 1;
	}
	else if ( rt->ntype ) {
		ol3 = rt->ptr.ol;
		if ( rt_equ(&ol3->r,&ol2->r) == 0 ) {
			ol2->next = ol3;
			rt->ptr.ol = ol2;
			return;
		}
		rt->ptr.node = new_rect_node(&rt->min,&rt->max,0,0);
		rt->ntype = 0;
		_insert_rect_tree(rt,rt->ptr.node,ol2,0);
		_insert_rect_tree(rt,rt->ptr.node,ol3,0);
	}
	else {
		_insert_rect_tree(rt,rt->ptr.node,ol2,0);
	}
}


int get_index_mask(GB_RECT * min,GB_RECT * max,GB_RECT * avg)
{
int index_flags;
	index_flags = (1<<RN_INDEX_MAX)-1;
	if ( min->tl.x > avg->tl.x )
		index_flags &= ~RNM_TLX_LOW;
	if ( max->tl.x <= avg->tl.x )
		index_flags &= ~RNM_TLX_HIGH;
	if ( min->tl.y > avg->tl.y )
		index_flags &= ~RNM_TLY_LOW;
	if ( max->tl.y <= avg->tl.y )
		index_flags &= ~RNM_TLY_HIGH;

	if ( min->br.x > avg->br.x )
		index_flags &= ~RNM_BRX_LOW;
	if ( max->br.x <= avg->br.x )
		index_flags &= ~RNM_BRX_HIGH;
	if ( min->br.y > avg->br.y )
		index_flags &= ~RNM_BRY_LOW;
	if ( max->br.y <= avg->br.y )
		index_flags &= ~RNM_BRY_HIGH;
	return index_flags;
}

RT_OBJ_LIST *
_search_rect_node(RECT_TREE * rt,RECT_NODE * rn,
	GB_RECT * min,GB_RECT * max,RT_OBJ_LIST * ol)
{
int im,index;
GB_RECT * r;
RT_OBJ_LIST * ol2;
	im = get_index_mask(min,max,&rn->avg);
	for ( index = 0 ; index < RN_INDEX_MAX ; index ++ ) {
		if ( !(im & (1<<index)) )
			continue;
		if ( rn->ptr[index].ol == 0 )
			continue;
		if ( rn->ntype&(1<<index) ) {
			r = &rn->ptr[index].ol->r;
			if ( rt_cmp(min,r,max) == 0 )
				for ( ol2 = rn->ptr[index].ol ; ol2;
						ol2 = ol2->next ) {
					ol = new_rt_obj_list(
						ol2->obj,ol);
				}
		}
		else {
			ol = _search_rect_node(rt,
				rn->ptr[index].node,
				min,max,ol);
		}
	}
	return ol;
}

RT_OBJ_LIST *
search_rect_tree(RECT_TREE * rt,GB_RECT * min,GB_RECT * max)
{
GB_RECT * r;
RT_OBJ_LIST* olp;
RT_OBJ_LIST * ret;
	if ( rt->ptr.node == 0 )
		return 0;
	if ( rt->ntype ) {
		r = &rt->ptr.ol->r;
		if ( rt_cmp(min,r,max) == 0 ) {
			ret= 0;
			for ( olp = rt->ptr.ol ; olp ; olp = olp->next )
				ret = new_rt_obj_list(olp->obj,ret);
			return ret;
		}
		return 0;
	}
	else {
		return _search_rect_node(rt,rt->ptr.node,min,max,0);
	}
}

void
_delete_search(RECT_NODE * rn,GB_RECT * r,void * obj)
{
int index;
int i;
RECT_NODE * rn1;
RT_OBJ_LIST ** olp;
RT_OBJ_LIST * ol2;
	index = 0;
	if ( r->tl.x > rn->avg.tl.x )
		index |= RN_TLX;
	if ( r->tl.y > rn->avg.tl.y )
		index |= RN_TLY;

	if ( r->br.x > rn->avg.br.x )
		index |= RN_BRX;
	if ( r->br.y > rn->avg.br.y )
		index |= RN_BRY;

	if ( rn->ptr[index].node == 0 ) {
		return;
	}
	if ( rn->ntype&(1<<index) ) {
		if ( rt_equ(&rn->ptr[index].ol->r,r) )
			return;
		for ( olp = &rn->ptr[index].ol ;
				*olp ; olp = &(*olp)->next ) {
			if ( (*olp)->obj == obj ) {
				ol2 = *olp;
				*olp = ol2->next;
				ol2->next = 0;
				free_rt_obj_list(ol2);
				break;
			}
		}
		if ( rn->ptr[index].ol == 0 )
			rn->ntype &= ~(1<<index);
		return;
	}
	_delete_search(rn1 = rn->ptr[index].node,r,obj);
	for ( i = 0 ; i < RN_INDEX_MAX ; i ++ ) {
		if ( rn1->ptr[i].node )
			return;
	}
	free_rect_node(rn1);
	rn->ptr[index].node = 0;
}

void
delete_rect_tree(RECT_TREE * rt,void * obj)
{
GB_RECT * r;
int i;
RT_OBJ_LIST ** olp;
RT_OBJ_LIST * ol2;
	r = (*rt->get_minrect)(obj);
	if ( rt->ptr.node == 0 ) {
		return;
	}
	if ( rt->ntype ) {
		if ( rt_equ(r,&rt->ptr.ol->r) )
			return;
		for ( olp = &rt->ptr.ol; *olp ; olp = &(*olp)->next ) {
			ol2 = *olp;
			*olp = ol2->next;
			ol2->next = 0;
			free_rt_obj_list(ol2);
			break;
		}
		if ( rt->ptr.ol == 0 )
			rt->ntype = 0;
		return;
	}
	_delete_search(rt->ptr.node,r,obj);
	for ( i = 0 ; i < RN_INDEX_MAX ; i ++ ) {
		if ( rt->ptr.node->ptr[i].node )
			return;
	}
	free_rect_node(rt->ptr.node);
	rt->ptr.node = 0;
	rt->ntype = 0;
}
