/***************************************
  $Revision: 1.10 $

  Radix tree (rx).  rx_node.c - functions to operate on nodes of the tree
  (creation/deletion).

  Status: NOT REVUED, TESTED, INCOMPLETE

  Design and implementation by: Marek Bukowy

  ******************/ /******************
  Copyright (c) 1999                              RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  ***************************************/

#include <erroutines.h>
#include <rxroutines.h>
#include <memwrap.h>
#include <stubs.h>
#include <glib.h>

#include <comparisons.h> 

/***************************************************************************/
/*++++++++++++++++
  rx_creat_node = create a new data node 
  (empty{glue} nodes get created automatically).

  Takes a pointer to the (already allocated) data leaf to be included 
  in the list of data nodes (presumably empty as the node is only now being
  created).
  
  Requires a stack of nodes created in CREAT mode (with glue nodes, 
  until deep enough and the last node being non-glue).
  
  MT notes: requires the tree to be locked.
  
  Returns: RX_OK or error code.

  +++++++++++++++++*/
static
er_ret_t
rx_creat_node (
	       ip_prefix_t   *newpref,  /*+ prefix of the node to be added +*/
	       rx_tree_t     *tree,     /*+ tree the new node goes to +*/
	       rx_dataleaf_t *dataleaf, /*+ dataleaf to attach at this node+*/
	       rx_nodcpy_t   stack[],   /*+ stack==array of node_copies +*/
	       int           stackdepth /*+ length of the stack +*/
	     )
{
  rx_node_t *newnode, *curnode, *memnode, *gluenode;
  int chk_bit, dif_bit, link, curpos;
  char buf[1024];
  er_ret_t err;

  // assume no such node yet. Will die if there is one.
   
  // calloc, because parent/child keys and child ptrs are not always set.

  if( (err=wr_calloc( (void **) & newnode, 1, sizeof(rx_node_t))) != UT_OK) {
    return err; 
  }
  
  // increment the number of nodes in the tree
  tree -> num_nodes ++;
  
  newnode -> prefix = *newpref;
  
  // attach the leaf to a (presumably empty?! hence NULL) list...
  newnode->leaves_ptr = g_list_prepend(NULL, dataleaf);
  newnode->glue = 0;
  
  // OK, so take a look at the tree

  if ( tree -> num_nodes == 1 ) { 
    // The tree was empty. Create a new top node.
    
    tree -> top_ptr = newnode;
    ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "Created as the top node");
    return RX_OK;
  }

  // OK, there is at least one node in the tree. Take a look at the stack.

  //    we've got a real node there (not a glue), but we may be too deep.
  //   (it's not a glue, because glues have always two children.
  //    we had to go that deep because from a glue alone one doesn't know 
  //    what it glues)
  // GO UP.
  // take the first differing bit from comparing 
  // the new and the found nodes' prefixes. 
  // (not deeper than the shorter of the two)
  
  curpos =   stackdepth-1;
  curnode =  & stack[curpos].cpy;

  chk_bit = smaller(curnode->prefix.bits, newpref->bits );
  
  for(dif_bit = 0; dif_bit < chk_bit; dif_bit++) {
    // break the loop when the first different bit is found

    if( IP_addr_bit_get( & curnode->prefix.ip, dif_bit) 
	!=  IP_addr_bit_get( & newpref->ip, dif_bit) ) {
      break;
    }
  }
 
  ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, 
	    "cur = %d, new = %d, chk_bit = %d, dif_bit = %d", 
	    curnode->prefix.bits, newpref->bits, chk_bit, dif_bit );
  
  if(dif_bit == IP_sizebits(newpref->ip.space)) die; // it mustn't happen!!!
 
  // go up to that level (watch the head of the tree!)
  
  while( curpos > 0 && stack[curpos-1].cpy.prefix.bits >= dif_bit) {
    curpos--;
    ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, 
	      "up to level %d", curpos );
  }
  
  /*
    if the bit lenghts of the node, new prefix and the diffbit are equal
    {
    YOU'VE GOT THE NODE where the new one will be attached.
    Either it has data (and will be moved accordingly), 
    or is a glue (and will be turned into a regular node).
    }
  */
  
  curnode =  & stack[curpos].cpy;
  
  // RAM: set a pointer to the real node in memory
  memnode = stack[curpos].srcptr;
        
  if(    dif_bit == newpref->bits 
	 && dif_bit == curnode->prefix.bits ) {

    // such node already exists, nothing to change in the tree!!!
    // this should be checked before calling this function, so..
      
    die;
  }
  /*
    else  ** the branch ends here; we must create a new node... **
    {
    OK, how is the new node's prefix length w.r.t the dif_bit ? 
    longer  -> make it a child of the node found
    shorter -> make it the parent of the node found and take its place
    equal   -> make a glue node the parent of both 
    }
    
    WHEN ATTACHING THE NODE, VALUES FROM THE STACK ARE USED,
    TO PREVENT EXCESSIVE LOOKUPS AGAIN.
    
  */
  else {
    
    // **** attach it.
    if( ER_is_traced(FAC_RX, ASP_RX_NODCRE_DET) ) {
      rx_nod_print(curnode, buf, 1024);
      ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "Looking at node %s", buf);
    }
    
    if( curnode -> prefix.bits == dif_bit ) {
      
      // attach here as a child of the node found      
      link = IP_addr_bit_get( &newpref->ip, dif_bit );
      
      ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "attaching as child %d", link);
      
      if( memnode -> child_ptr[link] != NULL ) {
	die;
      }
      
      memnode -> child_ptr[link] = newnode;
      newnode -> parent_ptr = memnode;
    }
    else if ( newpref->bits == dif_bit ) {
      // make it the parent of the node found and take its place,
      // moving it down.

      // set the link from the NEW node to the OLD one (different than before)

      link = IP_addr_bit_get( &curnode->prefix.ip, dif_bit );
      
      ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "shifting down as child %d", link);

      // PARENT<->NEW LINKS
      // see if the node was the top_node
      if (curnode -> parent_ptr == NULL) {
	//  update tree struct 
	tree -> top_ptr = newnode;
      } else {    
	// no - fix the child link at the parent.
	// at the link where it was attached
	int link = (curnode->parent_ptr->child_ptr[1] == memnode);
	memnode -> parent_ptr -> child_ptr[link] = newnode;
      }
      memnode -> parent_ptr = newnode;

      // NEW<->CHILD LINKS
      newnode -> parent_ptr = curnode->parent_ptr;
      newnode -> child_ptr[link] = memnode;
    }
    else {
      // create a glue and shift the curnode below the glue,
      // then attach the new node at the glue
      
      // calloc, because parent/child keys are not set.

      if( (err=wr_calloc( (void **)& gluenode, 1, sizeof(rx_node_t))) != UT_OK) {
	return err; // die;
      }
      tree -> num_nodes ++;

      ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "created glue node at %p", gluenode);

      gluenode -> prefix.bits = dif_bit;

      // fill in the address. The glue node should get the prefix
      // shorter by one than the shorter of the two prefixes that are glued
      // (difbit)
      //
      
      gluenode -> prefix.ip = newpref->ip;
      gluenode -> prefix.bits = dif_bit;
      
      // the ip in this prefix is probably incorrect. Fix it.
      IP_pref_bit_fix(  & gluenode -> prefix );
      
      gluenode -> leaves_ptr = NULL;
      gluenode -> glue = 1;

      // 1. Fix the link to and from the parent to the gluenode.

      gluenode -> parent_ptr = curnode->parent_ptr;
      if (gluenode->parent_ptr == NULL) {
	tree -> top_ptr = gluenode;
      } 
      else {
	// fix the child link in the parent. 
	// if it was at 1, then let fix the link 1, 0 otherwise
	
	link = (curnode->parent_ptr->child_ptr[1] == memnode);
      
	memnode->parent_ptr->child_ptr[link] = gluenode;
      }

      // 2. Fix the links between gluenode and the OLD node

      link = IP_addr_bit_get( &newpref->ip, dif_bit );

      gluenode -> child_ptr[ ! link ] = memnode;
      memnode->parent_ptr = gluenode;

      // 3. Fix the links between gluenode and the NEW node
      
      gluenode -> child_ptr[ link ] = newnode;
      newnode -> parent_ptr = gluenode;
    }
    return RX_OK;
  }
  die;
  return -1; //this is just to calm down the compiler
}


/***************************************************************************/
/*+ hook for g_list_foreach to free a list element +*/

void
rx_free_list_element(void *cpy, void *trash)
{
  wr_free(cpy);
}

/***************************************************************************/
/*+++++++++++++++++++

  General function to operate on dataleaves attached to a single node
  (create / modify / delete).
  
  searches tree, finds and creates (modifies/deletes) a node,
  copies modified nodes to disk using rx_sql_node_set (not yet implemented).
  Updates memory rollback info.
  
  Currently only creation is implemented.

  Add a dataleaf at node defined by prefix. Create a new node if it doesn't 
  exist yet.

  MT notes: requires the tree to be locked.
  
  Returns: RX_OK or error code.

  Errors from:
  rx_bin_search,
  memory alloc routines.
  
  - no such node (if not in create mode)
  
  - too many nodes found (strange).
  
  +++++++++++++++++*/
  
er_ret_t
RX_bin_node (
	     rx_oper_mt   mode,       /*+ MODE={cre|mod|del} +*/
	     ip_prefix_t *newpref,    /*+ prefix of the node +*/
	     rx_tree_t	*tree,        /*+ pointer to the tree structure +*/
	     rx_dataleaf_t *dataleaf  /*+ dataleaf to attach at the node +*/
	     )
     
{
  GList *nodlist = NULL;
  int nodesfound, stackdepth;
  int glue;
  rx_nodcpy_t *curcpy;
  rx_node_t *curnode;
  rx_nodcpy_t *stack;
  er_ret_t err;
  char bbf[IP_PREFSTR_MAX];


  if( ER_is_traced( FAC_RX, ASP_RX_NODCRE_BOT)) {
    IP_pref_b2a( newpref , bbf, IP_PREFSTR_MAX);
    ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT,
	      "rx_bin_node: new %s in spc %d /fam %d /reg %d", 
	      bbf, tree->space, tree->family, tree->reg_id);
  }

  // first check: are we using the correct tree ???
  if( tree->space != newpref->ip.space ) {
    /* trying to insert a prefix of space %d into a tree of space %d\n",
	   tree->space,
	   newpref->ip.space);
    */
    die;
  }

  assert( newpref->bits <= IP_sizebits(tree->space) );

  // fix the prefix, to make sure all insignificant bits are 0
  IP_pref_bit_fix( newpref );
  
  if( (err=wr_malloc( (void **) &stack, 
	   sizeof(rx_nodcpy_t) * IP_sizebits(tree->space))) != UT_OK) {
    return err; //die;
  }
  
  if( (err=rx_build_stack(stack, &stackdepth, 
		     tree, newpref, RX_STK_CREAT) != RX_OK )) {
    return err; //die
  }
  
  //   rx_stk_print(stack, stackdepth);
  
  // perform a search on the stack. The result is a list, and it must
  // be properly deleted after use!!

  if( rx_nod_search(RX_SRCH_CREAT, 0, 0, 
		    tree, newpref, stack, stackdepth, 
		    &nodlist, RX_ANS_ALL) != RX_OK ) {
    return err; // die;
  }

  
  // count number of nodes in the answer 
  nodesfound = g_list_length (nodlist);
  
  switch( nodesfound ) {
  case 0:
    /* no such node (yet). See what we're up to.
       if( mode==cre )  create, else - program error, die */

    if( mode != RX_OPER_CRE) {
      die;
    }
    
    /*  C R E A T I O N */
    ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, 
	      "Creating a new node ");
    rx_creat_node(  newpref, tree, dataleaf, stack, stackdepth );
    break;
  case 1: /* found */
    switch( mode ) {
    case RX_OPER_CRE:
      //  attach the data at the node that was found;

      curcpy = g_list_nth_data(nodlist, 0);

      curnode = curcpy->srcptr;
      // was it glue ?
      glue = curnode->glue;
            
      curnode->leaves_ptr = g_list_prepend(curnode->leaves_ptr, dataleaf);
      // not it's not a glue anymore
      curnode->glue = 0;

      ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, "Appended data to a %s node",
		glue ? "glue" : "data");
      
      break;
    case RX_OPER_MOD:
      // put new data in;
      break;
    case RX_OPER_DEL:
      break;
      // fine, check the number of children:
      //  0 - just delete,
      //  1 - copy the child's link to parent. then delete
      //  2 - turn into a glue
    }
    break;
  default:
    /* too many nodes found! from an exact/exact-less-1 search.
       this cannot happen. Call Ghostbusters now.
     */
    die;
  }

  g_list_foreach(nodlist, rx_free_list_element, NULL);
  
  wr_free(stack);
  return RX_OK;
}

/***************************************************************************/
/*+++++++++++++++
  performs the actual update for inetnums (possibly composed of many prefixes).
  Decomposes the ranges into prefixes and then falls back to rx_bin_node
  to perform changes at the nodes.
  
  Requires/returns - practically the same as rx_bin_node.
++++++++++++++++*/

er_ret_t
RX_inum_node( rx_oper_mt mode,       /*+ MODE={cre|mod|del} +*/
	      ip_range_t *rang,      /*+ range of IP addresses +*/
	      rx_tree_t *tree,       /*+ pointer to the tree structure +*/
	      rx_dataleaf_t *leafptr /*+ dataleaf to attach at the node +*/
	      )     
{
  int i, prefcount;
  GList *preflist = NULL;
  char buf[IP_RANGSTR_MAX];

  if( ER_is_traced( FAC_RX, ASP_RX_NODCRE_BOT)) {
    IP_rang_b2a(rang, buf, IP_RANGSTR_MAX );
    ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, 
	      "rx_inum_node: adding %s", buf);
  }

  // decompose, put links to the data leaf into every prefix
  // that makes up this range.
  IP_rang_decomp(rang, &preflist);
  
  // see if there is more than 1 prefix, set the composed flag
  prefcount = g_list_length(preflist);
  leafptr->composed = (prefcount > 1) ;
  
  leafptr->iprange = *rang;
  
  for(i=0; i < prefcount; i++) {
    ip_prefix_t *mypref = g_list_nth_data(preflist, i);
    
    RX_bin_node(mode, mypref, tree, leafptr);
  }
    
  // free the storage from decomposition
  g_list_foreach(preflist, rx_free_list_element, NULL);
  g_list_free(preflist);

  return RX_OK;
}


/***************************************************************************/
/*+++++++++++++++
  translates ranges/prefixes into binary prefixes.
  finds tree, locks it.
  initiates memory rollback structure (???)
  
  builds a dataleaf and puts into the node(s), 
  calling rx_bin_node for every prefix.
  
  checks rollback condition and (possibly) rolls back ???
  
  MT-note: locks/unlocks the tree.
  
  Possible errors
  - all errors from:
    ip_asc_2_bin,
    rx_get_tree,
    rx_bin_node,
    wr_free
  
+++++++++++++++++*/
er_ret_t
RX_asc_node ( rx_oper_mt mode,       /*+ MODE={cre|mod|del} +*/
	      char *rangstr,         /*+ string prefix/range/IP +*/
	      rx_regid_t reg_id,     /*+ id of the registry +*/
	      ip_space_t spc_id,     /*+ type of space (ipv4/ipv6) +*/
	      rx_fam_t   fam_id,     /*+ family of objects (route/inetnum) +*/
	      void *data             /*+ pointer to the payload +*/
	      )     

{
 
  /*
   For creation of a new node:

     READ-LOCK THE FOREST 

     get the root tree for this space (rx_get_tree)
     got it ? good. No ? error!!!

     Check if any of the prefixes spans more than one subtree...
     Check if they all exist already..
 
     if any is missing
     then
         WRITE-LOCK THE FOREST
     fi

     for all missing subtrees
         create missing trees
     rof

     UNLOCK THE FOREST

     **now start writing the data:**

     put *data* records in memory and sql table

     for all matchind [sub]trees (in order of the list)
	 WRITE-LOCK the in-memory [sub]tree
	 WRITE-LOCK the sql-table for it
	 
	 for(all prefixes in memory that match this tree)
	     create a node in the tree pointing to the data
	 rof
	 UNLOCK the tree
     rof


*/
  

  ip_range_t myrang;
  ip_prefix_t mypref;
  rx_dataleaf_t *leafptr;
  rx_tree_t *mytree;
  int rang_ok;

  if( RX_get_tree ( &mytree, reg_id, spc_id, fam_id) != RX_OK ) {
    die;
  }
  
  ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, 
	    "rx_asc_node: inserting object  %s", rangstr);
  
  // set the data leaf values
  if( wr_calloc( (void **)& leafptr, sizeof(rx_dataleaf_t), 1) 
      != UT_OK) {
    die;
  }
  
  leafptr->data_ptr = data;

  switch( fam_id )
    {
    case RX_FAM_IN:
      rang_ok = 1;
      
      if( IP_rang_e2b(&myrang, rangstr) == IP_OK ) {
	// that's nice. everything is set.
      } else {
	// see if's a valid IP, maybe it's an IPv4 classful range
	if( IP_addr_e2b( &myrang.begin, rangstr ) == IP_OK ) {
	  if( IP_rang_classful( &myrang , &myrang.begin ) != IP_OK ) {
	    rang_ok = 0;
	  }
	}
	else {
	  // sorry. we don't accept that.	      
	  rang_ok = 0;
	}
      }
      
      if( rang_ok == 1 ) {
	return RX_inum_node( mode, &myrang, mytree, leafptr );
      }
      // else: fall through to the end of the function. (unrecognized arg)

      break;

    case RX_FAM_RT:
      if( IP_pref_e2b(&mypref, rangstr) == IP_OK ) {
	return RX_bin_node(RX_OPER_CRE, &mypref, mytree, leafptr);
      }
    }
  
  ER_dbg_va(FAC_RX, ASP_RX_NODCRE_BOT, 
	    "can't understand the key, discarding the OBJECT.");
  wr_free(data);
  wr_free(leafptr);
  
  return RX_BADKEY;
}

