/*
 * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
 *               2002, 2003, 2004
 *      Ohio University.
 *
 * ---
 *
 * Starting with the release of tcptrace version 6 in 2001, tcptrace
 * is licensed under the GNU General Public License (GPL).  We believe
 * that, among the available licenses, the GPL will do the best job of
 * allowing tcptrace to continue to be a valuable, freely-available
 * and well-maintained tool for the networking community.
 *
 * Previous versions of tcptrace were released under a license that
 * was much less restrictive with respect to how tcptrace could be
 * used in commercial products.  Because of this, I am willing to
 * consider alternate license arrangements as allowed in Section 10 of
 * the GNU GPL.  Before I would consider licensing tcptrace under an
 * alternate agreement with a particular individual or company,
 * however, I would have to be convinced that such an alternative
 * would be to the greater benefit of the networking community.
 * 
 * ---
 *
 * This file is part of Tcptrace.
 *
 * Tcptrace was originally written and continues to be maintained by
 * Shawn Ostermann with the help of a group of devoted students and
 * users (see the file 'THANKS').  The work on tcptrace has been made
 * possible over the years through the generous support of NASA GRC,
 * the National Science Foundation, and Sun Microsystems.
 *
 * Tcptrace 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 your option) any later version.
 *
 * Tcptrace 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 Tcptrace (in the file 'COPYING'); if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 *
 * Author:      Ramani Yellapragada
 *              School of Electrical Engineering and Computer Science
 *              Ohio University
 *              Athens, OH
 *              http://www.tcptrace.org/
 */

#include "tcptrace.h"
static char const GCC_UNUSED copyright[] =
    "@(#)Copyright (c) 2004 -- Ohio University.\n";
static char const GCC_UNUSED rcsid[] =
    "@(#)$Header: /usr/local/cvs/tcptrace/avl.c,v 5.5 2004/11/04 21:44:53 sdo Exp $";


/* Local routines for handling AVL tree balancing after inserting a node */
 
static void SnapRotLeft(ptp_snap **n);
static void SnapRotRight(ptp_snap **n);
static enum AVLRES SnapLeftGrown(ptp_snap **n);
static enum AVLRES SnapRightGrown(ptp_snap **n);
 
/* Local routines for handling AVL tree balancing after removing a node */
 
static enum AVLRES SnapLeftShrunk(ptp_snap **n);
static enum AVLRES SnapRightShrunk(ptp_snap **n);
static int SnapFindHighest(ptp_snap *target, ptp_snap **n, enum AVLRES *res);
static int SnapFindLowest(ptp_snap *target, ptp_snap **n, enum AVLRES *res);

/*
 * SnapRotLeft - perform counter clockwise rotation
 */

static void 
SnapRotLeft(
	    ptp_snap **n)
{
   ptp_snap *tmp = *n;

   if (debug > 4)
     printf("SnapRotLeft(): Rotating the AVL tree counter clockwise\n");
   
   *n = (*n)->right;
   tmp->right = (*n)->left;
   (*n)->left = tmp;
}

/*
 * SnapRotRight - perform clockwise rotation
 */

static void 
SnapRotRight(
	     ptp_snap **n)
{
   ptp_snap *tmp = *n;
   
   if (debug > 4)
     printf("SnapRotRight(): Rotating the AVL tree clockwise\n");
   
   *n = (*n)->left;
   tmp->left = (*n)->right;
   (*n)->right = tmp;
}

/* SnapLeftGrown - For balancing an AVL tree after insertion
 * Input is the address of a node. The node's left subtree has grown 
 * due to insertion. 
 */

static enum AVLRES 
SnapLeftGrown(
	      ptp_snap **n)
{
   if (debug > 4)
     printf("SnapLeftGrown(): Balancing the AVL tree because left subtree\
             has grown after insertion\n");
   
   switch ((*n)->skew) {
    case LEFT:
      if ((*n)->left->skew == LEFT) {
	 (*n)->skew = (*n)->left->skew = EQUAL1;
	 SnapRotRight(n);
      }
      else {
	 switch ((*n)->left->right->skew) {
	  case LEFT:
	    (*n)->skew = RIGHT;
	    (*n)->left->skew = EQUAL1;
	    break;
	  case RIGHT:
	    (*n)->skew = EQUAL1;
	    (*n)->left->skew = LEFT;
	    break;
	  default:
	    (*n)->skew = EQUAL1;
	    (*n)->left->skew = EQUAL1;
	 }
	 
	 (*n)->left->right->skew = EQUAL1;
	 SnapRotLeft(&(*n)->left);
	 SnapRotRight(n);
      }
      return OK;
      
    case RIGHT:
      (*n)->skew = EQUAL1;
      return OK;
      
    default:
      (*n)->skew = LEFT;
      return BALANCE;
   }
}

/* SnapRightGrown - For balancing an AVL tree after insertion
 * Input is the address of a node. The node's right subtree has grown
 * due to insertion.
 */

static enum AVLRES 
SnapRightGrown(
	       ptp_snap **n)
{
   if (debug > 4)
     printf("SnapRightGrown(): Balancing the AVL tree because right subtree\
             has grown after insertion\n");
   
   switch ((*n)->skew) {
    case LEFT:
      (*n)->skew = EQUAL1;
      return OK;
      
    case RIGHT:
      if ((*n)->right->skew == RIGHT) {
	 (*n)->skew = (*n)->right->skew = EQUAL1;
	 SnapRotLeft(n);
      }
      else {
	 switch ((*n)->right->left->skew) {
	  case RIGHT:
	    (*n)->skew = LEFT;
	    (*n)->right->skew = EQUAL1;
	    break;
	    
	  case LEFT:
	    (*n)->skew = EQUAL1;
	    (*n)->right->skew = RIGHT;
	    break;
	    
	  default:
	    (*n)->skew = EQUAL1;
	    (*n)->right->skew = EQUAL1;
	 }
	 
	 (*n)->right->left->skew = EQUAL1;
	 SnapRotRight(&(*n)->right);
	 SnapRotLeft(n);
      }
      return OK;
      
    default:
      (*n)->skew = RIGHT;
      return BALANCE;
   }
}

/*
 * SnapInsert - insert a node into the AVL tree
 * and balance the AVL tree
 */

enum AVLRES 
SnapInsert(
	   ptp_snap **root, 
	   ptp_snap *new_node)
{
   enum AVLRES tmp;
   
   if (debug > 4)
     printf("SnapInsert(): Inserting a node in the AVL tree\n");
   
   if (!(*root)) {	
      *root = new_node;	
      (*root)->left = (*root)->right = NULL;
      (*root)->skew = EQUAL1;
      return BALANCE;
   }
   
   else if (AVL_WhichDir(&new_node->addr_pair, &((*root)->addr_pair)) == LT) {
      if ((tmp = SnapInsert(&(*root)->left, new_node)) == BALANCE) {
	 return SnapLeftGrown(root);
      }
      return tmp;
   }
   
   else if (AVL_WhichDir(&new_node->addr_pair, &((*root)->addr_pair)) == RT) {
      if ((tmp = SnapInsert(&(*root)->right, new_node)) == BALANCE) {
	 return SnapRightGrown(root);
      }
      return tmp;
   }  
   return 0;
}

/*
 * SnapLeftShrunk - For balancing an AVL tree after removing a node
 * Input is the address of a node. The node's left subtree has shrunk
 * due to removal and might have made the tree unbalanced.
 */

static enum AVLRES 
SnapLeftShrunk(
	       ptp_snap **n)
{
   if (debug > 4)
     printf("SnapLeftshrunk(): Balancing the AVL tree because left subtree\
             has shrunk after removal\n");
   
   switch ((*n)->skew) {
    case LEFT:
      (*n)->skew = EQUAL1;
      return BALANCE;
      
    case RIGHT:
      if ((*n)->right->skew == RIGHT) {
	 (*n)->skew = (*n)->right->skew = EQUAL1;
	 SnapRotLeft(n);
	 return BALANCE;
      }
      
      else if ((*n)->right->skew == EQUAL1) {
	 (*n)->skew = RIGHT;
	 (*n)->right->skew = LEFT;
	 SnapRotLeft(n);
	 return OK;
      }
      
      else {
	 switch ((*n)->right->left->skew) {
	  case LEFT:
	    (*n)->skew = EQUAL1;
	    (*n)->right->skew = RIGHT;
	    break;
	    
	  case RIGHT:
	    (*n)->skew = LEFT;
	    (*n)->right->skew = EQUAL1;
	    break;
	    
	  default:
	    (*n)->skew = EQUAL1;
	    (*n)->right->skew = EQUAL1;
	 }
	 
	 (*n)->right->left->skew = EQUAL1;
	 SnapRotRight(& (*n)->right);
	 SnapRotLeft(n);
	 return BALANCE;
      }
      
    default:
      (*n)->skew = RIGHT;
      return OK;
   }  
}

/*
 * SnapRightShrunk - For balancing an AVL tree after removing a node
 * Input is the address of a node. The node's right subtree has shrunk
 * due to removal and might have made the tree unbalanced.
 */

static enum AVLRES 
SnapRightShrunk(
		ptp_snap **n)
{
   if (debug > 4)
     printf("SnapRightShrunk(): Balancing the AVL tree because right subtree\
             has shrunk after removal\n");
   
   switch ((*n)->skew) {
    case RIGHT:
      (*n)->skew = EQUAL1;
      return BALANCE;
      
    case LEFT:
      if ((*n)->left->skew == LEFT) {
	 (*n)->skew = (*n)->left->skew = EQUAL1;
	 SnapRotRight(n);
	 return BALANCE;
      }
      else if ((*n)->left->skew == EQUAL1) {
	 (*n)->skew = LEFT;
	 (*n)->left->skew = RIGHT;
	 SnapRotRight(n);
	 return OK;
      }
      
      else {
	 switch ((*n)->left->right->skew) {
	  case LEFT:
	    (*n)->skew = RIGHT;
	    (*n)->left->skew = EQUAL1;
	    break;
	    
	  case RIGHT:
	    (*n)->skew = EQUAL1;
	    (*n)->left->skew = LEFT;
	    break;
	    
	  default:
	    (*n)->skew = EQUAL1;
	    (*n)->left->skew = EQUAL1;
	 }
	 
	 (*n)->left->right->skew = EQUAL1;
	 SnapRotLeft(& (*n)->left);
	 SnapRotRight(n);
	 return BALANCE;
      }
      
    default:
      (*n)->skew = LEFT;
      return OK;
   }
}

/*
 * SnapFindHighest - replace a node with a subtree's highest-ranking item
 */

static int 
SnapFindHighest(
		ptp_snap *target, 
		ptp_snap **n, 
		enum AVLRES *res)
{
   ptp_snap *tmp;
   
   if (debug > 4)
     printf("SnapFindHighest(): Replacing a node with a subtree's\
             highest ranking item \n");
   
   *res = BALANCE;
   if (! (*n)) {
      return 0;
   }
   
   if ((*n)->right) {
      if (!SnapFindHighest(target, & (*n)->right, res)) {
	 return 0;
      }
      
      if (*res == BALANCE) {
	 *res = SnapRightShrunk(n);
      }
      
      return 1;
   }
   
   target->addr_pair  = (*n)->addr_pair;
   target->ptp = (*n)->ptp;
   tmp = *n;
   (*n) = (*n)->left;
   return 1;
}

/*
 * SnapFindLowest - replace a node with a subtree's lowest-ranking item
 */

static int 
  SnapFindLowest(
		 ptp_snap *target, 
		 ptp_snap **n, 
		 enum AVLRES *res)
{
   ptp_snap *tmp;
   
   if (debug > 4)
     printf("SnapFindLowest(): Replacing a node with a subtree's\
             lowest ranking item \n");
   
   *res = BALANCE;
   if (!(*n)) {
      return 0;
   }
   
   if ((*n)->left) {
      if (!SnapFindLowest(target, & (*n)->left, res)) {
	 return 0;
      }
      
      if (*res == BALANCE) {
	 *res =  SnapLeftShrunk(n);
      }
      
      return 1;
   }
   
   target->addr_pair = (*n)->addr_pair;
   target->ptp = (*n)->ptp;
   tmp = *n;
   *n = (*n)->right;
   return 1;
}

/*
 * SnapRemove - remove a node from the AVL tree
 * and balance the AVL tree
 */

enum AVLRES 
SnapRemove(
	   ptp_snap **root, 
	   tcp_pair_addrblock addr)
{   
   enum AVLRES tmp = BALANCE;
   
   if (debug > 4)
     printf("SnapRemove(): Removing a node from the AVL tree\n");
   
   if (!(*root)) {
      return 0;
   }
   
   if (AVL_WhichDir(&addr, &((*root)->addr_pair)) == LT) {
      if ((tmp = SnapRemove(&(*root)->left, addr)) == BALANCE) {
	 return SnapLeftShrunk(root);
      }
      
      return tmp;
   }
   
   if (AVL_WhichDir(&addr, &((*root)->addr_pair)) == RT) {
      if ((tmp = SnapRemove(&(*root)->right, addr)) == BALANCE) {
	 return SnapRightShrunk(root);
      }
      
      return tmp;
   }
   
   if ((*root)->left) {
      if (SnapFindHighest(*root, &((*root)->left), &tmp)) {
	 if (tmp == BALANCE) {
	    tmp = SnapLeftShrunk(root);
	 }
	 return tmp;
      }
   }
   
   if ((*root)->right) {
      if (SnapFindLowest(*root, &((*root)->right), &tmp)) {
	 if (tmp == BALANCE) {
	    tmp = SnapRightShrunk(root);
	 }
	 return tmp;
      }
   }
   
   *root = NULL;
   return BALANCE;
}
