/*
 * OLSR ad-hoc routing table management protocol GUI front-end
 * Copyright (C) 2003 Andreas Tønnesen (andreto@ifi.uio.no)
 *
 * This file is part of olsr.org.
 *
 * uolsrGUI 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.
 *
 * uolsrGUI 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 olsr.org; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "common.h"
#include "nodes.h"
#include <math.h>


void
init_nodes()
{

  nodes.next = &nodes;
  nodes.prev = &nodes;
}

/*
 *Insert a new node in the list
 *NB! The list is NOT checked for duplicates!!
 */
struct node *
insert_node(struct node *n, olsr_u8_t vtime)
{
  struct node *new_node;

  printf("Inserting node %s\n", ip_to_string((union olsr_ip_addr *)&n->addr));

  if((new_node = malloc(sizeof(struct node))) == 0)
    {
      fprintf(stderr, "OUT OF MEMORY!\n");
      exit(1);
    }

  memcpy(new_node, n, sizeof(struct node));
  
  /* queue */
  nodes.next->prev = new_node;
  new_node->next = nodes.next;
  nodes.next = new_node;
  new_node->prev = &nodes;

  new_node->hna.next = &new_node->hna;
  new_node->hna.prev = &new_node->hna;
  new_node->mid.next = &new_node->mid;
  new_node->mid.prev = &new_node->mid;
  new_node->mpr.next = &new_node->mpr;
  new_node->mpr.prev = &new_node->mpr;


  update_timer_node(&n->addr, vtime);

  return new_node;
}


/**
 *Add a new node to the set
 */
int
add_node(union olsr_ip_addr *node, olsr_u8_t vtime)
{
  struct node new;
  struct node *tmp_nodes;
  struct timeval tmp_timer;
  double dbl_time;
  olsr_u32_t time_value;
  struct mid *tmp_mid;

  dbl_time = me_to_double(vtime);
  time_value = (olsr_u32_t) dbl_time*1000;

  tmp_timer.tv_sec = time_value/1000;
  tmp_timer.tv_usec = (time_value-(tmp_timer.tv_sec*1000)) * 1000;   

  /* Check if node exists */
  for(tmp_nodes = nodes.next;
      tmp_nodes != &nodes;
      tmp_nodes = tmp_nodes->next)
    {
      if(memcmp(&tmp_nodes->addr, node, ipsize) == 0)
	{
	  //printf("updating timer for node %s\n", ip_to_string(node));
	  //printf("Updatimng timer for: %s\n", ip_to_string(node));
	  //printf("Secs: %d, usecs: %d\n", (int)tmp_timer.tv_sec, (int)tmp_timer.tv_usec);
	  gettimeofday(&now, (struct timezone *)NULL);
	  timeradd(&now, &tmp_timer, &tmp_nodes->timer);
	  return 0; 
	}
      /* Check MID */
      for(tmp_mid = tmp_nodes->mid.next;
	  tmp_mid != &tmp_nodes->mid;
	  tmp_mid = tmp_mid->next)
	{
	  if(memcmp(&tmp_mid->alias, node, ipsize) == 0)
	    {
	      //printf("updating timer for node %s\n", ip_to_string(node));
	      //printf("Updatimng timer for (MID): %s\n", ip_to_string(&tmp_nodes->addr));
	      //printf("Secs: %d, usecs: %d\n", (int)tmp_timer.tv_sec, (int)tmp_timer.tv_usec);
	      gettimeofday(&now, (struct timezone *)NULL);
	      timeradd(&now, &tmp_timer, &tmp_nodes->timer);
	      return 0; 
	    }
	}
    }

  /* New node */
  memset(&new, 0, sizeof(struct node));
  memcpy(&new.addr, node, ipsize);
  new.display = 1;
  printf("1:");
  insert_node(&new, vtime);
  update_nodes_list(&new);
 
  return 1;
}


int
update_timer_node(union olsr_ip_addr *node, olsr_u8_t vtime)
{
  struct node *tmp_nodes;
  struct timeval tmp_timer;
  double dbl_time;
  olsr_u32_t time_value;

  dbl_time = me_to_double(vtime);
  time_value = (olsr_u32_t) dbl_time*1000;

  tmp_timer.tv_sec = time_value/1000;
  tmp_timer.tv_usec = (time_value-(tmp_timer.tv_sec*1000)) * 1000;   

  //printf("Updatimng timer for: %s\n", ip_to_string(node));
  //printf("Secs: %d, usecs: %d\n", (int)tmp_timer.tv_sec, (int)tmp_timer.tv_usec);

  for(tmp_nodes = nodes.next;
      tmp_nodes != &nodes;
      tmp_nodes = tmp_nodes->next)
    {
      if(memcmp(&tmp_nodes->addr, node, ipsize) == 0)
	{
	  //printf("updating timer for node %s\n", ip_to_string(node));
	  gettimeofday(&now, (struct timezone *)NULL);
	  timeradd(&now, &tmp_timer, &tmp_nodes->timer);
	  if(tmp_nodes->display)
	    update_nodes_list(tmp_nodes);
	  return 1; 
	}
    }
  
  return 0;
}


/**
 *Updates the hold time for the mpr 'mpr' registered on
 *the node 'node'. Adds the mpr to the node if not already
 *registered.
 *@param node the node that has chosen the MPR
 *@param mpr the MPR chosen by the node
 *@return 0 if node was added, 1 if not
 */
int
update_timer_mpr(union olsr_ip_addr *node, union olsr_ip_addr *mpr, olsr_u8_t vtime)
{
  struct node *tmp_nodes;
  struct mpr *tmp_mpr;
  struct timeval tmp_timer;
  double dbl_time;
  olsr_u32_t time_value;

  dbl_time = me_to_double(vtime);
  time_value = (olsr_u32_t) dbl_time*1000;

  tmp_timer.tv_sec = time_value/1000;
  tmp_timer.tv_usec = (time_value-(tmp_timer.tv_sec*1000)) * 1000;   

  //printf("Updatimng MPR timer for: %s\n", ip_to_string(node));
  //printf("Secs: %d, usecs: %d\n", (int)tmp_timer.tv_sec, (int)tmp_timer.tv_usec);

  //printf("Updatimng timer for: %s\n", ip_to_string(node));
  for(tmp_nodes = nodes.next;
      tmp_nodes != &nodes;
      tmp_nodes = tmp_nodes->next)
    {
      if(memcmp(&tmp_nodes->addr, node, ipsize) == 0)
	{
	  for(tmp_mpr = tmp_nodes->mpr.next;
	      tmp_mpr != &tmp_nodes->mpr;
	      tmp_mpr = tmp_mpr->next)
	    {
	      if(memcmp(&tmp_mpr->addr, mpr, ipsize) == 0)
		{
		  //printf("updating timer for MPR %s ", ip_to_string(mpr));
		  //printf("node %s\n", ip_to_string(node));
		  gettimeofday(&now, (struct timezone *)NULL);
		  timeradd(&now, &tmp_timer, &tmp_mpr->timer);
		  return 1; 
		}
	    }
	  /* Only add if parent is added */
	  add_mpr(node, mpr, &tmp_timer);
	  return 0;
	}
    }

  return 0;
}




int
add_mid_node(union olsr_ip_addr *node, union olsr_ip_addr *alias, olsr_u8_t vtime)
{

  struct node *tmp_nodes;
  struct mid *tmp_mid;
  struct node new, *inserted;

  //printf("MID_add: %s\n", ip_to_string(alias));

  //update_timer_node(node, vtime);

  for(tmp_nodes = nodes.next;
      tmp_nodes != &nodes;
      tmp_nodes = tmp_nodes->next)
    {
      if(memcmp(&tmp_nodes->addr, node, ipsize) == 0)
	{
	  for(tmp_mid = tmp_nodes->mid.next;
	      tmp_mid != &tmp_nodes->mid;
	      tmp_mid = tmp_mid->next)
	    {
	      if(memcmp(&tmp_mid->alias, alias, ipsize) == 0)
		return 0;
	    }

	  /* we didn't find the address */
	  printf("(1)NEW MID %s ", ip_to_string(alias));
	  printf("ADDED FOR %s\n", ip_to_string(node));
	  if((tmp_mid = malloc(sizeof(struct mid))) == 0)
	    {
	      fprintf(stderr, "OUT OF MEMORY\n");
	      exit(1);
	    }

	  memcpy(&tmp_mid->alias, alias, ipsize);

	  tmp_nodes->mid.next->prev = tmp_mid;
	  tmp_mid->next = tmp_nodes->mid.next;
	  tmp_nodes->mid.next = tmp_mid;
	  tmp_mid->prev = &tmp_nodes->mid;

	  remove_node_addr(alias); // Remove if already registered as a node
	  
	  update_nodes_list(tmp_nodes);
	  return 1; 

	}
    }



  /*New node */

  printf("ADDING NEW NODE %s FROM MID...\n", ip_to_string(node));
  /* We don't know wery much... */
  memset(&new, 0, sizeof(struct node));
  memcpy(&new.addr, node, ipsize);
  inserted = insert_node(&new, vtime);

  if((tmp_mid = malloc(sizeof(struct mid))) == 0)
    {
      fprintf(stderr, "OUT OF MEMORY!\n");
      exit(1);
    }

  memcpy(&tmp_mid->alias, alias, ipsize);

  tmp_mid->next = &inserted->mid;
  tmp_mid->prev = &inserted->mid;
  inserted->mid.next = tmp_mid;
  inserted->mid.prev = tmp_mid;

  update_nodes_list(inserted);

  return 2;
}


int
add_hna_node(union olsr_ip_addr *node, union olsr_ip_addr *net, union olsr_ip_addr *mask, olsr_u8_t vtime)
{

  struct node *tmp_nodes;
  struct hna *tmp_hna;
  struct node new, *inserted;

  //printf("HNA: %s\n", ip_to_string(&net));

  update_timer_node(node, vtime);

  for(tmp_nodes = nodes.next;
      tmp_nodes != &nodes;
      tmp_nodes = tmp_nodes->next)
    {
      if(memcmp(&tmp_nodes->addr, node, ipsize) == 0)
	{
	  for(tmp_hna = tmp_nodes->hna.next;
	      tmp_hna != &tmp_nodes->hna;
	      tmp_hna = tmp_hna->next)
	    {
	      if((memcmp(&tmp_hna->net, net, ipsize) == 0) && (memcmp(&tmp_hna->mask, mask, ipsize) == 0))
		return 0;
	    }

	  //printf("NEW HNA ADDED FOR %s ", ip_to_string(node));
	  //printf("net: %s \n", ip_to_string(&net));
	  /* we didn't find the address */
	  if((tmp_hna = malloc(sizeof(struct hna))) == 0)
	    {
	      fprintf(stderr, "OUT OF MEMORY\n");
	      exit(1);
	    }

	  memcpy(&tmp_hna->net, net, ipsize);
	  memcpy(&tmp_hna->mask, mask, ipsize);

	  /* queue */
	  tmp_nodes->hna.next->prev = tmp_hna;
	  tmp_hna->next = tmp_nodes->hna.next;
	  tmp_nodes->hna.next = tmp_hna;
	  tmp_hna->prev = &tmp_nodes->hna;
	  
	  update_nodes_list(tmp_nodes);
	  return 1; 
	}
    }



  printf("ADDING NEW NODE %s FROM HNA...\n", ip_to_string(node));
  /* We don't know wery much... */
  memset(&new, 0, sizeof(struct node));
  memcpy(&new.addr, node, ipsize);
  inserted = insert_node(&new, vtime);

  if((tmp_hna = malloc(sizeof(struct hna))) == 0)
    {
      fprintf(stderr, "OUT OF MEMORY!\n");
      exit(1);
    }

  memcpy(&tmp_hna->net, net, ipsize);
  memcpy(&tmp_hna->mask, mask, ipsize);

  tmp_hna->next = &inserted->hna;
  tmp_hna->prev = &inserted->hna;
  inserted->hna.next = tmp_hna;
  inserted->hna.prev = tmp_hna;

  update_nodes_list(inserted);

  return 2;
}


/**
 *Add the MPR mpr to the node nodes selected MPRs.
 *Nodes are NOT added if they are not yet registered!
 *
 *@param node the node that has chosen an MPR
 *@param mpr the MPR choosen by node
 *@return negative if node already registered or node not found
 */
int
add_mpr(union olsr_ip_addr *node, union olsr_ip_addr *mpr, struct timeval *tmp_timer)
{

  struct node *tmp_nodes;
  struct mpr *mprs;
  struct mpr *tmp_mpr;

  for(tmp_nodes = nodes.next;
      tmp_nodes != &nodes;
      tmp_nodes = tmp_nodes->next)
    {
      if(memcmp(&tmp_nodes->addr, node, ipsize) == 0)
	{
	  for(mprs = tmp_nodes->mpr.next;
	      mprs != &tmp_nodes->mpr;
	      mprs = mprs->next)
	    {
	      if(memcmp(&mprs->addr, mpr, ipsize) == 0)
		  return 0;
	    }

	  //printf("Adding MPR %s to ", ip_to_string(mpr));
	  //printf("%s\n", ip_to_string(node));
	  /* Add mpr */

	  if((tmp_mpr = malloc(sizeof(struct mpr))) == 0)
	    {
	      fprintf(stderr, "OUT OF MEMORY\n");
	      exit(1);
	    }

	  memcpy(&tmp_mpr->addr, mpr, ipsize);

	  gettimeofday(&now, (struct timezone *)NULL);
	  timeradd(&now, tmp_timer, &tmp_mpr->timer);

	  /* queue */
	  tmp_nodes->mpr.next->prev = tmp_mpr;
	  tmp_mpr->next = tmp_nodes->mpr.next;
	  tmp_nodes->mpr.next = tmp_mpr;
	  tmp_mpr->prev = &tmp_nodes->mpr;

	  update_nodes_list(tmp_nodes);
	  return 1; 

	}
    }

  return 1;
}




int
remove_node(struct node *node)
{
  struct hna *tmp_hna, *tmp_hna2;
  struct mid *tmp_mid, *tmp_mid2;
  struct mpr *tmp_mpr, *tmp_mpr2;

  printf("Remove node %s\n", ip_to_string(&node->addr));


  tmp_hna = node->hna.next;
  while(tmp_hna != &node->hna)
    {
      tmp_hna2 = tmp_hna;
      tmp_hna = tmp_hna->next;
      free(tmp_hna2);
    }
  tmp_mpr = node->mpr.next;
  while(tmp_mpr != &node->mpr)
    {
      tmp_mpr2 = tmp_mpr;
      tmp_mpr = tmp_mpr->next;
      free(tmp_mpr2);
    }
  tmp_mid = node->mid.next;
  while(tmp_mid != &node->mid)
    {
      tmp_mid2 = tmp_mid;
      tmp_mid = tmp_mid->next;
      free(tmp_mid2);
    }
  
  /* Gemove form GUI */
  remove_nodes_list(&node->addr);
  
  /* Dequeue */
  node->prev->next = node->next;
  node->next->prev = node->prev;
  
  free(node);

  return 1;  
}






/*
 * Remove based on address
 */

int
remove_node_addr(union olsr_ip_addr *node)
{
  struct node *tmp_nodes;
  struct hna *tmp_hna, *tmp_hna2;
  struct mid *tmp_mid, *tmp_mid2;
  struct mpr *tmp_mpr, *tmp_mpr2;

  printf("Remove node %s\n", ip_to_string(node));


  tmp_nodes = nodes.next;

  while(tmp_nodes != &nodes)
    {
      if(memcmp(&tmp_nodes->addr, node, ipsize) == 0)
	{
	  printf("(2)Deleting node %s\n", ip_to_string((union olsr_ip_addr *)&tmp_nodes->addr));

	  tmp_hna = tmp_nodes->hna.next;
	  while(tmp_hna != &tmp_nodes->hna)
	    {
	      tmp_hna2 = tmp_hna;
	      tmp_hna = tmp_hna->next;
	      free(tmp_hna2);
	    }
	  tmp_mpr = tmp_nodes->mpr.next;
	  while(tmp_mpr != &tmp_nodes->mpr)
	    {
	      tmp_mpr2 = tmp_mpr;
	      tmp_mpr = tmp_mpr->next;
	      free(tmp_mpr2);
	    }
	  tmp_mid = tmp_nodes->mid.next;
	  while(tmp_mid != &tmp_nodes->mid)
	    {
	      tmp_mid2 = tmp_mid;
	      tmp_mid = tmp_mid->next;
	      free(tmp_mid2);
	    }

	  /* Gemove form GUI */
	  remove_nodes_list(&tmp_nodes->addr);

	  /* Dequeue */
	  tmp_nodes->prev->next = tmp_nodes->next;
	  tmp_nodes->next->prev = tmp_nodes->prev;

	  free(tmp_nodes);

	  return 1;
	}

      tmp_nodes = tmp_nodes->next;
    }

  return 0;
}




struct node *
find_node(char *ip)
{
  struct node *tmp_nodes;

  for(tmp_nodes = nodes.next;
      tmp_nodes != &nodes;
      tmp_nodes = tmp_nodes->next)
    {
      if(strcmp(ip_to_string((union olsr_ip_addr *)&tmp_nodes->addr), ip) == 0)
	return tmp_nodes;
    }

  return NULL;
}


struct node *
find_node_t(union olsr_ip_addr *ip)
{
  struct node *tmp_nodes;

  for(tmp_nodes = nodes.next;
      tmp_nodes != &nodes;
      tmp_nodes = tmp_nodes->next)
    {
      if(memcmp(&tmp_nodes->addr, ip, ipsize) == 0)
	return tmp_nodes;
    }


  return 0;
}


/*
 *Remove timed out nodes
 */
gint
time_out_nodes(gpointer data)
{
  struct node *tmp_nodes;
  struct node *node_to_delete;

  /* Wait before starting timing out */
  if(timeouts)
    {
      timeouts--;
      //printf("Waiting...\n");
      return 1;
    }

  //printf("Timing out nodes...\n");
  gettimeofday(&now, (struct timezone *)NULL);

  tmp_nodes = nodes.next;

  while(tmp_nodes != &nodes)
    {
      //printf("%s: %6d < %6d\n", ip_to_string(&tmp_nodes->addr), tmp_nodes->timer.tv_sec, now.tv_sec);
      if(timercmp(&tmp_nodes->timer,&now,<))
	{
	  printf("Node %s timed out...\n", ip_to_string((union olsr_ip_addr *)&tmp_nodes->addr));
	  node_to_delete = tmp_nodes;

	  tmp_nodes = tmp_nodes->next;

	  remove_nodes_list(&node_to_delete->addr);
	  remove_node(node_to_delete);
	} 
      else
	tmp_nodes = tmp_nodes->next;
    }

  return 1;
}



/**
 *Timeout MPRs for a given node. Only called when user
 *is to see the registered MPRs of the node.
 *@param node the node whom MPRs should be timed out
 *@return negative if node not found
 */
int
time_out_mprs(union olsr_ip_addr *node)
{

  struct node *tmp_nodes;
  struct mpr *mpr_to_delete;
  struct mpr *tmp_mpr;

  gettimeofday(&now, (struct timezone *)NULL);


  /* W A R N I N G !
   *
   * THIS ALGORITHM HAS NOT BEEN TESTED PROPERLY!!!!!!
   * -Andreas
   */

  for(tmp_nodes = nodes.next;
      tmp_nodes != &nodes;
      tmp_nodes = tmp_nodes->next)
    {
      if(memcmp(&tmp_nodes->addr, node, ipsize) == 0)
	{
	  tmp_mpr = tmp_nodes->mpr.next;
	 
	  while(tmp_mpr != &tmp_nodes->mpr)
	    {
	      if(timercmp(&tmp_mpr->timer,&now,<))
		{
		  printf("MPR %s OF NODE ", ip_to_string((union olsr_ip_addr *)&tmp_mpr->addr));
		  printf("%s TIMIED OUT ", ip_to_string((union olsr_ip_addr *)&tmp_nodes->addr));fflush(stdout);

		  mpr_to_delete = tmp_mpr;
		  tmp_mpr = tmp_mpr->next;

		  /* Dequeue */
		  mpr_to_delete->next->prev = mpr_to_delete->prev;
		  mpr_to_delete->prev->next = mpr_to_delete->next;
		  /* Delete */
		  free(mpr_to_delete);
		}
	      else
		tmp_mpr = tmp_mpr->next;
	    }

	  return 1;
	}
    }

  return 0;
}



void
init_timer(olsr_u32_t time_value, struct timeval *hold_timer)
{ 
  olsr_u16_t  time_value_sec=0;
  olsr_u16_t  time_value_msec=0;

  time_value_sec=time_value/1000;
  time_value_msec=time_value-(time_value_sec*1000);

  hold_timer->tv_sec=time_value_sec;
  hold_timer->tv_usec=time_value_msec*1000; 
  
}


/**
 *Function that converts a mantissa/exponent 8bit value back
 *to double as described in RFC3626:
 *
 * value = C*(1+a/16)*2^b [in seconds]
 *
 *  where a is the integer represented by the four highest bits of the
 *  field and b the integer represented by the four lowest bits of the
 *  field.
 *
 *@param me the 8 bit mantissa/exponen value
 *
 *@return a double value
 */
double
me_to_double(olsr_u8_t me)
{
  int a = me>>4;
  int b = me - a*16;
  return (double)(VTIME_SCALE_FACTOR*(1+(double)a/16)*(double)pow(2,b));
}


syntax highlighted by Code2HTML, v. 0.9.1