/*
 * lookup_tree.c -- the tree of host lookup engine
 * Part of the tcpick project
 *
 * Author: Francesco Stablum <duskdruid @ despammed.com>
 *
 * Copyright (C) 2003, 2004  Francesco Stablum
 * Licensed under the GPL
 *
 */

/* 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at you option) any later version.
 *
 * 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.  
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111,
 * USA.
 */


/* lookup_tree.c
 * this file contains the functions called by lookup.c
 * all name/ip couples are cached in a balanced tree (in a similar way
 * to the avl tree)
 */

#include "tcpick.h"
#include "extern.h"

#define PARENT_POSITION_IS_LEFT(p) (p->parent->right == p)

struct _l_node *  _l_root; /* the first node */

/* prototypes */

struct _l_node *
_l_alloc(struct in_addr, char *);

char *
_l_get(struct in_addr);

int
_l_insert(struct _l_node *);

int
_l_walkup(struct _l_node *);

int
_l_checkweight(struct _l_node *);

int
_l_balance_right(struct _l_node *);

int
_l_balance_left(struct _l_node *);

int
_l_adjust_h(struct _l_node *);

struct _l_node *
_l_alloc(struct in_addr ip, char * name)
{
	register struct _l_node * new;

	new = (struct _l_node *) S_malloc(sizeof(struct _l_node));
	memset(new, 0, sizeof(struct _l_node));
	
	new->name = (char *)strdup(name); 
	/* FIXME: not sure strdup is a good thing*/
	memcpy(&(new->ip), &ip, sizeof(struct in_addr));
	
	return new;
}

char *
_l_get(struct in_addr ia)
{
	register struct _l_node * p;

	p = _l_root;

	while(p) {
		if ( p->ip.s_addr == ia.s_addr )
			return p->name; /* found */
		p = ( ia.s_addr > p->ip.s_addr ) 
			? p->right : p->left;
	}

	return NULL; /* not found */
}

int
_l_insert(struct _l_node * new)
{
	/* FIXME: could be better */

	register struct _l_node ** p;
	register struct _l_node * pre = NULL;
	
	p = & _l_root;

	while(*p) {
		pre = *p;
		p = ( new->ip.s_addr > (*p)->ip.s_addr ) ?
			&((*p)->right) : &((*p)->left);
	}
	
	*p = new;
	new->parent = pre;

	_l_walkup(new);

	return 1;
}

int
_l_walkup(struct _l_node * node)
/* FIXME: could be improved maybe */
{
	register struct _l_node * par;

	par = node->parent;

	while( par != NULL ) {

		if( par->right == node )
			par->right_h = MAX(par->right->right_h,
					   par->right->left_h) + 1;
		else
			par->left_h = MAX(par->left->right_h,
					   par->left->left_h) + 1;

		if( _l_checkweight(par) != 0 ) 
			/* something was done */
			return 1;

		node = par;
		par = par->parent;
	}
	return 0;
}

int
_l_checkweight(struct _l_node * node)
{
	if( (node->right_h - node->left_h) > 1) {
		/* need balance because of right too weight */
		_l_balance_right(node->right);
		return 1;
	}

	if( (node->left_h - node->right_h) > 1) {
		/* need balance because of left too weight */
		_l_balance_left(node->left);
		return 2;
	}
	
	return 0;
}

int
_l_balance_right(struct _l_node * D)
{
/* before:

        B
       / \
      A   D
         / \
        C   E

   after:
   
       D
      / \
     B   E
    / \
   A   C

*/

	register struct _l_node * 
		B = D->parent;

	/* 1. step: put up the node */
	if( B->parent != NULL ) {
		
		if( PARENT_POSITION_IS_LEFT(B) )
			B->parent->right = D;
		else
			B->parent->left  = D;

		D->parent = B->parent;

	} else { /* this is the root */
		_l_root = D;
		_l_root->parent = NULL;
	}

	/* 2. step: the left side C of the node D becames the
	 * right of the node B */

	B->right = D->left;

	if( B->right )
		B->right->parent = B;
	
	/* 3. step: left side of D is B */
	D->left = B;
	B->parent = D;

	/* 4. step: adjust height values */
	_l_adjust_h(B);
	_l_adjust_h(D);

	return 1;
}

int
_l_balance_left(struct _l_node * D)
{
/* before:

        B
       / \
      D   A
     / \
    E   C

   after:
   
       D
      / \
     E   B
        / \
       C   A

*/

	register struct _l_node * 
		B = D->parent;
	
	/* 1. step: put up the node */
	if( B->parent != NULL ) {

		if( PARENT_POSITION_IS_LEFT(B) )
			B->parent->right = D;
		else
			B->parent->left  = D;
	}

	D->parent = B->parent;
	
	/* 2. step: the right side C of the node D becames the
	 * left of the node B */
	B->left = D->right;

	if( B->left )
		B->left->parent = B;
	
	/* 3. step: right side of D is B */
	D->right = B;
	B->parent = D;

	/* 4. step: adjust height values */
	_l_adjust_h(B);
	_l_adjust_h(D);

	return 1;
}

int
_l_adjust_h(struct _l_node * node)
{

	node->right_h = ( node->right != NULL )
		? MAX(node->right->left_h, 
		      node->right->right_h) + 1
		: 0;

	node->left_h = ( node->left != NULL )
		? MAX(node->left->left_h,
		      node->left->right_h) + 1
		: 0;

	return 1;
}


syntax highlighted by Code2HTML, v. 0.9.1